Frage

Ich bin mit System.Speech.Synthesis.SpeechSynthesizer convert Text zu Sprache. Und aufgrund Microsofts anämisch Dokumentation (siehe meinen Link, gibt es keine Bemerkungen oder Codebeispiele) Ich Mühe machen Kopf oder Zahl der Differenz zwischen zwei Methoden habe:

SetOutputToAudioStream und SetOutputToWaveStream.

Hier ist, was ich abgeleitet haben:

SetOutputToAudioStream nimmt einen Strom und eine SpeechAudioFormatInfo Instanz, definieren das Format der Wellendatei (Abtastungen pro Sekunde, Bits pro Sekunde, Audiokanäle, etc.) und den Text an den Stream schreiben.

SetOutputToWaveStream dauert nur einen Stream und schreibt eine 16-Bit, Mono, 22 kHz, PCM-Wave-Datei in den Stream. Es gibt keine Möglichkeit in SpeechAudioFormatInfo passieren.

Mein Problem ist SetOutputToAudioStream keine gültige Wave-Datei in den Stream schreiben. Zum Beispiel bekomme ich eine InvalidOperationException ( „The Wave-Header ist beschädigt“), wenn der Strom zu System.Media.SoundPlayer vorbei. Wenn ich den Strom auf die Festplatte schreiben und versuchen, es mit WMP ich ein „Windows Media Player kann die Datei abspielen kann nicht ...“ bekommen zu spielen Fehler, aber die von SetOutputToWaveStream geschrieben Stream wiedergegeben werden richtig in beiden. Meine Theorie ist, dass SetOutputToAudioStream ist keine (gültige) Header zu schreiben.

Seltsamer die Namenskonventionen für die SetOutputTo * Blah * ist inkonsistent. SetOutputToWaveFile nimmt eine SpeechAudioFormatInfo während SetOutputToWaveStream nicht.

Ich brauche einen 8 kHz schreiben zu können, 16-bit, Datei in einen Stream Mono-Welle, etwas, dass weder SetOutputToAudioStream oder SetOutputToWaveStream erlauben Sie mir zu tun. Hat jemand haben Einblick in Speechsynthesizer und diese beiden Methoden?

Als Referenz hier einige Code:

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);
}

Lösung:

Vielen Dank an @Hans Passanten, hier ist der Kern das, was ich bin jetzt mit:

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;

Für meine grobe Prüfung es funktioniert super, wenn auch mithilfe von Reflektion ist ein bisschen eklig, es ist besser als die Datei auf der Festplatte zu schreiben und einen Strom zu öffnen.

War es hilfreich?

Lösung

Ihr Code-Snippet borked ist, die Sie verwenden Synth , nachdem er angeordnet ist. Aber das ist nicht das eigentliche Problem, das ich sicher bin. SetOutputToAudioStream erzeugt das rohe PCM-Audio, die ‚Zahlen‘. Ohne ein Container-Dateiformat (Header) wie das, was in einer .wav-Datei verwendet wird. Ja, das kann nicht wieder mit einem normalen Medienprogramm abgespielt werden.

Die fehlende Überlast für SetOutputToWaveStream, die eine SpeechAudioFormatInfo nimmt, ist seltsam. Es ist wirklich aussehen wie ein Versehen zu mir, auch wenn das in dem .NET-Framework extrem selten ist. Es gibt keinen zwingenden Grund, warum es nicht funktionieren sollte, die zugrunde liegende SAPI-Schnittstelle es nicht unterstützt. Es kann mit Reflexion gehackt um werden die private SetOutputStream Methode aufzurufen. Das funktioniert gut, wenn ich es getestet, aber ich kann es nicht bürgen:

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);
                    }
                }
            }

Wenn Sie sich unwohl mit dem Hack dann Path.GetTempFileName () verwenden, um es vorübergehend zu einer Datei streamen wird sicherlich zu arbeiten.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top