-
Здравсте. Я тут уже с ума схожу. Пытаюсь вопроизвести звук из *.WAV файла. Ну да это ладно. До этого доберусь. Для начала хотя бы просто плавный звук. Что имеем (подготовка): [code] const SoundChannels = 1; SoundSamples = 8000; SoundBits = 16; SoundBufferSize = SoundSamples * (SoundBits div 8); SoundFormat = 1;
var SndOutHeader: TWaveFormatEx; SndWaveOut: hWaveOut; SndOutBufferHeaders: array [0..1] of TWaveHdr; SndOutSoundBuffers: array [0..1] of array of byte; SndOutCurrentBuffer: integer; ...... with SndOutHeader do begin wFormatTag := WAVE_FORMAT_PCM; nChannels := SoundChannels; nSamplesPerSec := SoundSamples; wBitsPerSample := SoundBits; nBlockAlign := (nChannels * wBitsPerSample) div 8; nAvgBytesPerSec := nSamplesPerSec * nBlockAlign; cbSize := 0; end;
FillChar(SndOutBufferHeaders[0], SizeOf(SndOutBufferHeaders[0]), #0); SetLength(SndOutSoundBuffers[0], SoundSamples * (SoundBits div 8)); with SndOutBufferHeaders[0] do begin lpData := @SndOutSoundBuffers[0][0]; dwBufferLength := Length(SndOutSoundBuffers[0]); end; bufRes := waveOutOpen(@SndWaveOut, WAVE_MAPPER, @SndOutHeader, cardinal(@RecOutProc), 0, CALLBACK_FUNCTION); if bufRes <> MMSYSERR_NOERROR then begin waveOutGetErrorText(bufRes, bufErr, SizeOf(bufErr)); ShowMessage('waveOutOpen' + #13#10 + bufErr); end;
bufRes := waveOutPrepareHeader(SndWaveOut, @SndOutBufferHeaders[0], SizeOf(SndOutBufferHeaders[0])); if bufRes <> MMSYSERR_NOERROR then begin waveOutGetErrorText(bufRes, bufErr, SizeOf(bufErr)); ShowMessage('waveOutPrepareHeader' + #13#10 + bufErr); end; [/code] На индексы особо внимание не обращайте. Это "типа двойная буферизация". Точнее заготовка. Изначально все было на одном единственном буфере. Т.е. предположим что индекс всегда равен нулю. Вот функция callback: [code] procedure RecOutProc(hwi: HWAVEOUT; Msg: UINT; Inst, par1, par2: DWORD); stdcall; var bufHeader: TWaveHdr; begin if Msg <> WOM_DONE then Exit;
bufHeader := SndOutBufferHeaders[SndOutCurrentBuffer];
waveOutWrite(SndWaveOut, @bufHeader, SizeOf(bufHeader)); end; [/code] Т.е. как "сказал" драйвер, что пора... так мы сразу отдаем ему ТОТЖЕ буфер. Ничего не меняя. Вот "первый запуск": [code] FillChar(SndOutSoundBuffers[0][0], Length(SndOutSoundBuffers[0]), #128); SndOutCurrentBuffer := 0; bufRes := waveOutWrite(SndWaveOut, @SndOutBufferHeaders[SndOutCurrentBuffer], SizeOf(SndOutBufferHeaders[SndOutCurrentBuffer])); if bufRes <> MMSYSERR_NOERROR then begin waveOutGetErrorText(bufRes, bufErr, SizeOf(bufErr)); ShowMessage('waveOutWrite' + #13#10 + bufErr); end; [/code] В колонках появляется характерный писк.. Все супер. Но при этом и появились паразитические щелчки. Не могу никак понять их природу. Ведь я не трачу никакого времени на заполнение буфера или переключение... Пауз по идее нет. Я сразу отправляю тотже самый буфер "назад". Откуда щелчки? Грешил на размер буфера, но вроде как он 16000 равен, при моно звуке 8000 Гц и 16 бит. Вообщем не дайте мне умереть за клавой.. Пол инета перерыл... везде рекомендуют использовать двойную буферизацию... а вот ЗАЧЕМ ее использовать.. никто не говорит.. Нигде не описывается именно ПРИРОДА этих щелчков.
-
Ой... ну вот... с тэгами напутал.. дурья моя бошка.. Щас перепишу части кода.
const
SoundChannels = 1;
SoundSamples = 8000;
SoundBits = 16;
SoundBufferSize = SoundSamples * (SoundBits div 8);
SoundFormat = 1;
var
SndOutHeader: TWaveFormatEx;
SndWaveOut: hWaveOut;
SndOutBufferHeaders: array [0..1] of TWaveHdr;
SndOutSoundBuffers: array [0..1] of array of byte;
SndOutCurrentBuffer: integer;
......
with SndOutHeader do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := SoundChannels;
nSamplesPerSec := SoundSamples;
wBitsPerSample := SoundBits;
nBlockAlign := (nChannels * wBitsPerSample) div 8;
nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
cbSize := 0;
end;
FillChar(SndOutBufferHeaders[0], SizeOf(SndOutBufferHeaders[0]), #0);
SetLength(SndOutSoundBuffers[0], SoundSamples * (SoundBits div 8));
with SndOutBufferHeaders[0] do
begin
lpData := @SndOutSoundBuffers[0][0];
dwBufferLength := Length(SndOutSoundBuffers[0]);
end;
bufRes := waveOutOpen(@SndWaveOut, WAVE_MAPPER, @SndOutHeader, cardinal(@RecOutProc), 0, CALLBACK_FUNCTION);
if bufRes <> MMSYSERR_NOERROR then
begin
waveOutGetErrorText(bufRes, bufErr, SizeOf(bufErr));
ShowMessage('waveOutOpen' + #13#10 + bufErr);
end;
bufRes := waveOutPrepareHeader(SndWaveOut, @SndOutBufferHeaders[0], SizeOf(SndOutBufferHeaders[0]));
if bufRes <> MMSYSERR_NOERROR then
begin
waveOutGetErrorText(bufRes, bufErr, SizeOf(bufErr));
ShowMessage('waveOutPrepareHeader' + #13#10 + bufErr);
end;
......
procedure RecOutProc(hwi: HWAVEOUT; Msg: UINT; Inst, par1, par2: DWORD); stdcall;
var
bufHeader: TWaveHdr;
begin
if Msg <> WOM_DONE then
Exit;
bufHeader := SndOutBufferHeaders[SndOutCurrentBuffer];
waveOutWrite(SndWaveOut, @bufHeader, SizeOf(bufHeader));
end;
......
FillChar(SndOutSoundBuffers[0][0], Length(SndOutSoundBuffers[0]), #128);
SndOutCurrentBuffer := 0;
bufRes := waveOutWrite(SndWaveOut, @SndOutBufferHeaders[SndOutCurrentBuffer], SizeOf(SndOutBufferHeaders[SndOutCurrentBuffer]));
if bufRes <> MMSYSERR_NOERROR then
begin
waveOutGetErrorText(bufRes, bufErr, SizeOf(bufErr));
ShowMessage('waveOutWrite' + #13#10 + bufErr);
end;
-
Для начала вникни в: http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2006-09/msg00041.htmlУчитывай, что колбэк осуществляется в доп.потоке, создаваемом при вызове waveOutWrite. Это крайне важно при мультибуферизации вывода. В теле колбэка параметр lParam есть ссылка на заголовок, буфер из которого драйвер скопировал и поставил в очередь на воспроизведение. В этом заголовоке драйвер также изменил состояние ряда флагов, поэтому заголовок перед повторным использованием следует депрепарировать и вновь препарировать перед передачей в waveOutWrite
-
Премного благодарен. Вы, Сергей, как всегда выручаете. Задача решена. Вот только один вопрос остался. А почему об этом в MSDN (который с D2006) нету? Или я не там смотрел?
-
> DeadMeat (30.11.07 21:06) [3]
>почему
Понятия не имею. Все что я тебе сказал - это две минуты "пошарить в Гугле".
|