Правильно, я работаю над внедрением DirectSound в наше приложение Delphi voip (приложение позволяет использовать радио для нескольких пользователей по сетевому соединению)
Данные поступают через UDP-трансляции.
Как и сейчас, мы снижаем уровень необработанных данных и сами микшируем аудио из нескольких источников и получаем централизованный компонент, который используется для воспроизведения всего этого.
Само приложение является приложением Delphi 5, и мне поручено портировать его на Delphi 2010. Как только я добрался до этой части воспроизведения аудио, мы пришли к выводу, что лучше всего, если мы сможем избавиться от этого старого кода и заменить его с направленным звуком.
Итак, идея состоит в том, чтобы иметь один SecondaryBuffer для каждого радио (у нас есть одна 'панель' для каждого радио соединения, основанная на наборе компонентов, которые мы создаем для каждого конкретного радио), и просто позволить этим добавлять данные в соответствующие SecondaryBuffers всякий раз, когда они получают данные, они приостанавливаются, чтобы заполнить полусекундные аудиоданные в буфере, если в них заканчиваются данные.
Теперь, я застрял в той части, где я добавляю данные в буферы в моем тестовом приложении, где я просто пытаюсь заставить это работать должным образом, прежде чем я начну писать компонент, чтобы использовать его так, как мы хочу.
Я использую перенесенные заголовки DirectX для Delphi (http://www.clootie.ru/delphi/download_dx92.html)
Смысл этих заголовков в том, чтобы портировать через обычный интерфейс DirectSound на Delphi, так что, надеюсь, не-Delphi программисты с DirectSound могут знать, в чем причина моей проблемы.
Мой SecondaryBuffer (IDirectSoundBuffer) был создан следующим образом:
var
BufferDesc: DSBUFFERDESC;
wfx: tWAVEFORMATEX;
wfx.wFormatTag := WAVE_FORMAT_PCM;
wfx.nChannels := 1;
wfx.nSamplesPerSec := 8000;
wfx.wBitsPerSample := 16;
wfx.nBlockAlign := 2; // Channels * (BitsPerSample/2)
wfx.nAvgBytesPerSec := 8000 * 2; // SamplesPerSec * BlockAlign
BufferDesc.dwSize := SizeOf(DSBUFFERDESC);
BufferDesc.dwFlags := (DSBCAPS_GLOBALFOCUS or DSBCAPS_GETCURRENTPOSITION2 or DSBCAPS_CTRLPOSITIONNOTIFY);
BufferDesc.dwBufferBytes := wfx.nAvgBytesPerSec * 4; //Which should land at 64000
BufferDesc.lpwfxFormat := @wfx;
case DSInterface.CreateSoundBuffer(BufferDesc, DSCurrentBuffer, nil) of
DS_OK: ;
DSERR_BADFORMAT: ShowMessage('DSERR_BADFORMAT');
DSERR_INVALIDPARAM: ShowMessage('DSERR_INVALIDPARAM');
end;
Я пропустил части, в которых я определил мой PrimaryBuffer (он настроен для воспроизведения с флагом зацикливания и был создан именно так, как MSDN говорит, что это должно быть) и DSInterface, но это, как вы можете себе представить, IDirectSoundInterface.
Теперь, каждый раз, когда я получаю звуковое сообщение (обнаруженное, декодированное и преобразованное в соответствующий аудиоформат другими созданными нами компонентами, которые были подтверждены для работы более семи лет), я делаю следующее:
DSCurrentBuffer.Lock(0, 512, @FirstPart, @FirstLength, @SecondPart, @SecondLength, DSBLOCK_FROMWRITECURSOR);
Move(AudioData, FirstPart^, FirstLength);
if SecondLength > 0 then
Move(AudioData[FirstLength], SecondPart^, SecondLength);
DSCurrentBuffer.GetStatus(Status);
DSCurrentBuffer.GetCurrentPosition(@PlayCursorPosition, @WriteCursorPosition);
if (FirstPart <> nil) or (SecondPart <> nil) then
begin
Memo1.Lines.Add('FirstLength = ' + IntToStr(FirstLength));
Memo1.Lines.Add('PlayCursorPosition = ' + IntToStr(PlayCursorPosition));
Memo1.Lines.Add('WriteCursorPosition = ' + IntToStr(WriteCursorPosition));
end;
DSCurrentBuffer.Unlock(@FirstPart, FirstLength, @SecondPart, SecondLength);
AudioData содержит данные в моем сообщении. Сообщения всегда содержат 512 байт аудиоданных.
Я добавил строки Memo1.Lines.Add, чтобы иметь возможность получить отладочный вывод (поскольку использование точек останова не совсем работает, так как directsound продолжает воспроизводить содержимое основного буфера независимо)
Теперь, когда я играю в свой DSCurrentBuffer, используя флаг цикла (который в соответствии с документами MSDN достаточно для того, чтобы сделать его потоковым буфером), и этот код работает так, как он хочет, мой выводимый текст в заметке показывает, что Мне разрешено писать до конца буфера ... Но он не переносится.
SecondPart всегда равен нулю. Он никогда не переносится в начало буфера, что означает, что я получаю одни и те же несколько секунд воспроизведения аудиоданных снова и снова.
И да, я искал сеть для компонентов, которые могут сделать это для нас, и пришел к выводу, что единственный надежный способ - это сделать это самим.
И да, аудиоданные, воспроизводимые этим приложением, нестабильны. Я задерживаюсь на написании полусекундного буферизирующего кода, пока не получу код записи в буфер для переноса, как следует: /
Я читал, что люди предлагают отслеживать ваш собственный курсор записи, но из того, что я прочитал, Lock and Unlock должен помочь мне обойти эту потребность.
Я также предпочел бы избежать необходимости иметь два буфера, которые я чередую между назад и вперед (или разделенный буфер, который по сути был бы тем же самым, только немного более сложным в записи)
Любая помощь с благодарностью!