NAudio предоставляет обертки для четырёх различных API вывода (воспроизведения) аудио. Кроме того, некоторые из них поддерживают несколько различных режимов работы. У новичков в NAudio и различных Windows аудио API могут возникнуть сложности, поэтому в этом посте я объясню какие четыре основных варианта и когда вам следует их использовать.

IWavePlayer

Мы начнем с обсуждения различных интерфейсов для всех устройств вывода. В NAudio каждое устройство вывода принадлежит IWavePlayer, который имеет метод Init (initialization — инициализация) в который вы передаете Wave Provider, который будет поставлять аудио данные. Затем вы можете вызвать Play (Воспроизведение), Pause (Пауза) и Stop (Стоп), которые говорят сами за себя, кроме того, что вам вам следует знать что Play только начинает проигрывание. Вам следует только раз вызвать Init на указанный экземпляр IWavePlayer'а. Если вам надо воспроизвести что-то другое, вам следует Dispose (освободить) ваше устройство вывода и создать новый экземпляр.

Вы заметите, что нет возможности получить или изменить позицию проигрывания. Это потому, что устройства воспроизведения не знают позицию - они только читают аудио из WaveProvider предоставляемым до того моменты, пока он не достигнет конца, на который указывает PlaybackStopped (проигрывание остановилось) событие законченно (подробнее тут). Альтернативно вы можете игнорировать PlayBackStopped и просто вызвать метод Stop когда бы вы не решили, что проигрывание окончено.

Вы можете заметить в интерфейсе свойство Volume (громкость), которое отмечено как Obsolete (устаревший). Не используйте его. Есть способы получше для установки громкости в NAudio. Например посмотрите на класс WaveChannel32 или класс SampleChannel (начиная с NAudio 1.5).

В конце концов есть свойство PlayBackState (Состояние Проигрывания), которому можно сообщить Stopped (Остановлен), Playing (играет) или Paused (приостановлен, на паузе). Будьте осторожны с Stopped т.к. если вы вызываете метод Stop, PlaybackState немедленно перейдет в состояние Stopped (остановится), но это может занять несколько милисекунд перед тем, как любой фоновый поток воспроизведения на самом деле завершится.

WaveOut

WaveOut следует воспринимать как устройство вывода по умолчанию в NAudio. Если вы не знаете что использовать - выберайте WaveOut. Фактически это обертка для waveOut API Windows'а, и наиболее универсально поддерживается всеми API.

Объект WaveOut позволяет конфигурировать несколько вещей перед вызовом Init. Самым распространенным является изменение свойства DeviceNumber (номер устройства).
-1 показывает устройство вывода по умолчанию, в то время как
0 является первым устройством вывода (по моему опыту, обычно тем же самым).
Что бы выяснить сколько доступно устройств вывода WaveOut, запросите статическое свойство WaveOut.DeviceCount (количество устройств).

Вы так же можете изменить DesiredLatency (желаемую задержку), которая измеряется в милисекундах. Эта настройка фактически устанавливает общую длительность всех буферов. Поэтому фактически вы можете поспорить с тем, что реальная задержка короче. В будущем NAudio я, возможно, уменьшу путаницу заменив это свойством BufferDuration (длительность буфера). По умолчанию DesiredLatency 300 мс, это должно гарантировать гладкое воспроизведение на большинстве компьютеров. Вы так же можете изменить NumberOfBuffers (количество буферов) на какое-то другое, вместо стандартных 2-ух, несмотря на то, что 3 - это единственное значение, которое действительно стоит использовать.

Единственная сложность с WaveOut в том, что доступны несколько различных "моделей обратного вызова". Понимание того, какую использовать важно. Обратный вызов используются когда бы WaveOut ни завершил проигрывание одного из своих буферов и хочет больше данных. В обратном вызове мы читаем из исходного wave provider'а и заполняем новый буфер звуками. Это затем задает очередь для проигрывания, предпалогая, что еще есть данные для проигрывания. Как и во всех моделях драйверов воспроизведения аудио, крайне важно, что бы это случилось как можно скорее. Иначе выводимый звук будет заикаться.
New Window (Новое Окно)


Это стандартный и рекомендуемый подход если вы создаете объект WaveOut из потока GUI (графического пользовательского интерфейса) Windows Forms'а, или WPF приложения. Когда бы WaveOut не захотел больше данных, он отправляет сообщение о том, что обрабатывается при помощи Windows message pump (не смог подобрать внятного перевода) невидимого нового окна. По умолчанию вы получаете эту модель обратного вызова когда вы вызываете пустой конструктор WaveOut. Однако, это не будет работать в фоновом потоке пока не подтянется какое-либо сообщение.

Одно из основных преимуществ использования этой модели (или модели Existing Window (существующего окна)) в том, что всё происходит в том же потоке. Это защищает вас от состояния гонки потоков, в которой перемещение происходит в то же время, что и чтение.

Примечание: Причина использования нового окна вместо существующего в том, что бы ликвидировать баги, которые могут произойти если вы запустите одно проигрывание до того, как предыдущее (проигрывание) завершилось. Это может привести к тому, что WaveOut перехватит сообщения, которые не следует.
Existing Window (Существующее окно)



По сути, Existing Window это тот же механизм обратного вызова, что и New Window, но вы должны передать в него handle (указатель) на существующее окно. Для совместимости с WPF так же хорошо, как и с WinForms, он передается как IntPtr. Единственное, с чем следует быть осторожным в этой модели это несколько параллельных экземпляров WaveOut т.к. они будут перехватывать сообщения друг друга (я могу исправить это в будущей версии NAudio).

Примечание: С обоими: Новым и Существующим Окнами методами обратного вызова, аудио проигрывание будет ухудшаться если ваше Windows message pump (не смог подобрать внятного перевода) на потоке GUI (графического пользовательского интерфейса) имеет еще слишком много работы для выполнения.
Function Callback (Функция обратного вызова)


Функция обратного вызова была первым методом обратного вызова, которую я пытался реализовать для NAudio, и она оказалась наиболее проблематичой из всех методов обратного вызова. По сути, вы можете дать ей функцию для обратного вызова, что кажется довольно удобным, т.к. эти обратные вызовы исходят из потока внутри операционной системы.

Ситуация усложняется тем, что некоторые звуковые карты не любят двух потоков вызывающих waveOut функции в одно и то же время (особенно когда одна вызывает waveOutWrite в то время как другая вызывает waveOutReset). Теоретически, это может быть легко исправлено блокировками вокруг всех waveOut вызовов, но некоторые аудио драйвера вызывают обратный вызов из другого потока, в то время как вы вызываете waveOutReset. В результате получается безысходная ситуация.

Я уверен, что в NAudio версии 1.5 все потенциальные безысходные ситуации были убраны. До недавнего времени Функция обратных вызовов была единственным способом для проигрывания аудио через WaveOut на не-GUI (без интерфейса) потоке в NAudio. Но начиная с версии NAudio 1.5 , существует Event (событие) обратного вызова, которое, как только оно наберет достаточное количество тестов, станет рекомендуемой альтернативой оконному обратному вызову для тех желающих играть аудио в фоном потоке.
Event Callback (Событие обратного вызова)

Новое для NAudio 1.5, оно уже реализовано в классе WaveOutEvent. Так же я могу попытаться подумать о способе написания события обратных вызовов варианта внутри класса WaveOut. Это реализовано похожим способом как WASAPI и DirectSound. Фоновый поток просто находится рядом, наполняя буферы, когда они становятся пустыми. Что бы помочь ему реагировать в нужное время, указатель события назначен сигнализировать фоновому потоку о том, что буфер был возвращен звуковой картой и нуждается в повторном наполнении.

Примечание: WaveOut так же поддерживает режим No Callback (без обратного вызова), который в настоящее время не реализован в NAudio, но будет очень просто изменить WaveOutEvent что бы его поддерживать. По сути, вместо того что бы ждать указатель события, вы засыпаете не на долго (половины продолжительности буфера будет достаточно) а затем просыпаетесь что бы увидеть нуждается ли какой-то буфер в заполнении.

Примечание 2: Есть одна финальная модель обратного вызова WaveOut, которую я не упомянул. Это обратный вызов потока. Я не думаю, что он предлагает что-то, чего другие модели не имеют, но вы даете ему ID (идентификатор) потока и он отправляет сообщения потоку. Потоку понадобится вызвать Application.Run для подтверждения выброса сообщения и, если выполнен, этот метод может так же разрешить методу обратного вызова Окна работать в фоновом потоке.
DirectSoundOut (Прямой вывод звук)

Если вы по каким-то причинам не хотите использовать WaveOut, то DirectSound - это хорошая альтернатива, т.к. он прост и имеет широкую поддержку.

Что бы выбрать конкретное устройство при помощи DirectSound, вы можете статичное свойство DirectSoundOut.Devices, которое позволит вам получить GUID (Globally Unique Identifier = Глобальный Уникальный Идентификатор) каждого устройства? который вы сможете передать в конструктор DirectSoundOut. Как и в WaveOut, вы можете регулировать задержку (общий размер буфера).

DirectSoundOut использует фоновый поток ждущий заполнения буфера (то же что и WaveOut с событием обратного вызова). Это надежный и незамысловатый механизм, но как и с любым механизмом обратного вызова, который использует фоновый поток, вы должны взять на себя ответственность и следить за тем, что бы смена позиции не произошла в то же время, что и чтение (так-же некоторые из встроенных в NAudio WaveStream-ов могут защитить вас от этой ошибки).
WasapiOut

WASAPI последний и величайший Windows аудио API, представленный Windows Vista. Но только потому что он новее, не значит, что вы должны его использовать. В действительности его использование может быть действительно болезненным, т.к. он более привередливый к формату WaveProvider, переданного его функции инициализации.и не выполнит для вас ресемплинг (изменение частоты дискретизации дискретного сигнала).

Для выбора конкретного устройства вывода вам нужно использовать класс MMDeviceEnumerator, который может сообщить свободные аудио "конечные точки" в системе.

WASAPI предлагает вам пару конфигураций на выбор. Основная - это любая, которую вы откроете в моде share (делиться с другими, не жадничать) или exclusive (эксклюзивный, жадничать). В эксклюзивном моде запросы вашего приложения имеют эксклюзивный доступ к звуковой карте. Это рекомендуется только с очень низкой задержкой. Вы так-же можете выбрать используется ли событие обратного вызова. Я вам рекомендую так делать, т.к. это позволяет фоновому потоку заполнять новый буфер так скоро, как ему надо.

Почему вам следует использовать WASAPI? Я бы его рекомендовал вам, только для работы на низкой задержке, или если вы хотите иметь эксклюзивный доступ к звуковой карте. Помните, что WASAPI не поддерживается на Windows XP. Однако, в ситуациях где WASAPI был бы хорошим выбором, AsioOut чаще более лучший...
AsioOut

Asio де-факто является стандартом для аудио интерфейсов драйверов для записывающий студий.Все профессиональные аудио интерфейсы для Windows будут поставляться с драйверами ASIO, который спроектированы для работы на самых низких задержках, из всех возможных. Для приложений с низкой задержкой ASIO наверное более лучший выбор чем WASAPI, т.к. он более широко поддерживается (для примера, вы можете использовать ASIO на XP).

Устройства вывода ASIO выбираются по имени. Используйте AsioOut.GetDriverNames() что бы увидеть какие устройства доступны в вашей системе. Отметьте, что оно вернет все установленные драйверы ASIO. Это не обязательно значит, что звуковая карта подключена в случае внешнего аудио интерфейса. Поэтому инициализация может не удастся.

Драйверы ASIO поддерживают их собственные изменяемые настройки графического интерфейса.Вы можете получить доступ вызвав ShowControlPanel(). Задержки обычно устанавливаются на панели управления и обычно указывают на примеры. Запомните, что если вы пытаетесь работать на действительно низкой задержке, функция инициализации вашего WaveProvider'а должна быть очень высокой.

Драйверы ASIO могут обрабатывать данные в целом хосте родных WAV форматов (например big endian против little endian, 16, 24, 32 bit ints, IEEE floats, и т.д.), не все из которых на настоящий момент поддерживаются NAudio. Если ASIO Out не работает с вашей звуковой картой - напишите сообщение в группе обсуждений NAudio, т.к. это очень легко добавить поддержку другого формата.

Last edited Jul 18, 2014 at 9:29 AM by markheath, version 1

Comments

No comments yet.