我正在使用System.Speech.Synthesis.SpeechSynthesizer将文字转换为语音。由于微软的文档不足(参见我的链接,没有任何评论或代码示例),我无法解决两种方法之间差异的问题:
SetOutputToAudioStream和SetOutputToWaveStream。
以下是我推断的内容:
SetOutputToAudioStream接受一个流和一个SpeechAudioFormatInfo实例,该实例定义波形文件的格式(每秒采样数,每秒位数,音频通道等),并将文本写入流中。
SetOutputToWaveStream只接收一个流,并将16位单声道22kHz PCM波形文件写入流中。没有办法传入SpeechAudioFormatInfo。
我的问题是SetOutputToAudioStream没有将有效的wave文件写入流。例如,在将流传递给System.Media.SoundPlayer时,我得到一个InvalidOperationException(“wave header is corrupt”)。如果我将流写入磁盘并尝试使用WMP播放它我得到“Windows Media Player无法播放文件...”错误,但SetOutputToWaveStream写入的流在两者中都正常播放。我的理论是SetOutputToAudioStream没有写一个(有效的)标题。
奇怪的是,SetOutputTo * Blah *的命名约定不一致。 SetOutputToWaveFile采用SpeechAudioFormatInfo而SetOutputToWaveStream不采用。
我需要能够将一个8kHz,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;
对于我的粗略测试,它工作得很好,虽然使用反射有点icky它比将文件写入磁盘并打开流更好。
答案 0 :(得分:7)
您的代码段已经被屏蔽,您在处理后正在使用 synth 。但这不是我确定的真正问题。 SetOutputToAudioStream生成原始PCM音频,即“数字”。没有像.wav文件中使用的容器文件格式(标题)。是的,这不能用常规媒体节目播放。
使用SpeechAudioFormatInfo的SetOutputToWaveStream缺少的重载很奇怪。它确实看起来像是对我的疏忽,尽管这在.NET框架中非常罕见。没有令人信服的理由说明它不应该工作,底层的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);
}
}
}
如果你对hack感到不舒服,那么使用Path.GetTempFileName()暂时将其流式传输到文件肯定会有效。