8

System.Speech.Synthesis.SpeechSynthesizerを使用してテキストを音声に変換しています。また、Microsoft の貧血に関するドキュメント (私のリンクを参照してください。コメントやコード例はありません) のために、2 つの方法の違いを理解するのに苦労しています。

SetOutputToAudioStream と SetOutputToWaveStream。

これが私が推測したものです:

SetOutputToAudioStream は、ストリームと、wave ファイルの形式 (1 秒あたりのサンプル数、1 秒あたりのビット数、オーディオ チャネルなど) を定義する SpeechAudioFormatInfo インスタンスを受け取り、ストリームにテキストを書き込みます。

SetOutputToWaveStream は、ストリームだけを受け取り、16 ビット、モノラル、22kHz、PCM ウェーブ ファイルをストリームに書き込みます。SpeechAudioFormatInfo を渡す方法はありません。

私の問題は、 SetOutputToAudioStream が有効なウェーブ ファイルをストリームに書き込めないことです。たとえば、ストリームを System.Media.SoundPlayer に渡すと、InvalidOperationException (「ウェーブ ヘッダーが壊れています」) が発生します。ストリームをディスクに書き込んで WMP で再生しようとすると、「Windows Media Player はファイルを再生できません...」というエラーが表示されますが、SetOutputToWaveStream によって書き込まれたストリームは両方で正しく再生されます。私の理論は、SetOutputToAudioStream が (有効な) ヘッダーを書き込んでいないというものです。

奇妙なことに、SetOutputTo*Blah* の命名規則には一貫性がありません。SetOutputToWaveFile は SpeechAudioFormatInfo を受け取りますが、SetOutputToWaveStream は受け取りません。

SetOutputToAudioStream や SetOutputToWaveStream ではできないことですが、8kHz、16 ビット、モノラル ウェーブ ファイルをストリームに書き込めるようにする必要があります。SpeechSynthesizer とこれら 2 つの方法について洞察を持っている人はいますか?

参考までに、いくつかのコードを次に示します。

Stream ret = new MemoryStream();
using (SpeechSynthesizer synth = new SpeechSynthesizer())
{
  synth.SelectVoice(voiceName);
  synth.SetOutputToWaveStream(ret);
  //synth.SetOutputToAudioStream(ret, new SpeechAudioFormatInfo(8000, AudioBitsPerSample.Sixteen, AudioChannel.Mono));
  synth.Speak(textToSpeak);
}

解決:

@Hans Passantに感謝します。これが私が現在使用しているものの要点です:

Stream ret = new MemoryStream();
using (SpeechSynthesizer synth = new SpeechSynthesizer())
{
  var mi = synth.GetType().GetMethod("SetOutputStream", BindingFlags.Instance | BindingFlags.NonPublic);
  var fmt = new SpeechAudioFormatInfo(8000, AudioBitsPerSample.Sixteen, AudioChannel.Mono);
  mi.Invoke(synth, new object[] { ret, fmt, true, true });
  synth.SelectVoice(voiceName);
  synth.Speak(textToSpeak);
}
return ret;

私の大まかなテストでは、リフレクションを使用するのは少し厄介ですが、ファイルをディスクに書き込んでストリームを開くよりはましですが、うまく機能します。

4

1 に答える 1

8

あなたのコード スニペットは壊れています。破棄された後にシンセを使用しています。しかし、それは私が確信している本当の問題ではありません。SetOutputToAudioStream は生の PCM オーディオである「数値」を生成します。.wav ファイルで使用されているようなコンテナー ファイル形式 (ヘッダー) がない場合。はい、通常のメディア プログラムでは再生できません。

SpeechAudioFormatInfo を受け取る SetOutputToWaveStream の欠落しているオーバーロードは奇妙です。.NET フレームワークでは非常にまれですが、私には見落としのように見えます。それが機能しないという説得力のある理由はありません。基礎となる SAPI インターフェイスはそれをサポートしています。プライベートな SetOutputStream メソッドを呼び出すために、リフレクションを使ってハッキングすることができます。私がテストしたとき、これはうまくいきましたが、私はそれを保証することはできません:

using System.Reflection;
...
            using (Stream ret = new MemoryStream())
            using (SpeechSynthesizer synth = new SpeechSynthesizer()) {
                var mi = synth.GetType().GetMethod("SetOutputStream", BindingFlags.Instance | BindingFlags.NonPublic);
                var fmt = new SpeechAudioFormatInfo(8000, AudioBitsPerSample.Eight, AudioChannel.Mono);
                mi.Invoke(synth, new object[] { ret, fmt, true, true });
                synth.Speak("Greetings from stack overflow");
                // Testing code:
                using (var fs = new FileStream(@"c:\temp\test.wav", FileMode.Create, FileAccess.Write, FileShare.None)) {
                    ret.Position = 0;
                    byte[] buffer = new byte[4096];
                    for (;;) {
                        int len = ret.Read(buffer, 0, buffer.Length);
                        if (len == 0) break;
                        fs.Write(buffer, 0, len);
                    }
                }
            }

ハッキングに不安がある場合は、 Path.GetTempFileName() を使用して一時的にファイルにストリーミングすることで確実に機能します。

于 2010-10-06T19:32:16.940 に答える