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

Previous  Top  Next

    
 

 

 

Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (SoundBlaster). Надеюсь он поможет разобраться вам с этой сложной темой.

 

Включенный в код модуль RECUNIT делает всю изнурительную работу по извлечению звука со входа звуковой карты.

Code:

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;

 

 

 

 

 

 

Code:

{

Имя файла: 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 477576218068 StartRecord: 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.

 

 

 

©Drkb::03614

       

Взято с http://delphiworld.narod.ru