Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Щелчки при воспроизведении звука [D7, WinXP]


DeadMeat ©   (2007-11-27 17:05:00) 

Здравсте.
Я тут уже с ума схожу.
Пытаюсь вопроизвести звук из *.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 бит.
Вообщем не дайте мне умереть за клавой.. Пол инета перерыл... везде рекомендуют использовать двойную буферизацию... а вот ЗАЧЕМ ее использовать.. никто не говорит.. Нигде не описывается именно ПРИРОДА этих щелчков.


Сергей М.   (2007-12-01 11:38:00) [1]


> DeadMeat   (30.11.07 21:06) [3]


>почему


Понятия не имею.
Все что я тебе сказал - это две минуты "пошарить в Гугле".


DeadMeat   (2007-11-30 21:06:00) [2]

Премного благодарен. Вы, Сергей, как всегда выручаете. Задача решена. Вот только один вопрос остался. А почему об этом в MSDN (который с D2006) нету? Или я не там смотрел?


Сергей М.   (2007-11-28 10:17:00) [3]

Для начала вникни в:
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2006-09/msg00041.html

Учитывай, что колбэк осуществляется в доп.потоке, создаваемом при вызове waveOutWrite. Это крайневажно при мультибуферизации вывода.

В теле колбэка параметр lParam есть ссылка на заголовок, буфер из которого драйвер скопировал и поставил в очередь на воспроизведение. В этом заголовоке драйвер также изменил состояние ряда флагов, поэтому заголовок перед повторным использованием следует депрепарировать и вновь препарировать перед передачей в waveOutWrite


DeadMeat   (2007-11-27 17:07:00) [4]

Ой... ну вот... с тэгами напутал.. дурья моя бошка.. Щас перепишу части кода.

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;


Наверх

Титульная страница Поиск, карта сайта Написать письмо