Конференция "Media" » Щелчки при воспроизведении звука [D7, WinXP]
 
  • DeadMeat © (27.11.07 17:05) [0]
    Здравсте.
    Я тут уже с ума схожу.
    Пытаюсь вопроизвести звук из *.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 бит.
    Вообщем не дайте мне умереть за клавой.. Пол инета перерыл... везде рекомендуют использовать двойную буферизацию... а вот ЗАЧЕМ ее использовать.. никто не говорит.. Нигде не описывается именно ПРИРОДА этих щелчков.
  • DeadMeat © (27.11.07 17:07) [1]
    Ой... ну вот... с тэгами напутал.. дурья моя бошка.. Щас перепишу части кода.

    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;

  • Сергей М. © (28.11.07 10:17) [2]
    Для начала вникни в:
    http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.mmedia/2006-09/msg00041.html

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

    В теле колбэка параметр lParam есть ссылка на заголовок, буфер из которого драйвер скопировал и поставил в очередь на воспроизведение. В этом заголовоке драйвер также изменил состояние ряда флагов, поэтому заголовок перед повторным использованием следует депрепарировать и вновь препарировать перед передачей в waveOutWrite
  • DeadMeat (30.11.07 21:06) [3]
    Премного благодарен. Вы, Сергей, как всегда выручаете. Задача решена. Вот только один вопрос остался. А почему об этом в MSDN (который с D2006) нету? Или я не там смотрел?
  • Сергей М. © (01.12.07 11:38) [4]

    > DeadMeat   (30.11.07 21:06) [3]


    >почему


    Понятия не имею.
    Все что я тебе сказал - это две минуты "пошарить в Гугле".
 
Конференция "Media" » Щелчки при воспроизведении звука [D7, WinXP]
Есть новые Нет новых   [134431   +10][b:0][p:0.002]