如何避免DirectSound中的失真和卡顿?

时间:2015-01-20 06:54:43

标签: windows audio directsound

我有一个DirectSound应用程序,我用C语言编写,在Windows 7上运行。该应用程序只捕获一些声音帧,并播放它们。为了理智地检查捕获结果,我将PCM数据写入文件,我可以使用aplay在Linux中播放。

不幸的是,声音不稳定,有时包含口吃(并且在Linux中以错误的速度播放)。奇怪的是,如果PCM数据在捕获时没有在播放缓冲区中播放,则播放捕获文件时观察到的失真量会减少。

这是我的WAVEFORMATEX的初始化:

memset(&wfx, 0, sizeof(WAVEFORMATEX)); 
wfx.cbSize = 0;
wfx.wFormatTag = WAVE_FORMAT_PCM; 
wfx.nChannels = 1; 
wfx.nSamplesPerSec = sampleRate; 
wfx.wBitsPerSample = sampleBitWidth;
wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8; 
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign code here

sampleRate为8000,sampleBitWidth为16。

我使用相同的结构创建捕获和播放缓冲区,捕获缓冲区有3个通知位置。我开始捕捉:

lpDsCaptureBuffer->Start(DSCBSTART_LOOPING);

然后,我启动了一个回放线程,该线程在与通知点关联的事件上调用WaitForMultipleObjects。收到通知后,我重置所有事件,并将1或2个捕获缓冲区复制到本地缓冲区,并将其传递给播放例程:

void playFromBuff(LPVOID captureBuff,DWORD captureLen) {
  LPVOID playBuff;
  DWORD playLen;
  HRESULT hr;

  hr = lpDsPlaybackBuffer->Lock(0L,captureLen,&playBuff,&playLen,NULL,0L,0L);

  memcpy(playBuff,captureBuff,playLen);
  hr = lpDsPlaybackBuffer->Unlock(playBuff,playLen,NULL,0L);
  hr = lpDsPlaybackBuffer->SetCurrentPosition(0L);
  hr = lpDsPlaybackBuffer->Play(0L,0L,0L);
}

(省略了一些错误检查)。

请注意,播放缓冲区没有通知位置。每次从捕获缓冲区中获取一个块时,我都会从位置0开始锁定播放缓冲区。

由WaitForMultipleObjects保护的捕获代码如下所示:

    lpDsCaptureBuffer->GetCurrentPosition(NULL,&readPos);

    hr = lpDsCaptureBuffer->Lock(...,...,&captureBuff1,&captureLen1,&captureBuff2,&captureLen2,0L);

其中省略号包含涉及当前和先前看到的读取位置的计算。我省略了那些可能错误的计算 - 我怀疑这就是问题所在。

我的通知位置是1024的倍数。但是报告的读取位置是1500,2500和3500.所以如果我看到1500的读取位置,这是否意味着我可以从0到1500的字节读取。当下一个我看2500,这是否意味着我应该从1501读到2500?为什么那些阅读职位与我的通知职位不完全一致?什么是正确的算法?

我尝试了在捕获缓冲区已满时停止捕获的更简单的替代方法,没有其他通知位置。但这意味着,我认为,允许一些声音逃脱捕获。

1 个答案:

答案 0 :(得分:0)

  

我的通知位置是1024的倍数。但是报告的读取位置是1500,2500和3500.所以如果我看到1500的读取位置,这是否意味着我可以从0到1500的字节读取。当下一个我看2500,这是否意味着我应该从1501读到2500?为什么那些阅读职位与我的通知职位不完全一致?什么是正确的算法?

DirectSound API现在是其他“真实”音频捕获API之上的兼容层。这意味着内部音频捕获填充了一些缓冲区(尤其是500的倍数),然后将填充的缓冲区传递给DirectSound捕获,然后DirectSound捕获报告给您。这就解释了为什么你看到读取位置是500的倍数,因为DirectSound本身就有这样的数据。

由于您对获取捕获的数据感兴趣,因此您的假设是正确的,因为您主要对读取位置感兴趣。您收到通知,并且您知道可以安全读取的偏移量。由于捕获API是分层的,因此涉及到一些延迟,因为层在需要之前需要在彼此之间传递数据块。