Статьи Королевства Дельфи


Waveform Audio Win32 API. Часть I - часть 2


WAVE_FORMAT_1M08

11.025 kHz, mono, 8-bit

WAVE_FORMAT_1M16

11.025 kHz, mono, 16-bit
WAVE_FORMAT_1S08

11.025 kHz, stereo, 8-bit
WAVE_FORMAT_1S16

11.025 kHz, stereo, 16-bit
WAVE_FORMAT_2M08

22.05 kHz, mono, 8-bit
WAVE_FORMAT_2M16

22.05 kHz, mono, 16-bit
WAVE_FORMAT_2S08

22.05 kHz, stereo, 8-bit
WAVE_FORMAT_2S16

22.05 kHz, stereo, 16-bit
WAVE_FORMAT_4M08

44.1 kHz, mono, 8-bit
WAVE_FORMAT_4M16

44.1 kHz, mono, 16-bit
WAVE_FORMAT_4S08

44.1 kHz, stereo, 8-bit
WAVE_FORMAT_4S16

44.1 kHz, stereo, 16-bit

Обратите внимание на то, что подавляющее большинство (если не все) звуковые карты поддерживают промежуточные режимы записи-воспроизведения. Т.е. вполне возможно на карте с максимальной частотой дискретизации 44100 выборок/сек производить запись со скоростью 16000 выборок/сек, хотя это и не сообщается по запросу waveInGetDevCaps. wChannels: Word - количество входных каналов (1-моно, 2-стерео)
wReserved1: Word - зарезервировано


Функция waveInGetErrorText возвращает текстовое описание возникших в ходе выполнения ошибок.

function waveInGetErrorText(
mmrError: MMRESULT;
lpText: PChar;
uSize: UINT
): MMRESULT; stdcall;

mmrError - код ошибки;
lpText - адрес с которого будет размещена нуль-терминированная строка-описание;
uSize - размер участка памяти, на который ссылается lpText;

Ниже приведен пример процедуры, выводящей сведения об устройствах аудиоввода.
uses Windows, MMSystem; type TModeDescr=record mode: DWORD; // код режима работы descr: string[32]; // словесное описание end; const // массив содержит сопоставления режима работы и словесного описания modes: array [1..12] of TModeDescr=((mode: WAVE_FORMAT_1M08; descr:'11.025 kHz, mono, 8-bit'), (mode: WAVE_FORMAT_1M16; descr:'11.025 kHz, mono, 16-bit'), (mode: WAVE_FORMAT_1S08; descr:'11.025 kHz, stereo, 8-bit'), (mode: WAVE_FORMAT_1S16; descr:'11.025 kHz, stereo, 16-bit'), (mode: WAVE_FORMAT_2M08; descr:'22.05 kHz, mono, 8-bit'), (mode: WAVE_FORMAT_2M16; descr:'22.05 kHz, mono, 16-bit'), (mode: WAVE_FORMAT_2S08; descr:'22.05 kHz, stereo, 8-bit'), (mode: WAVE_FORMAT_2S16; descr:'22.05 kHz, stereo, 16-bit'), (mode: WAVE_FORMAT_4M08; descr:'44.1 kHz, mono, 8-bit'), (mode: WAVE_FORMAT_4M16; descr:'44.1 kHz, mono, 16-bit'), (mode: WAVE_FORMAT_4S08; descr:'44.1 kHz, stereo, 8-bit'), (mode: WAVE_FORMAT_4S16; descr:'44.1 kHz, stereo, 16-bit')); procedure ShowInfo; var WaveNums, i, j: integer; WaveInCaps: TWaveInCaps; // структура в которую помещается информация об устройстве begin WaveNums:=waveInGetNumDevs; if WaveNums>0 then // если в системе есть устройства аудиоввода,то begin for i:=0 to WaveNums-1 do // получаем характеристики всех имеющихся устройств begin waveInGetDevCaps(i,@WaveInCaps,sizeof(TWaveInCaps)); // добавляем наименование устройства MainForm.Memo.Lines.Add(PChar(@WaveInCaps.szPname)); for j:=1 to High(modes) do begin // выводим поддерживаемые устройством режимы работы if (modes[j].mode and WaveInCaps.dwFormats)=modes[j].mode then Memo.Lines.Add(modes[j].descr); end; end; end; end;


Рис 1. Сведения, выводимые процедурой ShowInfo.

Теперь Вы можете определить количество устройств аудиоввода Waveform audio и поддерживаемые ими режимы. Далее рассмотрим еще несколько функций, непосредственно обеспечивающих работу с звуковыми устройствами. Функция waveInOpen открывает имеющееся устройство ввода Waveform audio для оцифровки сигнала.

function waveInOpen( lphWaveIn: PHWAVEIN;
uDeviceID: UINT;
lpFormatEx: PWaveFormatEx;
dwCallback,
dwInstance,
dwFlags: DWORD
): MMRESULT; stdcall;


Здесь
lphWaveIn - указатель на идентификатор открытого Waveform audio устройства. Идентификатор используется после того, как устройство открыто, в других функциях Waveform audio;
uDeviceID - номер открываемого устройства (см. waveInGetNumDevs). Это может быть также идентификатор уже открытого ранее устройства. Вы можете использовать значение WAVE_MAPPER для того, чтобы функция автоматически выбрала совместимое с требуемым форматом данных устройство;
lpFormatEx - указатель на структуру типа TWaveFormatEx

type TWaveFormatEx = packed record wFormatTag: Word; { format type }
nChannels: Word; { number of channels (i.e. mono, stereo, etc.) }
nSamplesPerSec: DWORD; { sample rate }
nAvgBytesPerSec: DWORD; { for buffer estimation }
nBlockAlign: Word; { block size of data }
wBitsPerSample: Word; { number of bits per sample of mono data }
cbSize: Word; { the count in bytes of the size of }
end;

В этой структуре значения полей следующее:
wFormatTag - формат Waveform audio. Мы будем использовать значение WAVE_FORMAT_PCM (это означает импульсно-кодовая модуляция) другие возможные значения смотрите в заголовочном файле MMREG.H;
nChannels - количество каналов. Обычно 1 (моно) или 2(стерео);
nSamplesPerSec - частота дискретизации. Для формата PCM - в классическом смысле, т.е. количество выборок в секунду. Согласно теореме отсчетов должна вдвое превышать частоту оцифровываемого сигнала. Обычно находится в диапазоне от 8000 до 44100 выборок в секунду;
nAvgBytesPerSec - средняя скорость передачи данных. Для PCM равна nSamplesPerSec*nBlockAlign;
nBlockAlign - для PCM равен (nChannels*wBitsPerSample)/8;
wBitsPerSample - количество бит в одной выборке. Для PCM равно 8 или 16;
cbSize - равно 0. Подробности в Microsoft Multimedia Programmer's Reference;
dwCallback - адрес callback-функции, идентификатор окна или потока, вызываемого при наступлении события;
dwInstance - пользовательский параметр в callback-механизме. Сам по себе не используется
dwFlags - флаги для открываемого устройства:
CALLBACK_EVENT dwCallback-параметр - код сообщения (an event handle);
CALLBACK_FUNCTION dwCallback-параметр - адрес процедуры-обработчика;
CALLBACK_NULL dwCallback-параметр не используется;
CALLBACK_THREAD dwCallback-параметр - идентификатор потока команд;
CALLBACK_WINDOW dwCallback-параметр - идентификатор окна;
WAVE_FORMAT_DIRECT если указан этот флаг, ACM-драйвер не выполняет преобразование данных;
WAVE_FORMAT_QUERY функция запрашивает устройство для определения, поддерживает ли оно указанный формат, но не открывает его;

В случае использование Callback процедуры она имеет следующий вид:
procedure waveInProc(hwi: HWAVEIN; uMsg,dwInstance, dwParam1,dwParam2: DWORD);stdcall; begin // что-то делаем end; Параметры процедуры имеют следующее значение:
hwi - идентификатор связанного с функцией открытого устройства;
uMsg - Waveform audio сообщение. Может принимать значения:
WIM_CLOSE посылается, когда устройство закрывается функцией waveInClose;
WIM_DATA устройство завершило передачу данных в блок памяти, установленный процедурой waveInAddBuffer;
WIM_OPEN сообщение посылается если устройство открыто функцией waveInOpen;
dwInstance - данные, определенные пользователем при вызове waveInOpen;
dwParam1, dwParam2 - параметры сообщения.

Необходимо заметить, что в Microsoft Multimedia Programmer's Reference написано, что из callback-процедуры нельзя вызывать никаких системных функций кроме:
EnterCriticalSection, LeaveCriticalSection, midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage, PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime, timeKillEvent, и timeSetEvent , поскольку это вызывает deadlock.
Я столкнулся с весьма серьезным препятствием из-за этого ограничения, и решил все-таки рискнуть. В ходе небольших экспериментов я выяснил, что данное ограничение не распространяется на группу waveInAddBuffer, waveInReset и waveInClose, и возможно, некоторые другие. Не было проблем и с использованием функций Reset, BlockWrite, BlockRead, CloseFile. Говоря более точно, я так и не обнаружил возникновения deadlock, какие бы функции не вызывал изнутри waveInProc. Самое главное - не инициировать бесконечный рекурсивный вызов waveInProc. Для этого необходимо хорошо продумать обработчики поступающих в waveInProc сообщений.
Вообще, рекомендую использовать механизм оконных сообщений вместо callback. Это позволяет избежать ненужных экспериментов и возможной неработоспособности программы в других версиях ОС. Более подробно реализация этого механизма приведена в примере. Функция waveInPrepareHeader выполняет подготовку буфера для операции загрузки данных:

function waveInPrepareHeader( hWaveIn: HWAVEIN;
lpWaveInHdr: PWaveHdr;
uSize: UINT
): MMRESULT; stdcall;

Здесь:
hWaveIn - идентификатор открытого устройства;
lpWaveInHdr - адрес структуры WaveHdr:
type TWaveHdr = record lpData: PChar; { pointer to locked data buffer }
dwBufferLength: DWORD; { length of data buffer }
dwBytesRecorded: DWORD; { used for input only }
dwUser: DWORD; { for client's use }
dwFlags: DWORD; { assorted flags}
dwLoops: DWORD; { loop control counter }
lpNext: PWaveHdr; { reserved for driver }
reserved: DWORD; { reserved for driver }
end;

lpData - адрес буфера для загрузки данных;
dwBufferLength - длина буфера в байтах;
dwBytesRecorded - для режима загрузки данных определяет количество загруженных в буфер байт;
dwUser - пользовательские данные
dwFlags - флаги. Могут иметь следующие значения:
WHDR_DONE устанавливается драйвером при завершении загрузки буфера данными;
WHDR_PREPARED устанавливается системой. Показывает готовность буфера к загрузке данных;
WHDR_INQUEUE устанавливается системой когда буфер установлен в очередь;
dwLoops - используется только при воспроизведении. При записи звука всегда 0;
lpNext - зарезервировано;
reserved - зарезервировано;
uSize - размер структуры WaveHdr в байтах;

Функция waveInPrepareHeader вызывается только один раз для каждого устанавливаемого в очередь загрузки буфера. Существует функция waveInUnprepareHeader, с такими же параметрами, которая освобождает ресурсы системы по сопровождению выделенного под загрузку блока. waveInUnprepareHeader должна быть вызвана до удаления выделенного под буфер загрузки блока памяти.
Функция waveInAddBuffer ставит в очередь на загрузку данными буфер памяти. Когда буфер заполнен, система уведомляет об этом приложение (см. выше waveInOpen).
function waveInAddBuffer( hWaveIn: HWAVEIN;
lpWaveInHdr: PWaveHdr;
uSize: UINT
): MMRESULT; stdcall;

Здесь:
hWaveIn - идентификатор открытого Waveform audio устройства ввода;
lpWaveInHdr - адрес структуры TWaveHdr;
uSize - размер WaveHdr в байтах;

Функция waveInReset останавливает операцию загрузки данных. Все текущие буферы отмечаются как обработанные и приложение уведомляется о завершении загрузки данных (см. waveInOpen).
function waveInReset( hWaveIn: HWAVEIN ): MMRESULT; stdcall; Здесь:
hWaveIn - идентификатор открытого Waveform audio устройства.
Функция waveInClose закрывает открытое устройство ввода:

function waveInClose(
hWaveIn: HWAVEIN
): MMRESULT; stdcall;
hWaveIn - идентификатор открытого устройства;


MMRESULT может принимать следующие значения:
MMSYSERR_NOERROR нет ошибок;
MMSYSERR_ALLOCATED указанный ресурс уже выделен;
MMSYSERR_BADDEVICEID указанный идентификатор устройства вне диапазона;
MMSYSERR_NODRIVER отсутствует драйвер устройства;
MMSYSERR_NOMEM невозможно выделить или зафиксировать блок памяти;
WAVERR_BADFORMAT попытка открытия с неподдерживаемым форматом данных;
MMSYSERR_INVALHANDLE параметром является недопустимый идентификатор;
WAVERR_STILLPLAYING указанный буфер все еще в очереди;
WAVERR_UNPREPARED буфер не был подготовлен;

Пример реализации описанного в статье механизма (Delphi 3) Вы можете скачать (17.7 K)

Александр Галилов





Начало  Назад  Вперед