首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SpeechSynthesizer没有获得所有已安装的声音3

SpeechSynthesizer没有获得所有已安装的声音3
EN

Stack Overflow用户
提问于 2018-08-12 18:54:53
回答 5查看 3.7K关注 0票数 4

我在地区和语言下增加了许多使用“添加语言”的声音。这些都出现在从文字到演讲的演讲中。(我正在使用Windows 10)

我想在我的应用程序中使用这些,并在SpeechSynthesizer类中使用System.Speech.Synthesis

在我的应用程序中列出可用的声音时,只显示了少数实际可用的声音:

代码语言:javascript
复制
static void Main()
{
    SpeechSynthesizer speech = new SpeechSynthesizer();

    ReadOnlyCollection<InstalledVoice> voices = speech.GetInstalledVoices();
    if (File.Exists("available_voices.txt"))
    {
        File.WriteAllText("available_voices.txt", string.Empty);
    }
    using (StreamWriter sw = File.AppendText("available_voices.txt"))
    {
        foreach (InstalledVoice voice in voices)
        {                 
            sw.WriteLine(voice.VoiceInfo.Name);                           
        }
    }
}

available_voices.txt中只列出了这些声音:

代码语言:javascript
复制
Microsoft David Desktop
Microsoft Hazel Desktop
Microsoft Zira Desktop
Microsoft Irina Desktop

但是,从文本到语言的背景下看,还有更多,比如Microsoft GeorgeMicrosoft Mark

这里接受的答案是:SpeechSynthesizer没有获得所有已安装的声音建议将平台更改为x86。我试过了,但我没有看到任何变化。

答案:SpeechSynthesizer没有获得所有已安装的声音2建议使用.NET v4.5,因为System.Speech.Synthesis中有一个bug。我的目标是.NET Framework4.5,但我仍然只能检索4个声音。

在我所联系的问题中,没有一个答案帮助我解决了我的问题,所以我再问一次。任何帮助都是被认可的。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2018-08-17 22:35:09

我通过安装来自另一个源的声音并获得Runtime (Version11)来解决这个问题。

可用的声音可以在microsoft 网站上找到(单击红色下载按钮并列出声音)

票数 1
EN

Stack Overflow用户

发布于 2020-07-19 11:59:32

在尝试了所有已发布的解决方案之后,我通过编辑注册表来解决这个问题:

Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Speech_OneCore\Voices\Tokens\MSTTS_V110_heIL_Asaf (其中MSTTS_V110_heIL_Asaf是我想在.NET中使用的声音的注册表文件夹,但不显示在GetInstalledVoices()中)复制到一个看起来相同但不是Speech_OneCore的注册表地址,它只是Speech

从技术上讲,为了复制注册表文件夹,我导出了原始文件夹,然后编辑了.reg文件,将Speech OneCore更改为Speech,然后应用了新的.reg文件。

票数 3
EN

Stack Overflow用户

发布于 2022-02-20 19:54:45

在最初的问题被问了3年之后,API似乎包含了同样的问题,所以这里有一个更“深潜”的答案。

TL;DR;代码示例-在底部

语音列表的问题是微软语音API的一个奇怪的设计-在Windows中有两组声音在注册表中的不同位置注册-一个在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft**Speech\Voices,,另一个在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft**Speech_OneCore\Voices.。

问题是SpeechSynthesizer(或者更具体地说-VoiceSynthesis)的初始化例程是钉牢到第一个,同时我们通常需要两者的结合。

因此,实际上有两种方法可以克服这种行为。

选项1 (在其他答案中都提到过):操作注册表,从Speech_OneCore注册中心实际复制语音定义记录,这使SpeechSynthesizer可以看到它们。这里有很多选项:手动注册表操作、PowerShell脚本、基于代码的等等。

选项2 (我在项目中使用的选项):使用反射将额外的声音放入到内部VoiceSyntesis的installedVoices字段中,有效地模拟了微软在代码中所做的事情。

好消息是,语音API源代码现在已经开放,所以我们不必在黑暗中摸索,试图理解我们需要做什么。

下面是原码片段:

代码语言:javascript
复制
using (ObjectTokenCategory category = ObjectTokenCategory.Create(SAPICategories.Voices))
{
    if (category != null)
    {
        // Build a list with all the voicesInfo
        foreach (ObjectToken voiceToken in category.FindMatchingTokens(null, null))
        {
            if (voiceToken != null && voiceToken.Attributes != null)
            {
                voices.Add(new InstalledVoice(voiceSynthesizer, new VoiceInfo(voiceToken)));
            }
        }
    }
}

我们只需要用另一个注册表条目路径替换SAPICategories.Voices常量,并重复整个菜谱。

坏消息是这里使用的所有需要的类、方法和字段都是内部的,所以我们必须广泛地使用反射来实例化类、调用方法和获取/设置字段。

请在下面的例子中找到我的实现--您可以在合成器上调用InjectOneCoreVoices扩展方法,它就可以完成这项工作。注意,如果出了问题,它会抛出异常,所以不要忘记适当的尝试/捕捉环境。

代码语言:javascript
复制
public static class SpeechApiReflectionHelper
{
    private const string PROP_VOICE_SYNTHESIZER = "VoiceSynthesizer";
    private const string FIELD_INSTALLED_VOICES = "_installedVoices";

    private const string ONE_CORE_VOICES_REGISTRY = @"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices";

    private static readonly Type ObjectTokenCategoryType = typeof(SpeechSynthesizer).Assembly
        .GetType("System.Speech.Internal.ObjectTokens.ObjectTokenCategory")!;

    private static readonly Type VoiceInfoType = typeof(SpeechSynthesizer).Assembly
        .GetType("System.Speech.Synthesis.VoiceInfo")!; 
    
    private static readonly Type InstalledVoiceType = typeof(SpeechSynthesizer).Assembly
        .GetType("System.Speech.Synthesis.InstalledVoice")!;


    public static void InjectOneCoreVoices(this SpeechSynthesizer synthesizer)
    {
        var voiceSynthesizer = GetProperty(synthesizer, PROP_VOICE_SYNTHESIZER);
        if (voiceSynthesizer == null) throw new NotSupportedException($"Property not found: {PROP_VOICE_SYNTHESIZER}");

        var installedVoices = GetField(voiceSynthesizer, FIELD_INSTALLED_VOICES) as IList;
        if (installedVoices == null)
            throw new NotSupportedException($"Field not found or null: {FIELD_INSTALLED_VOICES}");

        if (ObjectTokenCategoryType
                .GetMethod("Create", BindingFlags.Static | BindingFlags.NonPublic)?
                .Invoke(null, new object?[] {ONE_CORE_VOICES_REGISTRY}) is not IDisposable otc)
            throw new NotSupportedException($"Failed to call Create on {ObjectTokenCategoryType} instance");

        using (otc)
        {
            if (ObjectTokenCategoryType
                    .GetMethod("FindMatchingTokens", BindingFlags.Instance | BindingFlags.NonPublic)?
                    .Invoke(otc, new object?[] {null, null}) is not IList tokens)
                throw new NotSupportedException($"Failed to list matching tokens");

            foreach (var token in tokens)
            {
                if (token == null || GetProperty(token, "Attributes") == null) continue;
                
                var voiceInfo =
                    typeof(SpeechSynthesizer).Assembly
                        .CreateInstance(VoiceInfoType.FullName!, true,
                            BindingFlags.Instance | BindingFlags.NonPublic, null,
                            new object[] {token}, null, null);

                if (voiceInfo == null)
                    throw new NotSupportedException($"Failed to instantiate {VoiceInfoType}");
                
                var installedVoice =
                    typeof(SpeechSynthesizer).Assembly
                        .CreateInstance(InstalledVoiceType.FullName!, true,
                            BindingFlags.Instance | BindingFlags.NonPublic, null,
                            new object[] {voiceSynthesizer, voiceInfo}, null, null);
                
                if (installedVoice == null) 
                    throw new NotSupportedException($"Failed to instantiate {InstalledVoiceType}");
                
                installedVoices.Add(installedVoice);
            }
        }
    }

    private static object? GetProperty(object target, string propName)
    {
        return target.GetType().GetProperty(propName, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(target);
    }

    private static object? GetField(object target, string propName)
    {
        return target.GetType().GetField(propName, BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(target);
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51811901

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档