2

waveOut...からのAPIを使用してオーディオを継続的に再生するアプリに取り組んでいますwinmm.dll。このアプリは「リープフロッグ」バッファーを使用します。これは基本的に、オーディオキューにダンプするサンプルの配列の集まりです。Windowsはそれらを順番にシームレスに再生し、各バッファーが完了すると、Windowsはコールバック関数を呼び出します。この関数内で、次のサンプルセットをバッファーにロードし、それらを処理してから、バッファーをオーディオキューにダンプします。このようにして、オーディオは無期限に再生されます。

アニメーションの目的で、アプリケーションに組み込んwaveOutGetPositionでいます(「バッファ完了」コールバックは不規則であるため、アニメーションがぎくしゃくします)。 waveOutGetPosition再生の現在の位置を返すため、超高精度です。

問題は、私のアプリケーションで、を呼び出すと、waveOutGetPosition最終的にアプリケーションがロックされることです。サウンドが停止し、呼び出しが戻らないということです。問題を示す簡単なアプリにまとめました。ここでアプリを実行できます:

http://www.musigenesis.com/SO/waveOut%20demo.exe

ほんの少しのピアノを何度も聞くだけなら、それは機能しています。問題を実証するためだけのものです。このプロジェクトのソースコードはここにあります(すべての肉はLeapFrogPlayer.csにあります):

http://www.musigenesis.com/SO/WaveOutDemo.zip

最初のボタンは、を呼び出さずにアプリをリープフロッグモードで実行しますwaveOutGetPosition。これをクリックすると、アプリは中断することなく永久に再生されます(Xボタンで閉じてシャットオフします)。waveOutGetPosition2番目のボタンは、リープフロガーを開始し、を呼び出して現在の位置を表示するフォームタイマーも開始します。これをクリックすると、アプリがしばらく実行されてからロックされます。私のラップトップでは、通常15〜30秒でロックされます。せいぜい1分かかります。

私はこれを修正する方法がわからないので、どんな助けや提案も大歓迎です。この問題に関する投稿はほとんど見つかりませんでしたが、複数の呼び出しから、waveOutGetPositionまたはそれへの呼び出しからwaveOutWrite、同時に発生する潜在的なデッドロックがあるようです。これを頻繁に呼び出しているため、システムで処理できない可能性があります。

編集:言及するのを忘れました、私はこれをWindowsVistaで実行しています。これは、他のOSではまったく発生しない可能性があります。

編集2:これらの(未回答の)投稿を除いて、私はこの問題についてオンラインでほとんど見つけていません:

http://social.msdn.microsoft.com/Forums/en-US/windowsgeneraldevelopmentissues/thread/c6a1e80e-4a18-47e7-af11-56a89f638ad7

編集3:さて、私は今、この問題を自由に再現することができます。waveOutGetPosition(次のコード行で)直後に呼び出すとwaveOutWrite、アプリケーションは毎回ハングします。また、特にひどい方法でハングします。アプリ自体だけでなく、OS全体がしばらくの間ロックされているようです。したがって、文字通り同時にではなく、ほぼ同時にwaveOutGetPositionデッドロックが発生した場合は、デッドロックが発生しているように見えます。これは、ロックが機能していない理由を説明している可能性があります。うん。waveOutWrite

4

3 に答える 3

3

mmsys API コード内でデッドロックが発生します。メイン スレッドが waveOutWrite() の実行でビジー状態のときに、コールバック内で waveOutGetPosition() を呼び出すとデッドロックが発生します。これは修正可能です。ロックが必要になるため、これら 2 つの関数を同時に実行することはできません。このフィールドを LeapFrogPlayer に追加します。

    private object mLocker = new object();

GetElapsedMilliseconds() で使用します。

        if (!noAPIcall)
        {
          lock (mLocker) {
            ret = WaveOutX.waveOutGetPosition(_hWaveOut, ref _timestruct,
                _timestructsize);
          }
        }

そしてHandleWaveCallback():

        // play the next buffer
        lock (mLocker) {
          int ret = WaveOutX.waveOutWrite(_hWaveOut, ref _header[_currentBuffer],
              Marshal.SizeOf(_header[_currentBuffer]));
          if (ret != WaveOutX.MMSYSERR_NOERROR) {
            throw new Exception("error writing audio");
          }
        }

これには副作用があるかもしれませんが、私は気づきませんでした。NAudio プロジェクトを見てください。

次回プロジェクトのアップロード可能な .zip を作成するときは、Build + Clean を使用してください。

于 2010-03-16T11:29:14.147 に答える
1

私は頻繁に使用NAudioしてクエリを実行しており、戦略WaveOut.GetPosition()を使用すると頻繁にデッドロックが発生します。Callbackこれは基本的にOPが抱えていた問題と同じであるため、この解決策が他の人に役立つ可能性があると思います。

ウィンドウベースの戦略を使用してみましたが(回答に記載されています)、メッセージキューに大量のメッセージがプッシュされると音声が途切れました。Callbackというわけで攻略に切り替えました。その後、デッドロックが発生し始めました。

アニメーションを同期するために 60 fps でオーディオの位置をクエリしているため、かなり定期的にデッドロックに陥っています (平均して 1 回の実行に約 20 秒かかります)。注: API を呼び出す量を減らすことができると確信していますが、それはここでの私のポイントではありません!

winmm.dll呼び出しはすべて、同じオブジェクト/ハンドルで内部的にロックされているようです。この仮定が成り立つ場合、NAudio でデッドロックが発生することはほぼ確実です。2 つのスレッドを使用するシナリオを次にA示します。 (UI スレッド); とB(のコールバック スレッドwinmm.dll) と 2 つのロックwaveOutLock(NAudio のように) とmmdll(winmm.dll が使用していると想定しているロック):

  1. A -> ロック (waveOutLock) --- 取得
  2. B -> コールバック用のロック (mmdll) --- 取得
  3. B -> ユーザー コードへのコールバック
  4. B -> ロックを試みます (waveOutLock) -- A が解放されるのを待ちます
  5. A→B待ちのため再開
  6. A -> waveOutGetPosition を呼び出します
  7. A -> ロックしようとする (mmdll) -- デッドロック

私の解決策は、コールバックで行われた作業を自分のスレッドに委任して、コールバックがすぐに戻って (仮想の)mmdllロックを解放できるようにすることでした。デッドロックがなくなったので、これは私にとっては完璧に機能しているようです。

興味のある方のために、NAudio ソースをフォークして修正し、私の変更を含めました。スレッドプールを使用しましたが、オーディオが時々少しガタガタします。これは、スレッド プールのスレッド管理が原因である可能性があるため、パフォーマンスが向上するソリューションが存在する可能性があります。

于 2013-05-09T18:46:03.300 に答える
0

これに対する解決策は非常に簡単でした (Larry Osterman のおかげです): コールバックを WndProc に置き換えます。

waveOutOpen メソッドは、デリゲート (コールバック用) またはウィンドウ ハンドルのいずれかを取ることができます。私はデリゲート アプローチを使用していましたが、これは本質的にデッドロックが発生しやすいようです (特にマネージ コードでは理にかなっています)。プレイヤー クラスにメソッドを継承さControlせてメソッドをオーバーライドさせるだけWndProcで、コールバックで行っていたのと同じことをこのメソッドで行うことができました。今、私はwaveOutGetPosition永遠に電話をかけることができ、ロックアップすることはありません.

于 2010-03-17T15:54:39.777 に答える