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

标签 c# audio sapi speechsynthesizer

在 C# 的文本到语音应用程序中,我使用 SpeechSynthesizer 类,它有一个名为 SpeakProgress 的事件,每个说出的词都会被触发。但对于某些声音,参数 e.AudioPosition 与输出音频流不同步,输出波形文件的播放速度比此位置显示的快(参见 this related question )。

无论如何,我正在尝试查找有关比特率的确切信息以及与所选语音相关的其他信息。根据我的经验,如果我可以使用此信息初始化 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 );
}

最佳答案

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

支持的音频格式问题

System.Speech 包装了古老的 COM 语音 API (SAPI),一些语音是 32 位和 64 位,或者可能配置错误(在 64 位机器的注册表中,HKLM/Software/Microsoft/Speech/Voices 对比 HKLM/Software/Wow6432Node/Microsoft/Speech/Voices

我已将 ILSpy 指向 System.Speech 及其 VoiceInfo 类,我非常相信 SupportedAudioFormats 仅来自注册表数据,因此有可能获得如果您的 TTS 引擎未针对应用程序的平台目标(x86、任何或 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;
}

用法:

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

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

0 formats are claimed as supported.
Actual format: 1 channel 16000 Hz 16 audio

关于c# - 如何找到 SpeechSynthesizer 所选语音的音频格式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34150205/

相关文章:

c# - C# SAPI 可以说 SSML 字符串吗?

c# - WasapiLoopbackCapture 内部音频识别在没有音频时会出现乱码和文本

c# - 禁止某些类型的通用类型约束?

python - 在python中均衡mp3文件的正确方法

c++ - SAPI5 语音/使用 32 位语音

c# - CScore 输出大于一个字节的 PCM

java - JLayer 暂停和恢复

c# - NOSQL数据库选型论坛

c# - 二进制字符串到 int,然后是 BitWise AND

c# - 条件变量的 TPL 等价物是什么?