DirectSound 버퍼를 사용할 수있는 방법에 관한 또 다른 DirectSound 질문으로 다시 돌아가십시오.DirectSound - 네트워크의 데이터로 가득 찬 스트리밍 버퍼 재생 문제!
디코딩 된 오디오 데이터가 들어있는 약 30ms 간격으로 네트워크를 통해 들어오는 패킷이 있습니다. 응용 프로그램의 다른 부분에서 원시 wav 데이터.
Indata 이벤트가 이러한 다른 코드에 의해 트리거되면 필자는 오디오 데이터가있는 프로 시저를 매개 변수로 사용합니다.
var
FirstPart, SecondPart: Pointer;
FirstLength, SecondLength: DWORD;
AudioData: Array [0 .. 511] of Byte;
I, K: Integer;
Status: Cardinal;
begin
입력 데이터는 질문 자체와 관련이없는, 여기에 오디오 데이터로 변환되어 다음과 같이
ZeroMemory(@BufferDesc, SizeOf(DSBUFFERDESC));
wfx.wFormatTag := WAVE_FORMAT_PCM;
wfx.nChannels := 1;
wfx.nSamplesPerSec := fFrequency;
wfx.wBitsPerSample := 16;
wfx.nBlockAlign := 2; // Channels * (BitsPerSample/8)
wfx.nAvgBytesPerSec := fFrequency * 2; // SamplesPerSec * BlockAlign
BufferDesc.dwSize := SizeOf(DSBUFFERDESC);
BufferDesc.dwFlags := (DSBCAPS_GLOBALFOCUS or DSBCAPS_GETCURRENTPOSITION2 or
DSBCAPS_CTRLPOSITIONNOTIFY);
BufferDesc.dwBufferBytes := BufferSize;
BufferDesc.lpwfxFormat := @wfx;
case DSInterface.CreateSoundBuffer(BufferDesc, DSCurrentBuffer, nil) of
DS_OK:
;
DSERR_BADFORMAT:
ShowMessage('DSERR_BADFORMAT');
DSERR_INVALIDPARAM:
ShowMessage('DSERR_INVALIDPARAM');
end;
내가 내 차 버퍼에 데이터를 쓰기 다음과 같이
DSCurrentBuffer는 초기화 .
DSCurrentBuffer.GetStatus(Status);
if (Status and DSBSTATUS_PLAYING) = DSBSTATUS_PLAYING then // If it is playing, request the next segment of the buffer for writing
begin
DSCurrentBuffer.Lock(LastWrittenByte, 512, @FirstPart, @FirstLength,
@SecondPart, @SecondLength, DSBLOCK_FROMWRITECURSOR);
move(AudioData, FirstPart^, FirstLength);
LastWrittenByte := LastWrittenByte + FirstLength;
if SecondLength > 0 then
begin
move(AudioData[FirstLength], SecondPart^, SecondLength);
LastWrittenByte := SecondLength;
end;
DSCurrentBuffer.GetCurrentPosition(@PlayCursorPosition,
@WriteCursorPosition);
DSCurrentBuffer.Unlock(FirstPart, FirstLength, SecondPart, SecondLength);
end
else // If it isn't playing, set play cursor position to the start of buffer and lock the entire buffer
begin
if LastWrittenByte = 0 then
DSCurrentBuffer.SetCurrentPosition(0);
LockResult := DSCurrentBuffer.Lock(LastWrittenByte, 512, @FirstPart, @FirstLength,
@SecondPart, @SecondLength, DSBLOCK_ENTIREBUFFER);
move(AudioData, FirstPart^, 512);
LastWrittenByte := LastWrittenByte + 512;
DSCurrentBuffer.Unlock(FirstPart, 512, SecondPart, 0);
end;
위는 내 OnAudioData 이벤트에서 실행되는 코드입니다 (우리의 프로토콜로 전송 된 메시지를 디코딩 우리 고유의 구성 요소에 의해 정의했다.) 나는 오디오가있는 UDP 메시지를받을 때마다 기본적으로 코드가 실행됩니다 데이터.
버퍼에 데이터를 쓰고 난 후 충분한 데이터가 버퍼에 있으면 재생을 시작하려면 다음을 수행하십시오. BufferSize는 현재 1 초에 해당합니다.
if ((Status and DSBSTATUS_PLAYING) <> DSBSTATUS_PLAYING) and
(LastWrittenByte >= BufferSize div 2) then
DSCurrentBuffer.Play(0, 0, DSCBSTART_LOOPING);
오디오 재생이 약간 고르지 만 지금까지는 그렇게 좋았습니다. 아쉽게도이 코드로는 충분하지 않습니다.
또한 재생을 멈추고 데이터가 부족한 경우 버퍼가 다시 채워질 때까지 기다려야합니다. 여기가 내가 문제를 겪고있는 곳입니다.
기본적으로 오디오 재생이 내가 마지막으로 버퍼에 쓴 지점에 도달했을 때이를 알아낼 수 있어야합니다. 그렇지 않으면 내가받는 오디오 데이터의 지연으로 인해 오디오가 엉망이 될 것입니다. 불행히도 버퍼에 계속 쓰는 것을 멈출 수는 없습니다. 버퍼를 계속 재생하면 1 초 전의 이전 데이터가 재생됩니다 (원형과 모두가 함께있는 것입니다). 그래서 if PlayCursorPosition이 버퍼 작성 코드에서 추적하는 LastWrittenByte 값에 도달했습니다.
DirectSound Notifications는 나를 위해 할 수있는 것처럼 보였지만 데이터를 쓸 때마다 (즉, SetNotificationPositions()에 버퍼를 중지해야 함) 버퍼를 새로 중지했다가 다시 시작하면 재생 자체에 눈에 띄는 영향을 미칩니다. 따라서 재생되는 오디오는 이전보다 훨씬 더 손상되었습니다.
알림을 받기 위해 글쓰기 코드 끝에이 내용을 추가했습니다. 나는 종류의 아마 너무 잘 모든 작동하지 않을 새로운 알림을 내가 버퍼에 데이터를 기록 할 때마다 설정을 예상 ...하지만, 이봐, 나는 그것이 시도 해치지 않을 수 생각 :
if (Status and DSBStatus_PLAYING) = DSBSTATUS_PLAYING then //If it's playing, create a notification
begin
DSCurrentBuffer.QueryInterface(IID_IDirectSoundNotify, DSNotify);
NotifyDesc.dwOffset := LastWrittenByte;
NotifyDesc.hEventNotify := NotificationThread.CreateEvent(ReachedLastWrittenByte);
DSCurrentBuffer.Stop;
LockResult := DSNotify.SetNotificationPositions(1, @NotifyDesc);
DSCurrentBuffer.Play(0, 0, DSBPLAY_LOOPING);
end;
NotificationThread가있다 스레드가 WaitForSingleObject 않습니다. CreateEvent는 새 이벤트 핸들을 만들고 WaitForSingleObject가 이전 이벤트 핸들 대신 기다리기 시작하도록 만듭니다. ReachedLastWrittenByte는 내 응용 프로그램에 정의 된 프로 시저입니다. 스레드는 임계 섹션을 시작하고 알림이 트리거 될 때이를 호출합니다. 가)
ReachedLastWrittenByte ((.의 WaitForSingleObject는 내가 CreateEvent에은 물론, 호출 될 때마다 핸들을 업데이트 할 수 있도록이 20ms의 타임 아웃이라고합니다)은 다음을
내 통지가 트리거DSCurrentBuffer.Stop;
LastWrittenByte := 0;
및 내가 사용하는 보조 버퍼에서 Stop을 호출하면 오디오는 여전히 기본 버퍼의 나머지 데이터 인 것처럼 보이는 루프를 반복합니다 ...
그리고 알림이 제대로 트리거되지 않습니다. 이러한 오디오 메시지를 보내는 다른 응용 프로그램에서 오디오 데이터의 브로드 캐스트를 중지하면 버퍼의 남은 부분을 반복적으로 반복합니다. 기본적으로, 내가 설정 한 최신 알림 (lastwrittenbyte)을 무시하고지나갑니다. 재생 중에 그냥 가끔 멈추고 버퍼를 채운 다음 재생을 시작합니다 ... 단지 버퍼 된 데이터의 절반을 건너 뛰고 버퍼가 채워진 후에 들어오는 데이터를 재생하려고합니다 (그래서 버퍼를 채우지 만 새 데이터가 채워지기 전에 내용을 재생하는 데 신경 쓰지 않는 것 같습니다. 예, 잘 모르겠습니다.)
여기에 누락 된 부분이 있습니까? DirectSound Notificatiosn을 사용하여 가장 최근에 기록 된 바이트가 효과가없는 노력을 한시기를 알아내는 아이디어입니까? 스트리밍 버퍼로 이런 종류의 버퍼링을하는 방법이 있다고 생각할 것입니다.
'항상 같은 질문을하는 사람들을 찾으면 결국에는 "해결할 생각이 없다"고 대답하지 않고 떠나야합니다. "<---- +1. – Jace