Программисту задают вопрос:
- Сколько Вы знаете позиций для занятий любовью?
- Он сверху, она снизу. Он снизу, она сверху.
- И все?
...немного подумав
- Оба сверху. Оба снизу.
Ниже приведен код, обрабатывающий аудиосигнал, получаемый со входа звуковой карты (SoundBlaster). Надеюсь он поможет разобраться вам с этой сложной темой.
Включенный в код модуль RECUNIT делает всю изнурительную работу по извлечению звука со входа звуковой карты.
var
WaveRecorder: TWaveRecorder;
...
WaveRecorder := TwaveRecorder(2048, 4); // 4 размером 2048 байт{ Устанавливает параметры дискретизации }with WaveRecorder.pWavefmtEx dobegin
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;
{*************************************************************************}interfaceuses
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) > thenbegin
RecErrorMessage := 'Ошибка получения размера формата максимального сжатия';
AllocWaveFormatEx := False;
Exit;
end;
{ распределяем структуру WAVEFMTEX }
hWaveFmtEx := GlobalAlloc(GMEM_MOVEABLE, maxFmtSize);
if (hWaveFmtEx = 0) thenbegin
RecErrorMessage := 'Ошибка распределения памяти для структуры WaveFormatEx';
AllocWaveFormatEx := False;
Exit;
end;
pWaveFmtEx := PWaveFormatEx(GlobalLock(hWaveFmtEx));
if (pWaveFmtEx = nil) thenbegin
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 dowith pWaveHeader[i]^ dobegin
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;
beginfor i := 0 to fTotalBuffers - 1 dobegin
hwaveheader[i] := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE or
GMEM_ZEROINIT, sizeof(TWAVEHDR));
if (hwaveheader[i] = 0) thenbegin{ Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
RecErrorMessage := 'Ошибка распределения памяти для wave-заголовка';
AllocWaveHeaders := FALSE;
Exit;
end;
pwaveheader[i] := GlobalLock(hwaveheader[i]);
if (pwaveheader[i] = nil) thenbegin{ Примечание: Это может привести к утечке памяти, надеюсь скоро исправить }
RecErrorMessage := 'Не могу заблокировать память заголовка для записи';
AllocWaveHeaders := FALSE;
Exit;
end;
end;
AllocWaveHeaders := TRUE;
end;
{---------------FreeWaveHeader---------------John Mertus---14-Июнь--97--}procedure TWaveRecorder.FreeWaveHeader;
{ Просто освобождаем распределенную AllocWaveHeaders память. }{ }{***********************************************************************}var
i: Integer;
beginfor i := 0 to fTotalBuffers - 1 dobeginif (hWaveHeader[i] <> 0) thenbegin
GlobalUnlock(hwaveheader[i]);
GlobalFree(hwaveheader[i]);
hWaveHeader[i] := 0;
endend;
end;
{-------------AllocPCMBuffers----------------John Mertus---14-Июнь--97--}function TWaveRecorder.AllocPCMBuffers: Boolean;
{ Распределяем и блокируем память waveform. }{ }{***********************************************************************}var
i: Integer;
beginfor i := 0 to fTotalBuffers - 1 dobegin
hWaveBuffer[i] := GlobalAlloc(GMEM_MOVEABLE or GMEM_SHARE, fBufferSize);
if (hWaveBuffer[i] = 0) thenbegin{ Здесь возможна утечка памяти }
RecErrorMessage := 'Ошибка распределения памяти wave-буфера';
AllocPCMBuffers := False;
Exit;
end;
pWaveBuffer[i] := GlobalLock(hWaveBuffer[i]);
if (pWaveBuffer[i] = nil) thenbegin{ Здесь возможна утечка памяти }
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;
beginfor i := 0 to fTotalBuffers - 1 dobeginif (hWaveBuffer[i] <> 0) thenbegin
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 }{ }{***********************************************************************}beginif (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;
begininherited Create;
for i := 0 to fTotalBuffers - 1 dobegin
hWaveHeader[i] := 0;
hWaveBuffer[i] := 0;
pWaveBuffer[i] := nil;
pWaveFmtEx := nil;
end;
fBufferSize := BFSize;
fTotalBuffers := TotalBuffers;
{ распределяем память для структуры wave-формата }if (not AllocWaveFormatEx) thenbegin
InitWaveRecorder := FALSE;
Exit;
end;
{ ищем устройство, совместимое с доступными wave-характеристиками }if (waveInGetNumDevs < 1) thenbegin
RecErrorMessage := 'Не найдено устройств, способных записывать звук';
InitWaveRecorder := FALSE;
Exit;
end;
{ распределяем память wave-заголовка }if (not AllocWaveHeaders) thenbegin
InitWaveRecorder := FALSE;
Exit;
end;
{ распределяем память буфера wave-данных }if (not AllocPCMBuffers) thenbegin
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 doif (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) thenbegin
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) thenbegin
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^ dobegin
ProcessBuffer(uMsg, pWaveBuffer[ProcessedBuffers mod fTotalBuffers],
WaveBufSize);
if (RecordActive) thencase 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) thenbegin
CloseWaveDeviceRecord;
RecErrorMessage := 'Ошибка начала записи wave: ' +
TWaveInGetErrorText(iErr);
end;
RecordActive := TRUE;
{ ставим в очередь следующие буферы }for i := 1 to fTotalBuffers - 1 doif (not AddNextBuffer) thenbegin
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) thenbegin
RecErrorMessage := 'Не могу открыть входное устройство для записи: ' + ^M
+
TWaveInGetErrorText(iErr);
SetupRecord := FALSE;
Exit;
end;
{ сообщаем CloseWaveDeviceRecord(), что устройство открыто }
bDeviceOpen := TRUE;
{ подготавливаем заголовки }
InitWaveHeaders();
for i := 0 to fTotalBuffers - 1 dobegin
iErr := waveInPrepareHeader(WaveIn, pWaveHeader[I], sizeof(TWAVEHDR));
if (iErr <> 0) thenbegin
CloseWaveDeviceRecord;
RecErrorMessage := 'Ошибка подготовки заголовка для записи: ' + ^M +
TWaveInGetErrorText(iErr);
SetupRecord := FALSE;
Exit;
end;
end;
{ добавляем первый буфер }if (not AddNextBuffer) thenbegin
SetupRecord := FALSE;
Exit;
end;
SetupRecord := TRUE;
end;
{-----------------ProcessBuffer---------------John Mertus---14-Июнь--97--}procedure TWaveRecorder.ProcessBuffer(uMsg: Word; P: Pointer; n:
Integer);
{ Болванка процедуры, вызываемой при готовности буфера. }{ }{***********************************************************************}beginend;
end.