我正在使用带有回调函数的waveOutWrite,并且在本机代码下一切都很快。在 .NET 下它要慢得多,以至于我认为我做错了什么,有时慢 5 到 10 倍。
我可以发布两组代码,但似乎太多了,所以我只发布速度很快的 C 代码并指出 .NET 代码中的微小差异。
HANDLE WaveEvent;
const int TestCount = 100;
HWAVEOUT hWaveOut[1]; // don't ask why this is an array, just test code
WAVEHDR woh[1][20];
void CALLBACK OnWaveOut(HWAVEOUT,UINT uMsg,DWORD,DWORD,DWORD)
{
if(uMsg != WOM_DONE)
return;
assert(SetEvent(WaveEvent)); // .NET code uses EventWaitHandle.Set()
}
void test(void)
{
WaveEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
assert(WaveEvent);
WAVEFORMATEX wf;
memset(&wf,0,sizeof(wf));
wf.wFormatTag = WAVE_FORMAT_PCM;
wf.nChannels = 1;
wf.nSamplesPerSec = 8000;
wf.wBitsPerSample = 16;
wf.nBlockAlign = WORD(wf.nChannels*(wf.wBitsPerSample/8));
wf.nAvgBytesPerSec = (wf.wBitsPerSample/8)*wf.nSamplesPerSec;
assert(waveOutOpen(&hWaveOut[0],WAVE_MAPPER,&wf,(DWORD)OnWaveOut,0,CALLBACK_FUNCTION) == MMSYSERR_NOERROR);
for(int x=0;x<2;x++)
{
memset(&woh[0][x],0,sizeof(woh[0][x]));
woh[0][x].dwBufferLength = PCM_BUF_LEN;
woh[0][x].lpData = (char*) malloc(woh[0][x].dwBufferLength);
assert(waveOutPrepareHeader(hWaveOut[0],&woh[0][x],sizeof(woh[0][x])) == MMSYSERR_NOERROR);
assert(waveOutWrite(hWaveOut[0],&woh[0][x],sizeof(woh[0][x])) == MMSYSERR_NOERROR);
}
int bufferIndex = 0;
DWORD times[TestCount];
for(int x=0;x<TestCount;x++)
{
DWORD t = timeGetTime();
assert(WaitForSingleObject(WaveEvent,INFINITE) == WAIT_OBJECT_0); // .NET code uses EventWaitHandle.WaitOne()
assert(woh[0][bufferIndex].dwFlags & WHDR_DONE);
assert(waveOutWrite(hWaveOut[0],&woh[0][bufferIndex],sizeof(woh[0][bufferIndex])) == MMSYSERR_NOERROR);
bufferIndex = bufferIndex == 0 ? 1 : 0;
times[x] = timeGetTime() - t;
}
}
C 代码的 times[] 数组的值总是在 80 左右,这是我正在使用的 PCM 缓冲区长度。.NET 代码有时也会显示类似的值,但有时会显示高达 1000 的值,更常见的是 300 到 500 范围内的值。
在 OnWaveOut 回调中执行底部循环中的部分而不是使用事件,使用 .NET 或本机代码使其始终保持快速。因此,问题似乎仅与 .NET 中的等待事件有关,并且主要仅在测试 PC 上发生“其他事情”时——但不是很多事情,可以像移动窗口或打开一样简单我电脑里的一个文件夹。
也许 .NET 事件对于上下文切换或 .NET 应用程序/线程一般来说真的很糟糕?在我用来测试我的 .NET 代码的应用程序中,代码只是在表单的构造函数中运行(添加测试代码的容易的地方),而不是在线程池线程或任何东西上。
我还尝试使用接收事件而不是函数回调的 waveOutOpen 版本。这在 .NET 中也很慢,但在 C 中则不然,因此,它再次指出了事件和/或上下文切换的问题。
我试图让我的代码保持简单,并设置一个事件来完成回调之外的工作是我可以用我的整体设计做到这一点的最佳方式。实际上只使用事件驱动的 waveOut 更好,但我尝试了另一种方法,因为直接回调很快,而且我没想到正常的事件等待句柄会这么慢。