TTS для потока с помощью rephaudioformatinfo с использованием рецепсинтесизатора
-
28-09-2019 - |
Вопрос
я использую System.speech.synthesis.speechsynThesizer. Конвертировать текст в речь. И из-за анемической документации Microsoft (см. Мою ссылку, нет замечаний или примеров кода) У меня проблемы с возникновением головы или хвоста разницы между двумя методами:
EtoututtoaudioTream и setoutputtowavestream.
Вот что я выводил:
SetoutTtoToaudiOream принимает поток и экземпляр of rephaudioformatinfo, который определяет формат волнового файла (образцы в секунду, биты в секунду, аудиоканалы и т. Д.) И записывает текст на поток.
SetoutPutToWavestream принимает только поток и пишет 16 бит, моно, 22 кГц, PCM Wave для потока. Там нет способа пройти в Репозиоформатинфо.
Моя проблема - это setoutpttoaudiOream не пишет действительный волновой файл в поток. Например, я получаю InvalidOperationException («Заголовок волны поврежден») при прохождении потока в System.Media.SoundPlayer. Если я напишу поток на диск и попытаюсь воспроизвести его с WMP, я получаю сообщение «Windows Media Player не может воспроизводить файл ...», но поток, написанный SetoutPutToWavestream, правильно играет в обоих. Моя теория заключается в том, что setoutttoaudioStream не пишет (действительный) заголовок.
Чрезвычайно конвенции именования для сайтатует * бла * несовместимо. Setoutputtovavefile принимает repoaDioformatinfo, пока setoutputtowavestream не делает.
Мне нужно иметь возможность написать 8 кГц, 16-битную, моноволновую волновую волну к потоку, то, что ни на компьютере нет тотаудиострема, ни SetoutPutToWaveStream, не позволяют мне делать. У кого-нибудь есть понимание рехсинтесайзера и эти два метода?
Для справки, вот какой-то код:
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;
Для моего грубого тестирования он отлично работает, хотя использование отражения немного ICKY лучше, чем писать файл на диск и открытие потока.
Решение
Ваш фрагмент кода Borked, вы используете синтезатор после того, как это расположено. Но это не реальная проблема, я уверен. SetoutPuttoaudiOream производит RAW PCM AUDIO, «Числа». Без формата файла контейнера (заголовки), как то, что используется в файле .wav. Да, это нельзя воспроизводить с обычной медиа-программой.
Отсутствует перегрузка для SetoutPutToWavestream, которая принимает rephaudiofmatinfo, странно. Это действительно похоже на надзор для меня, хотя это очень редко редко в .NET Framework. Там нет убедительных причин, почему она не должна работать, базовый интерфейс 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 (), чтобы временно поток его в файл, безусловно, будет работать.