私はコールバック関数で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を使用する方がさらに優れていますが、ストレートコールバックが高速であり、通常のイベント待機ハンドルがそれほど遅くなるとは思わなかったため、この別の方法を試しました。