Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (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 div8) * 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 }
{ Ниже определен класс TWaveRecorder для обслуживания входа звуковой } { карты. Ожидается, что новый класс будет производным от TWaveRecorder } { и перекроет TWaveRecorder.ProcessBuffer. После начала записи данная } { процедура вызывается каждый раз при наличии в буфере аудио-данных. }
function TWaveInGetErrorText(iErr: Integer): string;
{ Выдает сообщения об ошибках WaveIn в формате Pascal } { iErr - номер ошибки } { } {**********************************************************************} var
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;
{ Распределяем память, обнуляем заголовок wave и инициализируем } { } {**********************************************************************} var
i: Integer;
begin
{ делаем размер буфера кратным величине блока... } WaveBufSize := fBufferSize - (fBufferSize mod pwavefmtex.nBlockAlign);
{ Устанавливаем wave-заголовки } for i := 0to fTotalBuffers - 1do 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;
{ Распределяем и блокируем память заголовка } { } {***********************************************************************} var
i: Integer;
begin
for i := 0to fTotalBuffers - 1do 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;
{ Просто освобождаем распределенную AllocWaveHeaders память. } { } {***********************************************************************} var
i: Integer;
begin
for i := 0to fTotalBuffers - 1do begin if (hWaveHeader[i] <> 0) then begin GlobalUnlock(hwaveheader[i]); GlobalFree(hwaveheader[i]); hWaveHeader[i] := 0; end end; end;
{ Распределяем и блокируем память waveform. } { } {***********************************************************************} var
i: Integer;
begin
for i := 0to fTotalBuffers - 1do 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 память. } { } {***********************************************************************} var
i: Integer;
begin
for i := 0to fTotalBuffers - 1do begin if (hWaveBuffer[i] <> 0) then begin GlobalUnlock(hWaveBuffer[i]); GlobalFree(hWaveBuffer[i]); hWaveBuffer[i] := 0; pWaveBuffer[i] := nil; end; end; end;
{ Устанавливаем wave-заголовки, инициализируем указатели данных и } { и распределяем буферы дискретизации } { BFSize - размер буфера в байтах } { } {**********************************************************************} var
i: Integer; begin
inherited Create; for i := 0to fTotalBuffers - 1do 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;
{ Останавливаем запись и устанавливаем некоторые флаги. } { } {***********************************************************************} var
iErr: Integer;
begin
RecordActive := False; iErr := waveInReset(WaveIn); { прекращаем запись и возвращаем стоящие в очереди буферы } if (iErr <> 0) then begin RecErrorMessage := 'Ошибка в waveInReset'; end;
{ Добавляем буфер ко входной очереди и переключаем буферный индекс. } { } {***********************************************************************} 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;
{ Вызывается при наличии у 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;
{ Начало записи. } { } {***********************************************************************} var
iErr, i: Integer;
begin
{ начало записи в первый буфер } iErr := WaveInStart(WaveIn); if (iErr <> 0) then begin CloseWaveDeviceRecord; RecErrorMessage := 'Ошибка начала записи wave: ' + TWaveInGetErrorText(iErr);
end;
RecordActive := TRUE;
{ ставим в очередь следующие буферы } for i := 1to fTotalBuffers - 1do if (not AddNextBuffer) then begin StartRecord := FALSE; Exit; end;
function TWaveRecorder.SetupRecord(P: PWaveRecorder): Boolean;
{ Данная функция делает всю работу по созданию waveform-"записывателя". } { } {***********************************************************************} var
{ сообщаем CloseWaveDeviceRecord(), что устройство открыто } bDeviceOpen := TRUE;
{ подготавливаем заголовки }
InitWaveHeaders();
for i := 0to fTotalBuffers - 1do 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;