Delphi - объектно-ориентированный язык программирования, разработанный компанией Borland в 1995 году. Он основан на языке программирования Pascal, но имеет более расширенные возможности и добавлены новые функции.
Delphi является интегрированной средой разработки (IDE), которая позволяет разрабатывать программное обеспечение для различных платформ, включая Windows, macOS, Android и iOS. Delphi достигает многоплатформенности с помощью...
Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (SoundBlaster). Надеюсь он поможет разобраться вам с этой сложной темой.
Включенный в код модуль RECUNIT делает всю изнурительную работу по извлечению звука со входа звуковой карты.
varWaveRecorder: 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---} unitRECUNIT;
{*************************************************************************} interfaceuses
Windows, MMSystem, SysUtils, MSACM;
{ Ниже определен класс TWaveRecorder для обслуживания входа звуковой } { карты. Ожидается, что новый класс будет производным от TWaveRecorder } { и перекроет TWaveRecorder.ProcessBuffer. После начала записи данная } { процедура вызывается каждый раз при наличии в буфере аудио-данных. } constMAX_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;
{ Внутренние функции класса } functionInitWaveHeaders: 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-устройства } procedureStopRecord; function
477576218068 StartRecord: Boolean; Function477576218068 SetupRecord(P: PWaveRecorder): Boolean; end
;
{*************************************************************************} implementation{-------------TWaveInGetErrorText-----------John Mertus---14-Июнь--97--} function
TWaveInGetErrorText(iErr: Integer): string
;
{ Выдает сообщения об ошибках WaveIn в формате Pascal } { iErr - номер ошибки } { } {**********************************************************************} varPlayInErrorMsgC: array
[0..255] of
Char; begin
waveInGetErrorText(iErr, PlayInErrorMsgC, 255); TWaveInGetErrorText := StrPas(PlayInErrorMsgC); end
;
{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--} functionTWaveRecorder.AllocWaveFormatEx: Boolean;
{ Распределяем формат большого размера, требуемый для инсталляции ACM-в} { } {**********************************************************************} varMaxFmtSize: 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 div8) * pwavefmtex.nChannels; pwavefmtex.cbSize := 0;
{ Все успешно, идем домой } AllocWaveFormatEx := True; end
;
{-------------InitWaveHeaders---------------John Mertus---14-Июнь--97--} functionTWaveRecorder.InitWaveHeaders: Boolean;
{ Распределяем память, обнуляем заголовок wave и инициализируем } { } {**********************************************************************} vari: Integer; begin
{ делаем размер буфера кратным величине блока... } WaveBufSize := fBufferSize - (fBufferSize mod
pwavefmtex.nBlockAlign);
{ Устанавливаем wave-заголовки } fori := 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--} functionTWaveRecorder.AllocWaveHeaders: Boolean;
{ Распределяем и блокируем память заголовка } { } {***********************************************************************} vari: 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--} procedureTWaveRecorder.FreeWaveHeader;
{ Просто освобождаем распределенную AllocWaveHeaders память. } { } {***********************************************************************} vari: 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--} functionTWaveRecorder.AllocPCMBuffers: Boolean;
{ Распределяем и блокируем память waveform. } { } {***********************************************************************} vari: 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--} procedureTWaveRecorder.FreePCMBuffers;
{ Освобождаем использованную AllocPCMBuffers память. } { } {***********************************************************************} vari: 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--} procedureTWaveRecorder.FreeWaveFormatEx;
{ Просто освобождаем заголовки ExFormat headers } { } {***********************************************************************} beginif
(pWaveFmtEx = nil
) then
Exit; GlobalUnlock(hWaveFmtEx); GlobalFree(hWaveFmtEx); pWaveFmtEx := nil
; end
;
{-------------TWaveRecorder.Create------------John Mertus-----Авг--97--} constructorTWaveRecorder.Create(BFSize, TotalBuffers: Integer);
{ Устанавливаем wave-заголовки, инициализируем указатели данных и } { и распределяем буферы дискретизации } { BFSize - размер буфера в байтах } { } {**********************************************************************} vari: 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--} destructorTWaveRecorder.Destroy;
{ Просто освобождаем всю память, распределенную InitWaveRecorder. } { } {***********************************************************************} beginFreeWaveFormatEx; FreePCMBuffers; FreeWaveHeader; inherited
Destroy; end
;
{------------CloseWaveDeviceRecord-----------John Mertus---14-Июнь--97--} procedureTWaveRecorder.CloseWaveDeviceRecord;
{ Просто освобождаем (закрываем) waveform-устройство. } { } {***********************************************************************} vari: Integer; begin
{ если устройство уже закрыто, то выходим } if
(not
bDeviceOpen) then
Exit;
{ работа с заголовками - unprepare } fori := 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--} procedureTWaveRecorder.StopRecord;
{ Останавливаем запись и устанавливаем некоторые флаги. } { } {***********************************************************************} variErr: Integer; begin
RecordActive := False
; iErr := waveInReset(WaveIn);
{ прекращаем запись и возвращаем стоящие в очереди буферы } if(iErr <> 0) then
begin
RecErrorMessage := 'Ошибка в waveInReset'; end
; CloseWaveDeviceRecord; end
;
{--------------AddNextBuffer------------------John Mertus---14-Июнь--97--} functionTWaveRecorder.AddNextBuffer: Boolean;
{ Добавляем буфер ко входной очереди и переключаем буферный индекс. } { } {***********************************************************************} variErr: 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) modfTotalBuffers; QueuedBuffers := QueuedBuffers + 1; AddNextBuffer := TRUE
; end
;
{--------------BufferDoneCallBack------------John Mertus---14-Июнь--97--} procedureBufferDoneCallBack( hW: HWAVE;
// дескриптор waveform-устройства uMsg: DWORD; // посылаемое сообщение dwInstance: DWORD; // экземпляр данных dwParam1: DWORD; // определяемый приложением параметр dwParam2: DWORD; // определяемый приложением параметр ); stdcall;
{ Вызывается при наличии у wave-устройства какой-либо информации, } { например при заполнении буфера } { } {***********************************************************************} varBaseRecorder: 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--} functionTWaveRecorder.StartRecord: Boolean;
{ Начало записи. } { } {***********************************************************************} variErr, i: Integer; begin
{ начало записи в первый буфер } iErr := WaveInStart(WaveIn); if
(iErr <> 0) then
begin
CloseWaveDeviceRecord; RecErrorMessage := 'Ошибка начала записи wave: ' + TWaveInGetErrorText(iErr); end
; RecordActive := TRUE
;
{ ставим в очередь следующие буферы } fori := 1 to
fTotalBuffers - 1 do
if
(not
AddNextBuffer) then
begin
StartRecord := FALSE
; Exit; end
; StartRecord := True
; end
;
{-----------------SetupRecord---------------John Mertus---14-Июнь--97--} functionTWaveRecorder.SetupRecord(P: PWaveRecorder): Boolean;
{ Данная функция делает всю работу по созданию waveform-"записывателя". } { } {***********************************************************************} variErr, 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(); fori := 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--} procedureTWaveRecorder.ProcessBuffer(uMsg: Word; P: Pointer; n: Integer);
{ Болванка процедуры, вызываемой при готовности буфера. } { } {***********************************************************************} beginend
; end
.