Domanda

System.Speech.Synthesis.SpeechSynthesizer per convertire text to speech. E a causa di documentazione anemico di Microsoft (vedi il mio link, non ci sono osservazioni o esempi di codice) Sto avendo teste creare problemi o code della differenza tra due metodi:

SetOutputToAudioStream e SetOutputToWaveStream.

Ecco quello che ho dedotto:

SetOutputToAudioStream prende un flusso e un'istanza SpeechAudioFormatInfo che definisce il formato del file wave (campioni al secondo, bit per secondo, canali audio, etc.) e scrive il testo al flusso.

SetOutputToWaveStream prende solo un flusso e scrive una, 22 kHz, file a 16 bit, mono onda PCM al flusso. Non c'è modo di passare in SpeechAudioFormatInfo.

Il mio problema è SetOutputToAudioStream non scrive un file wave valida al flusso. Per esempio ho un InvalidOperationException ( "L'intestazione onda è danneggiato") quando passa il flusso di System.Media.SoundPlayer. Se scrivo il flusso su disco e tentare di giocare con WMP ottengo un "Windows Media Player non può riprodurre il file ..." l'errore, ma il flusso scritto da SetOutputToWaveStream gioca correttamente in entrambi. La mia teoria è che SetOutputToAudioStream non sta scrivendo un colpo di testa (valido).

Stranamente le convenzioni di denominazione per la SetOutputTo * * Blah è incoerente. SetOutputToWaveFile prende un po 'SpeechAudioFormatInfo SetOutputToWaveStream non lo fa.

ho bisogno di essere in grado di scrivere un 8 kHz, 16 bit, mono onda di file a un ruscello, qualcosa che né SetOutputToAudioStream o SetOutputToWaveStream mi permettono di fare. Qualcuno ha spaccato SpeechSynthesizer e questi due metodi?

Per riferimento, ecco qualche codice:

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

Soluzione:

Molte grazie a @Hans Passant, qui è l'essenza di quello che sto usando ora:

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;

Per il mio test di massima funziona benissimo, anche se utilizzando la riflessione è un po 'icky è meglio che scrivere il file su disco e l'apertura di un corso d'acqua.

È stato utile?

Soluzione

Il frammento di codice è borked, si sta utilizzando synth dopo che è disposto. Ma non è questo il vero problema sono sicuro. SetOutputToAudioStream produce l'audio PCM prima, i 'numeri'. Senza un formato di file contenitore (header), come quello che è utilizzato in un file wav. Sì, che non possono essere riprodotti con un programma multimediale regolare.

Il sovraccarico mancante per SetOutputToWaveStream che prende una SpeechAudioFormatInfo è strano. E 'davvero apparire come una svista per me, anche se questo è estremamente raro nel framework .NET. Non c'è alcun motivo valido per cui non dovrebbe lavoro, l'interfaccia SAPI sottostante non sostenerlo. Si può essere violato in giro con la riflessione per chiamare il metodo SetOutputStream privato. Questo raffinato funzionato quando ho provato ma non posso garantire per esso:

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

Se sei a disagio con l'hack quindi utilizzando Path.GetTempFileName () per lo streaming temporaneamente in un file certamente il lavoro.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top