正確なタイミングで音と沈黙を作り出すにはどうすればいいでしょうか? 質問する

正確なタイミングで音と沈黙を作り出すにはどうすればいいでしょうか? 質問する

RSS フィードのモールス信号を再生する C# プロジェクトがあります。Managed DirectX を使用して作成しましたが、Managed DirectX は古くて非推奨であることがわかりました。私のタスクは、長さが正確に計られた無音期間 (コード) を散りばめた純粋な正弦波バーストを再生することです。純粋なトーンを数ミリ秒再生する関数を呼び出し、次に Thread.Sleep() を実行して別のトーンを再生する、といった具合にする必要があります。最速の場合、トーンとスペースは 40 ミリ秒ほど短くなります。

Managed DirectX では、非常にうまく機能しています。正確なタイミングのトーンを取得するには、1 秒の正弦波をセカンダリ バッファーに作成し、特定の持続時間のトーンを再生するには、バッファーの末尾から x ミリ秒以内まで前方にシークしてから再生します。

System.Media.SoundPlayerを試してみた。ダメだった[編集 - 下記の私の回答を参照してください]任意の音の長さにするには、Play()、Sleep()、Stop() を実行する必要があるためです。その結果、CPU 負荷によって変化する長すぎる音が生成されます。実際に音を停止するまでにかかる時間は不確定です。

私はその後、長い使用しようとするNオーディオ1.3最終的には、メモリ常駐ストリームでトーン データを提供し、再度前方にシークして、必要な長さのトーンをストリーム内に残してから再生するようになりました。これは、DirectSoundOut クラスではしばらくは問題なく機能しましたが (下記参照)、WaveOut クラスは、PlayerStopped = true であるにもかかわらず、バッファーがキューに残っているという内部アサートですぐに停止します。最後まで再生してから、トーンの終了と次の開始の間に同じ期間の待機を置くので、これは奇妙です。40 ミリ秒のトーンの再生を開始してから 80 ミリ秒後には、キューにバッファーがないと思われます。

DirectSoundOut はしばらくはうまく動作しますが、問題は、トーン バースト Play() ごとに別のスレッドがスピンオフすることです。最終的に (5 分程度)、動作が停止します。VS2008 IDE でプロジェクトを実行している間、出力ウィンドウでスレッドが次々と終了するのを確認できます。再生中に新しいオブジェクトを作成せず、トーン ストリームを Seek() してから Play() を何度も呼び出すだけなので、孤立したバッファーなどが詰まるまで積み重なる問題ではないと思います。

この件についてはもう我慢の限界なので、ここで同様の要件に直面した人がいて、適切な解決策を示してくれることを期待して質問します。

ベストアンサー1

信じられません... System.Media.SoundPlayer に戻って、まさにやりたいことをやりました... 95% の未使用コードや、発見されるのを待っている奇妙な点がある巨大な依存ライブラリはありません :-)。 さらに、Mono (2.6) の MacOSX でも動作します!!! [間違いです - 音声がありません。別の質問をします]

MemoryStream と BinaryWriter を使用して、RIFF ヘッダーとチャンクを備えた WAV ファイルを作成しました。「ファクト」チャンクは必要ありません。これは 44100Hz の 16 ビット サンプルです。これで、1000 ミリ秒のサンプルが含まれ、BinaryReader でラップされた MemoryStream ができました。

RIFF ファイルには、ストリームの 4 バイト目 (ASCII の「RIFF」の直後) にある「全体」の長さと、サンプル データ バイトの直前にある「データ」の長さの 2 つの 4 バイト/32 ビットの長さがあります。私の戦略は、ストリーム内を検索し、BinaryWriter を使用して 2 つの長さを変更して、SoundPlayer にオーディオ ストリームが希望の長さ/継続時間であると思わせ、それを Play() することでした。次回は継続時間が異なるため、もう一度 BinaryWriter を使用して MemoryStream 内の長さを上書きし、それを Flush() して、もう一度 Play() を呼び出します。

これを試したところ、Stream プロパティを設定しても、SoundPlayer にストリームの変更を認識させることができませんでした。40 ミリ秒ごとに新しい SoundPlayer を作成する必要がありました... いいえ。

さて、今日はそのコードに戻りたいので、SoundPlayer メンバーを調べ始めました。「SoundLocation」を見つけて読みました。そこには、SoundLocation を設定すると Stream プロパティが null になり、Stream の場合はその逆になると書かれていました。そこで、SOundLocation プロパティを偽の「x」に設定するコード行を追加し、Stream プロパティを (変更したばかりの) MemoryStream に設定しました。なんと、それが認識され、要求したとおりの長さのトーンが正確に再生されました。その後のデッド タイムやメモリの増加などのおかしな副作用はないようです。WAV ストリームの調整を行ってからプレーヤーをロード/起動するには 1 ~ 2 ミリ秒かかりますが、これは非常に短く、価格も適切です。

また、サンプルを再生成し、Seek/BinaryWriter トリックを使用して、RIFF/WAV MemoryStream 内の古いデータを同じ数のサンプルで異なる周波数でオーバーレイする Frequency プロパティも実装し、Amplitude プロパティに対しても同じことを行いました。

このプロジェクトはSourceForgeにありますこのハックのC#コードはSPTones.CSから入手できます。このページをSVNブラウザで表示する。私の考えに近い考えを持っていた @arke さんを含め、この件について情報を提供してくれた皆さんに感謝します。本当に感謝しています。

おすすめ記事