我有一个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?为什么那些阅读职位与我的通知职位不完全一致?什么是正确的算法?
我尝试了在捕获缓冲区已满时停止捕获的更简单的替代方法,没有其他通知位置。但这意味着,我认为,允许一些声音逃脱捕获。
答案 0 :(得分:0)
我的通知位置是1024的倍数。但是报告的读取位置是1500,2500和3500.所以如果我看到1500的读取位置,这是否意味着我可以从0到1500的字节读取。当下一个我看2500,这是否意味着我应该从1501读到2500?为什么那些阅读职位与我的通知职位不完全一致?什么是正确的算法?
DirectSound API现在是其他“真实”音频捕获API之上的兼容层。这意味着内部音频捕获填充了一些缓冲区(尤其是500的倍数),然后将填充的缓冲区传递给DirectSound捕获,然后DirectSound捕获报告给您。这就解释了为什么你看到读取位置是500的倍数,因为DirectSound本身就有这样的数据。
由于您对获取捕获的数据感兴趣,因此您的假设是正确的,因为您主要对读取位置感兴趣。您收到通知,并且您知道可以安全读取的偏移量。由于捕获API是分层的,因此涉及到一些延迟,因为层在需要之前需要在彼此之间传递数据块。