如何找到SpeechSynthesizer所选语音的音频格式

Ahm*_*mad 11 c# audio sapi speechsynthesizer

在C#的文本到语音应用程序中,我使用SpeechSynthesizer类,它有一个名为的事件SpeakProgress,每个语音都被触发.但是对于某些声音,参数e.AudioPosition不与输出音频流同步,并且输出波形文件的播放速度比此位置显示的速度快(请参阅此相关问题).

无论如何,我试图找到有关比特率和与所选语音相关的其他信息的确切信息.正如我所经历的那样,如果我可以使用此信息初始化wave文件,则将解决同步问题.但是,如果我找不到这样的信息SupportedAudioFormat,我知道找不到其他方法.例如,"Microsoft David Desktop"语音不提供支持的格式VoiceInfo,但它似乎支持PCM 16000 hz,16位格式.

如何找到SpeechSynthesizer所选语音的音频格式

 var formats = CurVoice.VoiceInfo.SupportedAudioFormats;

 if (formats.Count > 0)
 {
     var format = formats[0];
     reader.SetOutputToWaveFile(CurAudioFile, format);
 }
 else
 {
        var format = // How can I find it, if the audio hasn't provided it?           
        reader.SetOutputToWaveFile(CurAudioFile, format );
}
Run Code Online (Sandbox Code Playgroud)

El *_*rko 2

更新:此答案已在调查后进行了编辑。最初,我凭记忆建议 SupportedAudioFormats 可能只是来自(可能配置错误)注册表数据;调查表明,对我来说,在 Windows 7 上,情况确实如此,并且在 Windows 8 上也有备份。

支持的音频格式的问题

System.Speech封装了古老的 COM 语音 API (SAPI),某些语音是 32 位与 64 位,或者可能配置错误(在 64 位计算机的注册表上,HKLM/Software/Microsoft/Speech/Voices与HKLM/Software/Wow6432Node/Microsoft/Speech/Voices.

我已经指出了 ILSpySystem.Speech及其类,并且我非常确信 SupportedAudioFormats 仅来自注册表数据,因此,如果您的 TTS 引擎没有为应用程序的平台目标正确注册,则VoiceInfo在枚举时可能会得到零结果( SupportedAudioFormatsx86、任何或 64 位),或者如果供应商根本不在注册表中提供此信息。

语音可能仍然支持不同的、附加的或更少的格式,因为这取决于语音引擎(代码)而不是注册表(数据)。所以这可能是在黑暗中进行的。标准 Windows 语音在这方面通常比第三方语音更加一致,但它们仍然不一定有用地提供SupportedAudioFormats.

艰难地找到这些信息

我发现仍然可以获得当前语音的当前格式 - 但这确实依赖于反射来访问 System.Speech SAPI 包装器的内部。

因此,这是非常脆弱的代码!我不建议在生产中使用。

注意:下面的代码确实需要您调用一次 Speak() 进行设置;如果没有 Speak(),则需要更多调用来强制设置。不过,我可以打电话Speak("")什么都不说,这样就很好了。

执行:

[StructLayout(LayoutKind.Sequential)]
struct WAVEFORMATEX
{
    public ushort wFormatTag;
    public ushort nChannels;
    public uint nSamplesPerSec;
    public uint nAvgBytesPerSec;
    public ushort nBlockAlign;
    public ushort wBitsPerSample;
    public ushort cbSize;
}

WAVEFORMATEX GetCurrentWaveFormat(SpeechSynthesizer synthesizer)
{
    var voiceSynthesis = synthesizer.GetType()
                                    .GetProperty("VoiceSynthesizer", BindingFlags.Instance | BindingFlags.NonPublic)
                                    .GetValue(synthesizer, null);

    var ttsVoice = voiceSynthesis.GetType()
                                 .GetMethod("CurrentVoice", BindingFlags.Instance | BindingFlags.NonPublic)
                                 .Invoke(voiceSynthesis, new object[] { false });

    var waveFormat = (byte[])ttsVoice.GetType()
                                     .GetField("_waveFormat", BindingFlags.Instance | BindingFlags.NonPublic)
                                     .GetValue(ttsVoice);

    var pin = GCHandle.Alloc(waveFormat, GCHandleType.Pinned);
    var format = (WAVEFORMATEX)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(WAVEFORMATEX));
    pin.Free();

    return format;
}
Run Code Online (Sandbox Code Playgroud)

用法:

SpeechSynthesizer s = new SpeechSynthesizer();
s.Speak("Hello");
var format = GetCurrentWaveFormat(s);
Debug.WriteLine($"{s.Voice.SupportedAudioFormats.Count} formats are claimed as supported.");
Debug.WriteLine($"Actual format: {format.nChannels} channel {format.nSamplesPerSec} Hz {format.wBitsPerSample} audio");
Run Code Online (Sandbox Code Playgroud)

为了测试它,我将 Microsoft Anna 的AudioFormats注册表项重命名为HKLM/Software/Wow6432Node/Microsoft/Speech/Voices/Tokens/MS-Anna-1033-20-Dsk/Attributes,导致SpeechSynthesizer.Voice.SupportedAudioFormats查询时没有元素。以下是这种情况下的输出:

0 formats are claimed as supported.
Actual format: 1 channel 16000 Hz 16 audio
Run Code Online (Sandbox Code Playgroud)