Я использую System.Speech.Synthesis.SpeechSynthesizer для преобразования текста в речь. . И из-за анемичной документации Microsoft (см. мою ссылку, там нет замечаний или примеров кода) у меня возникают проблемы с определением разницы между двумя методами:
SetOutputToAudioStream и SetOutputToWaveStream.
Вот что я вывел:
SetOutputToAudioStream принимает поток и экземпляр SpeechAudioFormatInfo, который определяет формат волнового файла (выборки в секунду, биты в секунду, аудиоканалы и т. д.) и записывает текст в поток.
SetOutputToWaveStream берет только поток и записывает в поток 16-битный, моно, 22 кГц, волновой файл PCM. Нет возможности пройти в SpeechAudioFormatInfo.
Моя проблема в том, что SetOutputToAudioStream не записывает в поток действительный волновой файл. Например, я получаю InvalidOperationException ("Заголовок волны поврежден") при передаче потока в System.Media.SoundPlayer. Если я записываю поток на диск и пытаюсь воспроизвести его с помощью WMP, я получаю сообщение об ошибке «Проигрыватель Windows Media не может воспроизвести файл...», но поток, записанный SetOutputToWaveStream, воспроизводится правильно в обоих случаях. Моя теория заключается в том, что SetOutputToAudioStream не записывает (действительный) заголовок.
Как ни странно, соглашения об именах для SetOutputTo*Blah* непоследовательны. SetOutputToWaveFile принимает SpeechAudioFormatInfo, а SetOutputToWaveStream — нет.
Мне нужно иметь возможность записывать 8 кГц, 16-битный, моно волновой файл в поток, что не позволяют мне ни SetOutputToAudioStream, ни SetOutputToWaveStream. Кто-нибудь разбирается в SpeechSynthesizer и этих двух методах?
Для справки, вот код:
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;
Для моего грубого тестирования это работает отлично, хотя использование отражения немного непривычно, это лучше, чем запись файла на диск и открытие потока.