Советы по Delphi

         

Низкоуровневые процедуры обработки звука


Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (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]



Содержание раздела