Question

J'utilise System.Speech.Synthesis.SpeechSynthesizer pour convertir texte pour parler. Et en raison de la documentation anémique de Microsoft (voir mon lien, il n'y a pas de remarques ou des exemples de code) Je vais avoir la tête de fauteurs de troubles ou des queues de la différence entre les deux méthodes:

SetOutputToAudioStream et SetOutputToWaveStream.

Voici ce que j'ai déduit:

SetOutputToAudioStream prend un cours d'eau et une instance SpeechAudioFormatInfo qui définit le format du fichier d'onde (échantillons par seconde, bits par seconde, les canaux audio, etc.) et écrit le texte dans le flux.

SetOutputToWaveStream prend juste un courant et écrit un 16 bit, mono, 22kHz, fichier d'onde MIC dans le flux. Il n'y a pas moyen de passer SpeechAudioFormatInfo.

Mon problème est SetOutputToAudioStream ne pas écrire un fichier wave valide dans le flux. Par exemple, je reçois un InvalidOperationException ( « L'en-tête de vague est corrompu ») lors du passage du flux à System.Media.SoundPlayer. Si j'écris le flux sur le disque et tenter de jouer avec WMP je reçois un « Windows Media Player ne peut pas lire le fichier ... » erreur mais le flux écrit par SetOutputToWaveStream joue correctement dans les deux. Ma théorie est que SetOutputToAudioStream n'écrit pas un en-tête (valide).

Bizarrement les conventions de nommage pour le SetOutputTo * Blah * est incompatible. SetOutputToWaveFile prend SpeechAudioFormatInfo alors que SetOutputToWaveStream ne fonctionne pas.

Je dois être en mesure d'écrire un 8kHz, 16 bits, mono fichier vague à un flux, quelque chose qui ne SetOutputToAudioStream ou SetOutputToWaveStream me permettent de faire. Est-ce que quelqu'un a aperçu SpeechSynthesizer et ces deux méthodes?

Pour référence, voici un 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);
}

Solution:

Un grand merci à @Hans Passant, voici l'essentiel de ce que je suis maintenant avec:

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;

Pour mes tests bruts il fonctionne très bien, mais en utilisant la réflexion est un peu dégueu, il vaut mieux que d'écrire le fichier sur le disque et l'ouverture d'un cours d'eau.

Était-ce utile?

La solution

extrait de code est foireuse, vous utilisez synthé après il est disposé. Mais ce n'est pas le vrai problème, je suis sûr. SetOutputToAudioStream produit l'audio PCM brut, les « chiffres ». Sans un format de fichier conteneur (en-têtes) comme ce qui est utilisé dans un fichier .wav. Oui, ce ne peut pas être lu avec un programme régulier de médias.

La surcharge manquante pour SetOutputToWaveStream qui prend SpeechAudioFormatInfo est étrange. Il ressemble vraiment à un oubli de moi, même si c'est extrêmement rare dans le framework .NET. Il n'y a aucune raison convaincante pourquoi il ne devrait pas fonctionner, l'interface sous-jacente SAPI le supporte. Il peut être piraté autour de la réflexion pour appeler la méthode SetOutputStream privée. Cette belle quand je travaillé testé, mais je ne peux pas se porter garant pour elle:

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

Si vous êtes mal à l'aise avec le hack puis en utilisant Path.GetTempFileName () pour diffuser temporairement dans un fichier sera certainement le travail.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top