Обратите внимание на то, что подавляющее большинство (если не все) звуковые карты поддерживают промежуточные режимы записи-воспроизведения. Т.е. вполне возможно на карте с максимальной частотой дискретизации 44100 выборок/сек производить запись со скоростью 16000 выборок/сек, хотя это и не сообщается по запросу waveInGetDevCaps. wChannels: Word - количество входных каналов (1-моно, 2-стерео) wReserved1: Word - зарезервировано
Функция waveInGetErrorText возвращает текстовое описание возникших в ходе выполнения ошибок.
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 // если в системе есть устройства аудиоввода,то beginfor 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) dobegin // выводим поддерживаемые устройством режимы работы 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 для оцифровки сигнала.
Здесь lphWaveIn - указатель на идентификатор открытого Waveform audio устройства. Идентификатор используется после того, как устройство открыто, в других функциях Waveform audio; uDeviceID - номер открываемого устройства (см. waveInGetNumDevs). Это может быть также идентификатор уже открытого ранее устройства. Вы можете использовать значение WAVE_MAPPER для того, чтобы функция автоматически выбрала совместимое с требуемым форматом данных устройство; lpFormatEx - указатель на структуру типа TWaveFormatEx
type TWaveFormatEx = packed recordwFormatTag: 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-параметр - адрес процедуры-обработчика;
если указан этот флаг, 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;
Необходимо заметить, что в 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 = recordlpData: 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 закрывает открытое устройство ввода: