Directsound带流式缓冲区-锁不换行!在Delphi中使用移植的DirectX头文件

Directsound带流式缓冲区-锁不换行!在Delphi中使用移植的DirectX头文件,delphi,directx,directsound,Delphi,Directx,Directsound,没错,我正在Delphi voip应用程序中实现DirectSound(该应用程序允许多个用户通过网络连接使用无线电) 数据通过UDP广播进入。 就像现在一样,我们在原始数据级别上,自己做来自多个源的音频混合,并有一个用于播放所有这些内容的集中组件 该应用程序本身是Delphi5应用程序,我的任务是将其移植到Delphi2010。当我谈到这个音频播放部分时,我们得出结论,如果我们能摆脱这个旧代码,用directsound取代它,那将是最好的 因此,我们的想法是每个无线电都有一个二级缓冲区(每个无

没错,我正在Delphi voip应用程序中实现DirectSound(该应用程序允许多个用户通过网络连接使用无线电) 数据通过UDP广播进入。 就像现在一样,我们在原始数据级别上,自己做来自多个源的音频混合,并有一个用于播放所有这些内容的集中组件

该应用程序本身是Delphi5应用程序,我的任务是将其移植到Delphi2010。当我谈到这个音频播放部分时,我们得出结论,如果我们能摆脱这个旧代码,用directsound取代它,那将是最好的

因此,我们的想法是每个无线电都有一个二级缓冲区(每个无线电连接都有一个“面板”,基于我们为每个特定无线电创建的一组组件),只要让这些设备在获得数据时将数据添加到各自的二级缓冲区,只有当缓冲区中的数据用完时,才会暂停以填充半秒钟的音频数据

现在,我被困在测试应用程序中向缓冲区添加数据的部分,我只是想在开始编写一个组件以我们想要的方式利用它之前,让它正常工作

我正在为Delphi()使用移植的DirectX头文件

这些头文件的目的是通过常规的DirectSound接口向Delphi进行移植,因此希望使用DirectSound的非Delphi程序员也知道我的问题的原因

我的第二个缓冲区(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);
DSCurrentBuffer.Lock(0,512,@FirstPart,@FirstLength,@SecondPart,@SecondLength,DSBLOCK_fromWriteCursors);
移动(音频数据,第一部分^,第一长度);
如果SecondLength>0,则
移动(音频数据[FirstLength],SecondPart^,SecondLength);
DSCurrentBuffer.GetStatus(状态);
GetCurrentPosition(@PlayCursorPosition、@WriteCursorPosition);
如果(第一部分为零)或(第二部分为零),则
开始
Memo1.Lines.Add('FirstLength='+IntToStr(FirstLength));
备注1.Lines.Add('PlayCursorPosition='+IntToStr(PlayCursorPosition));
Memo1.Lines.Add('WriteCursorPosition='+IntToStr(WriteCursorPosition));
结束;
DSCurrentBuffer.Unlock(@FirstPart,FirstLength,@SecondPart,SecondLength);
AudioData包含我的消息中的数据。消息始终包含512字节的音频数据。 我添加了Memo1.Lines.Add行,以便能够获得一些调试输出(因为使用断点并不十分有效,因为directsound始终播放主缓冲区的内容)

现在,当我使用循环标志播放我的DSCurrentBuffer(根据hte MSDN文档,循环标志足以使其成为流式缓冲区)并使此代码按需要运行时,我在备忘录中的输出文本显示,我被允许在缓冲区结束前进行写操作。。。但它没有包装

第二部分总是零。它从来不会绕到缓冲区的开头,这意味着我会一遍又一遍地播放同样的几秒钟音频数据

是的,我已经搜索了网络上可以为我们做这些事情的组件,并得出结论,唯一可靠的方法就是自己这样做

是的,这个应用程序播放的音频数据是不稳定的。我一直在等待半秒缓冲代码的编写,直到我能够将write to buffer代码按应有的方式包装:/

我一直在读人们建议跟踪你自己的写光标,但从我读到的锁和解锁应该可以帮助我绕过这种需要。 我还希望避免使用两个前后交替的缓冲区(或者一个拆分缓冲区,本质上是一样的,只是在编写时稍微复杂一点)


非常感谢您的帮助

一些可能导致这种情况的因素:

  • 只能从主线程(初始化VCL GUI的线程)调用Memo1.Lines.Add。用于此目的(更简单),或者使用线程安全的中间缓冲区,最好使用(更快;感谢此提示)

  • 解锁应该在下面这样的部分中,因为如果引发异常,则永远不会解锁缓冲区,请参见下面的代码示例

  • 您应该记录发生的任何异常

  • 示例代码:

      DSCurrentBuffer.Lock(0, 512, @FirstPart, @FirstLength, @SecondPart, @SecondLength, DSBLOCK_FROMWRITECURSOR);
      try
        //...
      finally
        DSCurrentBuffer.Unlock(@FirstPart, FirstLength, @SecondPart, SecondLength);
      end;
    

    --杰伦

    所以我解决了问题^^

    也很简单

    DSCurrentBuffer.Unlock(@FirstPart, FirstLength, @SecondPart, SecondLength);
    
    我想我应该只传递Lock()所需的相同指针

    改成

    DSCurrentBuffer.Unlock(FirstPart, FirstLength, SecondPart, SecondLength);
    
    解决了该问题,缓冲区现在已正确包装


    很抱歉浪费了你的时间,不过还是要谢谢你^^

    你看过lakeofsoft组件了吗?我以前在互联网音频应用程序中使用过它们,它附带了源代码。我喜欢他们。对我们来说,这只是一个副业(网络应用上的音频)是的,我做了。不过,我们不需要数据流的帮助——我们已经这样做了。我们需要的是DirectSound的自动混音功能。每一个独立线程都可以随心所欲地运行,而不必关注应用程序的任何其他部分。DirectSound似乎在它的某个地方也有某种形式的回声消除,这将是一个巨大的好处。你有没有尝试过低音音频?它的延迟很低,速度很快,并且有合适的Delphi包装器可用。我和BASS在.NET中做了一些非常漂亮的事情…谢谢你的建议。不幸的是,考虑到我们需要的