Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (SoundBlaster). Надеюсь он поможет разобраться вам с этой сложной темой.
Включенный в код модуль RECUNIT делает всю изнурительную работу по извлечению звука со входа звуковой карты.
Var WaveRecorder : TWaveRecorder; WaveRecorder := TwaveRecorder(2048, 4); // 4 размером 2048 байт { Устанавливает параметры дискретизации } With WaveRecorder.pWavefmtEx Do Begin wFormatTag := WAVE_FORMAT_PCM; nChannels := 1; nSamplesPerSec := 20000; wBitsPerSample := 16; nAvgBytesPerSec := nSamplesPerSec*(wBitsPerSample div 8)*nChannels; End; // Затем используем вариантную запись, поскольку я не знаю // как получить адрес самого объекта WaveRecorder.SetupRecord(@WaveRecorder); // Начинаем запись WaveRecorder.StartRecord; ... При каждом заполнении буфера вызывается процедура WaveRecorder.Processbuffer. // Заканчиваем запись WaveRecorder.StopRecord; WaveRecorder.Destroy; |
{ Имя файла: RECUNIT.PAS V 1.01 Создан: Авг 19 1996 в 21:56 на IBM ThinkPad Ревизия #7: Авг 22 1997, 15:01 на IBM ThinkPad -John Mertus Данный модуль содержит необходимые процедуры для записи звука. Версия 1.00 - первый релиз 1.01 - добавлен TWaveInGetErrorText } {-----------------Unit-RECUNIT---------------------John Mertus---Авг 96---} Unit RECUNIT; {*************************************************************************} Interface Uses Windows, MMSystem, SysUtils, MSACM; { Ниже определен класс TWaveRecorder для обслуживания входа звуковой } { карты. Ожидается, что новый класс будет производным от TWaveRecorder } { и перекроет TWaveRecorder.ProcessBuffer. После начала записи данная } { процедура вызывается каждый раз при наличии в буфере аудио-данных. } Const MAX_BUFFERS = 8; type PWaveRecorder = ^TWaveRecorder; TWaveRecorder = class(TObject) Constructor Create(BfSize, TotalBuffers : Integer); Destructor Destroy; Override; Procedure ProcessBuffer(uMsg : Word; P : Pointer; n : Integer); Virtual; private fBufferSize : Integer; // Размер буфера BufIndex : Integer; fTotalBuffers : Integer; pWaveHeader : Array [0..MAX_BUFFERS-1] of PWAVEHDR; hWaveHeader : Array [0..MAX_BUFFERS-1] of THANDLE; hWaveBuffer : Array [0..MAX_BUFFERS-1] of THANDLE; hWaveFmtEx : THANDLE; dwByteDataSize : DWORD; dwTotalWaveSize : DWORD; RecordActive : Boolean; bDeviceOpen : Boolean; { Внутренние функции класса } Function InitWaveHeaders : Boolean; Function AllocPCMBuffers : Boolean; Procedure FreePCMBuffers; Function AllocWaveFormatEx : Boolean; Procedure FreeWaveFormatEx; Function AllocWaveHeaders : Boolean; Procedure FreeWaveHeader; Function AddNextBuffer : Boolean; Procedure CloseWaveDeviceRecord; public { Public declarations } pWaveFmtEx : PWaveFormatEx; WaveBufSize : Integer; // Размер поля nBlockAlign InitWaveRecorder : Boolean; RecErrorMessage : String; QueuedBuffers, ProcessedBuffers : Integer; pWaveBuffer : Array [0..MAX_BUFFERS-1] of lpstr; WaveIn : HWAVEIN; { Дескриптор Wav-устройства } Procedure StopRecord; Function 477576218068StartRecord : Boolean; Function477576218068 SetupRecord(P : PWaveRecorder) : Boolean; end; {*************************************************************************} implementation {-------------TWaveInGetErrorText-----------John Mertus---14-Июнь--97--} Function TWaveInGetErrorText(iErr : Integer) : String; { Выдает сообщения об ошибках WaveIn в формате Pascal } { iErr - номер ошибки } { } {**********************************************************************} Var PlayInErrorMsgC : Array [0..255] of Char; Begin waveInGetErrorText(iErr,PlayInErrorMsgC,255); TWaveInGetErrorText := StrPas(PlayInErrorMsgC); End; {-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--} Function TWaveRecorder.AllocWaveFormatEx : Boolean; { Распределяем формат большого размера, требуемый для инсталляции ACM-в} { } {**********************************************************************} Var MaxFmtSize : UINT; BEGIN { maxFmtSize - сумма sizeof(WAVEFORMATEX) + pwavefmtex.cbSize } If( acmMetrics( 0, ACM_METRIC_MAX_SIZE_FORMAT, maxFmtSize ) <> 0) >Then Begin RecErrorMessage := 'Ошибка получения размера формата максимального сжатия'; AllocWaveFormatEx := False; Exit; End; { распределяем структуру WAVEFMTEX } hWaveFmtEx := GlobalAlloc(GMEM_MOVEABLE, maxFmtSize); If (hWaveFmtEx = 0) Then Begin RecErrorMessage := 'Ошибка распределения памяти для структуры WaveFormatEx'; AllocWaveFormatEx := False; Exit; End; pWaveFmtEx := PWaveFormatEx(GlobalLock(hWaveFmtEx)); If (pWaveFmtEx = Nil) Then Begin RecErrorMessage := 'Ошибка блокировки памяти WaveFormatEx'; AllocWaveFormatEx := False; Exit; End; { инициализация формата в стандарте PCM } ZeroMemory( pwavefmtex, maxFmtSize ); pwavefmtex.wFormatTag := WAVE_FORMAT_PCM; pwavefmtex.nChannels := 1; pwavefmtex.nSamplesPerSec := 20000; pwavefmtex.nBlockAlign := 1; pwavefmtex.wBitsPerSample := 16; pwavefmtex.nAvgBytesPerSec := pwavefmtex.nSamplesPerSec* (pwavefmtex.wBitsPerSample div 8)*pwavefmtex.nChannels; pwavefmtex.cbSize := 0; { Все успешно, идем домой } AllocWaveFormatEx := True; end; {-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--} Function TWaveRecorder.InitWaveHeaders : Boolean; { Распределяем память, обнуляем заголовок wave и инициализируем } { } {**********************************************************************} Var i : Integer; BEGIN { делаем размер буфера кратным величине блока... } WaveBufSize := fBufferSize - (fBufferSize mod pwavefmtex.nBlockAlign); { Устанавливаем wave-заголовки } For i := 0 to fTotalBuffers-1 Do With pWaveHeader[i]^ Do Begin lpData := pWaveBuffer[i]; // адрес буфера waveform dwBufferLength := WaveBufSize; // размер, в байтах, буфера dwBytesRecorded := 0; // смотри ниже dwUser := 0; // 32 бита данных пользователя dwFlags := 0; // смотри ниже dwLoops := 0; // смотри ниже lpNext := Nil; // зарезервировано; должен быть ноль reserved := 0; // зарезервировано; должен быть ноль End; InitWaveHeaders := TRUE; END; {-------------AllocWaveHeader----------------John Mertus---14-Июнь--97--} Function TWaveRecorder.AllocWaveHeaders : Boolean; { Распределяем и блокируем память заголовка } { } {***********************************************************************} Var i : Integer; BEGIN For i := 0 to fTotalBuffers-1 Do begin hwaveheader[i] := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE or GMEM_ZEROINIT, sizeof(TWAVEHDR)); if (hwaveheader[i] = 0) Then begin { Примечание: Это может привести к утечке памяти, надеюсь скоро исправить } RecErrorMessage := 'Ошибка распределения памяти для wave-заголовка'; AllocWaveHeaders := FALSE; Exit; end; pwaveheader[i] := GlobalLock (hwaveheader[i]); If (pwaveheader[i] = Nil ) Then begin { Примечание: Это может привести к утечке памяти, надеюсь скоро исправить } RecErrorMessage := 'Не могу заблокировать память заголовка для записи'; AllocWaveHeaders := FALSE; Exit; end; End; AllocWaveHeaders := TRUE; END; {---------------FreeWaveHeader---------------John Mertus---14-Июнь--97--} Procedure TWaveRecorder.FreeWaveHeader; { Просто освобождаем распределенную AllocWaveHeaders память. } { } {***********************************************************************} Var i : Integer; BEGIN For i := 0 to fTotalBuffers-1 Do begin If (hWaveHeader[i] <> 0) Then Begin GlobalUnlock(hwaveheader[i]); GlobalFree(hwaveheader[i]); hWaveHeader[i] := 0; End end; END; {-------------AllocPCMBuffers----------------John Mertus---14-Июнь--97--} Function TWaveRecorder.AllocPCMBuffers : Boolean; { Распределяем и блокируем память waveform. } { } {***********************************************************************} Var i : Integer; BEGIN For i := 0 to fTotalBuffers-1 Do begin hWaveBuffer[i] := GlobalAlloc( GMEM_MOVEABLE or GMEM_SHARE, fBufferSize ); If (hWaveBuffer[i] = 0) Then begin { Здесь возможна утечка памяти } RecErrorMessage := 'Ошибка распределения памяти wave-буфера'; AllocPCMBuffers := False; Exit; end; pWaveBuffer[i] := GlobalLock(hWaveBuffer[i]); If (pWaveBuffer[i] = Nil) Then begin { Здесь возможна утечка памяти } RecErrorMessage := 'Ошибка блокирования памяти wave-буфера'; AllocPCMBuffers := False; Exit; end; pWaveHeader[i].lpData := pWaveBuffer[i]; End; AllocPCMBuffers := TRUE; END; {--------------FreePCMBuffers----------------John Mertus---14-Июнь--97--} Procedure TWaveRecorder.FreePCMBuffers; { Освобождаем использованную AllocPCMBuffers память. } { } {***********************************************************************} Var i : Integer; BEGIN For i := 0 to fTotalBuffers-1 Do begin If (hWaveBuffer[i] <> 0) Then Begin GlobalUnlock( hWaveBuffer[i] ); GlobalFree( hWaveBuffer[i] ); hWaveBuffer[i] := 0; pWaveBuffer[i] := Nil; End; end; END; {--------------FreeWaveFormatEx--------------John Mertus---14-Июнь--97--} Procedure TWaveRecorder.FreeWaveFormatEx; { Просто освобождаем заголовки ExFormat headers } { } {***********************************************************************} BEGIN If (pWaveFmtEx = Nil) Then Exit; GlobalUnlock(hWaveFmtEx); GlobalFree(hWaveFmtEx); pWaveFmtEx := Nil; END; {-------------TWaveRecorder.Create------------John Mertus-----Авг--97--} Constructor TWaveRecorder.Create(BFSize, TotalBuffers : Integer); { Устанавливаем wave-заголовки, инициализируем указатели данных и } { и распределяем буферы дискретизации } { BFSize - размер буфера в байтах } { } {**********************************************************************} Var i : Integer; BEGIN Inherited Create; For i := 0 to fTotalBuffers-1 Do Begin hWaveHeader[i] := 0; hWaveBuffer[i] := 0; pWaveBuffer[i] := Nil; pWaveFmtEx := Nil; End; fBufferSize := BFSize; fTotalBuffers := TotalBuffers; { распределяем память для структуры wave-формата } If(Not AllocWaveFormatEx) Then Begin InitWaveRecorder := FALSE; Exit; End; { ищем устройство, совместимое с доступными wave-характеристиками } If (waveInGetNumDevs < 1 ) Then Begin RecErrorMessage := 'Не найдено устройств, способных записывать звук'; InitWaveRecorder := FALSE; Exit; End; { распределяем память wave-заголовка } If (Not AllocWaveHeaders) Then Begin InitWaveRecorder := FALSE; Exit; End; { распределяем память буфера wave-данных } If (Not AllocPCMBuffers) Then Begin InitWaveRecorder := FALSE; Exit; End; InitWaveRecorder := TRUE; END; {---------------------Destroy----------------John Mertus---14-Июнь--97--} Destructor TWaveRecorder.Destroy; { Просто освобождаем всю память, распределенную InitWaveRecorder. } { } {***********************************************************************} BEGIN FreeWaveFormatEx; FreePCMBuffers; FreeWaveHeader; Inherited Destroy; END; {------------CloseWaveDeviceRecord-----------John Mertus---14-Июнь--97--} Procedure TWaveRecorder.CloseWaveDeviceRecord; { Просто освобождаем (закрываем) waveform-устройство. } { } {***********************************************************************} Var i : Integer; BEGIN { если устройство уже закрыто, то выходим } If (Not bDeviceOpen) Then Exit; { работа с заголовками - unprepare } For i := 0 to fTotalBuffers-1 Do If (waveInUnprepareHeader(WaveIn, pWaveHeader[i], sizeof(TWAVEHDR)) <> 0 ) Then RecErrorMessage := 'Ошибка в waveInUnprepareHeader'; { сохраняем общий объем записи и обновляем показ } dwTotalwavesize := dwBytedatasize; { закрываем входное wave-устройство } If (waveInClose(WaveIn) <> 0) Then RecErrorMessage := 'Ошибка закрытия входного устройства'; { сообщаем вызвавшей функции, что устройство закрыто } bDeviceOpen := FALSE; END; {------------------StopRecord-----------------John Mertus---14-Июнь--97--} Procedure TWaveRecorder.StopRecord; { Останавливаем запись и устанавливаем некоторые флаги. } { } {***********************************************************************} Var iErr : Integer; BEGIN RecordActive := False; iErr := waveInReset(WaveIn); { прекращаем запись и возвращаем стоящие в очереди буферы } If (iErr <> 0) Then Begin RecErrorMessage := 'Ошибка в waveInReset'; End; CloseWaveDeviceRecord; END; {--------------AddNextBuffer------------------John Mertus---14-Июнь--97--} Function TWaveRecorder.AddNextBuffer : Boolean; { Добавляем буфер ко входной очереди и переключаем буферный индекс. } { } {***********************************************************************} Var iErr : Integer; BEGIN { ставим буфер в очередь для получения очередной порции данных } iErr := waveInAddBuffer(WaveIn, pwaveheader[bufindex], sizeof(TWAVEHDR)); If (iErr <> 0) Then begin StopRecord; RecErrorMessage := 'Ошибка добавления буфера' + TWaveInGetErrorText(iErr); AddNextBuffer := FALSE; Exit; end; { переключаемся на следующий буфер } bufindex := (bufindex+1) mod fTotalBuffers; QueuedBuffers := QueuedBuffers + 1; AddNextBuffer := TRUE; END; {--------------BufferDoneCallBack------------John Mertus---14-Июнь--97--} Procedure BufferDoneCallBack( hW : HWAVE; // дескриптор waveform-устройства uMsg : DWORD; // посылаемое сообщение dwInstance : DWORD; // экземпляр данных dwParam1 : DWORD; // определяемый приложением параметр dwParam2 : DWORD; // определяемый приложением параметр ); stdcall; { Вызывается при наличии у wave-устройства какой-либо информации, } { например при заполнении буфера } { } {***********************************************************************} Var BaseRecorder : PWaveRecorder; BEGIN BaseRecorder := Pointer(DwInstance); With BaseRecorder^ Do Begin ProcessBuffer(uMsg, pWaveBuffer[ProcessedBuffers Mod fTotalBuffers], WaveBufSize); If (RecordActive) Then Case uMsg of WIM_DATA: Begin BaseRecorder.AddNextBuffer; ProcessedBuffers := ProcessedBuffers+1; End; End; End; END; {------------------StartRecord---------------John Mertus---14-Июнь--97--} Function TWaveRecorder.StartRecord : Boolean; { Начало записи. } { } {***********************************************************************} Var iErr, i : Integer; BEGIN { начало записи в первый буфер } iErr := WaveInStart(WaveIn); If (iErr <> 0) Then begin CloseWaveDeviceRecord; RecErrorMessage := 'Ошибка начала записи wave: ' + TWaveInGetErrorText(iErr); end; RecordActive := TRUE; { ставим в очередь следующие буферы } For i := 1 to fTotalBuffers-1 Do If (Not AddNextBuffer) Then Begin StartRecord := FALSE; Exit; End; StartRecord := True; END; {-----------------SetupRecord---------------John Mertus---14-Июнь--97--} Function TWaveRecorder.SetupRecord(P : PWaveRecorder) : Boolean; { Данная функция делает всю работу по созданию waveform-"записывателя". } { } {***********************************************************************} Var iErr, i : Integer; BEGIN dwTotalwavesize := 0; dwBytedatasize := 0; bufindex := 0; ProcessedBuffers := 0; QueuedBuffers := 0; { открываем устройство для записи } iErr := waveInOpen(@WaveIn, WAVE_MAPPER, pWaveFmtEx, Integer(@BufferDoneCallBack), Integer(P), CALLBACK_FUNCTION + WAVE_ALLOWSYNC ); If (iErr <> 0) Then Begin RecErrorMessage := 'Не могу открыть входное устройство для записи: ' + ^M + TWaveInGetErrorText(iErr); SetupRecord := FALSE; Exit; End; { сообщаем CloseWaveDeviceRecord(), что устройство открыто } bDeviceOpen := TRUE; { подготавливаем заголовки } InitWaveHeaders(); For i := 0 to fTotalBuffers-1 Do Begin iErr := waveInPrepareHeader( WaveIn, pWaveHeader[I], sizeof(TWAVEHDR)); If (iErr <> 0) Then begin CloseWaveDeviceRecord; RecErrorMessage := 'Ошибка подготовки заголовка для записи: ' + ^M + TWaveInGetErrorText(iErr); SetupRecord := FALSE; Exit; end; End; { добавляем первый буфер } If (Not AddNextBuffer) Then begin SetupRecord := FALSE; Exit; end; SetupRecord := TRUE; END; {-----------------ProcessBuffer---------------John Mertus---14-Июнь--97--} Procedure TWaveRecorder.ProcessBuffer(uMsg: Word; P : Pointer; n : Integer); { Болванка процедуры, вызываемой при готовности буфера. } { } {***********************************************************************} BEGIN END; END. |
[000242]