【发布时间】:2015-01-20 06:54:43
【问题描述】:
我有一个使用 C 语言编写的 DirectSound 应用程序,在 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?为什么这些阅读位置与我的通知位置不完全对应?这里的正确算法是什么?
我尝试了一种更简单的替代方法,即在捕获缓冲区已满时停止捕获,而无需其他通知位置。但这意味着,我认为,允许一些声音逃脱捕获。
【问题讨论】:
标签: windows audio directsound