System.Speech.Synthesis 在 2012 R2 上挂起高 CPU

本文关键字:挂起 CPU R2 2012 Speech Synthesis System | 更新日期: 2023-09-27 18:10:03

我有一个 asp.net 的MVC应用程序,该应用程序具有控制器操作,该操作将字符串作为输入并发送合成语音的响应wav文件。 下面是一个简化的示例:

    public async Task<ActionResult> Speak(string text)
    {
        Task<FileContentResult> task = Task.Run(() =>
        {
            using (var synth = new System.Speech.Synthesis.SpeechSynthesizer())
            using (var stream = new MemoryStream())
            {
                synth.SetOutputToWaveStream(stream);
                synth.Speak(text);
                var bytes = stream.GetBuffer();
                return File(bytes, "audio/x-wav");
            }
        });
        return await task;
    }

应用程序(特别是此操作方法(在 2008 R2 服务器、2012(非 R2(服务器和我的 8.1 开发 PC 上的服务器环境中运行良好。 它在标准 Azure 2012 R2 虚拟机上运行良好。 但是,当我将其部署到三台 2012 R2 服务器(其最终的永久主页(时,操作方法永远不会生成 HTTP 响应 - IIS 工作进程无限期地最大化其中一个 CPU 内核。 事件查看器中没有任何内容,在使用 Procmon 观看服务器时,没有任何内容跳出来。 我已经通过远程调试附加到进程,synth.Speak(text)永远不会返回。 当执行synth.Speak(text)调用时,我会立即在服务器的任务管理器中看到失控的w3wp.exe进程。

我的第一个倾向是相信某些进程通常会干扰服务器上的语音合成,但 Windows 讲述人工作正常,像这样的简单控制台应用也可以正常工作:

static void Main(string[] args)
{
    var synth = new System.Speech.Synthesis.SpeechSynthesizer();
    synth.Speak("hello");
}

所以显然我不能责怪服务器的语音合成。 所以也许我的代码中有问题,或者 IIS 配置中有什么奇怪的问题? 如何使此控制器操作在这些服务器上正常工作?

这是测试操作方法的简单方法(只需为路由获取正确的url值(:

<div>
    <input type="text" id="txt" autofocus />
    <button type="button" id="btn">Speak</button>
</div>
<script>
    document.getElementById('btn').addEventListener('click', function () {
        var text = document.getElementById('txt').value;
        var url = window.location.href + '/speak?text=' + encodeURIComponent(text);
        var audio = document.createElement('audio');
        var canPlayWavFileInAudioElement = audio.canPlayType('audio/wav'); 
        var bgSound = document.createElement('bgsound');
        bgSound.src = url;
        var canPlayBgSoundElement = bgSound.getAttribute('src');
        if (canPlayWavFileInAudioElement) {
            // probably Firefox and Chrome
            audio.setAttribute('src', url);
            audio.setAttribute('autoplay', '');
            document.getElementsByTagName('body')[0].appendChild(audio);
        } else if (canPlayBgSoundElement) {
            // internet explorer
            document.getElementsByTagName('body')[0].appendChild(bgSound);
        } else {
            alert('This browser probably can''t play a wav file');
        }
    });
</script>

System.Speech.Synthesis 在 2012 R2 上挂起高 CPU

我发现我可以在其他服务器(包括 Azure VM(上重现该问题,因此我排除了特定环境出现问题的可能性。

此外,我发现如果我在服务器上的管理员身份下运行应用程序池并且以前已登录到服务器,则可以使代码在 2012 R2 上正常工作。 经过一个很长的排除权限问题的过程,我决定一定是登录过程中发生的某些事情使 TTS API 调用能够正常工作。 (不管它是什么,我都无法通过procmon痕迹找到它(。 因此,幸运的是,应用程序池身份可以通过在IIS中打开应用程序池的"高级设置"并将Load User Profile设置为True来应用类似的登录魔法。

运行应用程序池的标识还需要读取HKU'.Default'Software'Microsoft'Speech的权限,可以通过使用本地服务器作为位置和用户名IIS APPPOOL'.Net v4.5(其中.Net v4.5是应用程序池的名称(来授予应用程序池标识。

授予对 reg 密钥的读取权限,并将应用程序池配置为加载用户配置文件后,上述代码工作正常。 在 Azure VM 和 MSDN ISO 的原版 2012 R2 上进行了测试。

我认为问题是返回类型。 IIS Express让你侥幸逃脱,但IIS不是:

Task<FileContentResult>

因此,如果您尝试:

public async Task<FileContentResult> Speak(string text)
{
    Task<FileContentResult> task = Task.Run(() =>
    {
        using (var synth = new System.Speech.Synthesis.SpeechSynthesizer())
        using (var stream = new MemoryStream())
        {
            synth.SetOutputToWaveStream(stream);
            synth.Speak(text);
            var bytes = stream.GetBuffer();
            return File(bytes, "audio/x-wav");
        }
    });
    return await task;
}

我敢打赌,您还需要在IIS中添加音频/wav MIME类型。

我以前有过使用服务器 2012R2 的经验(不是授予的合成器 api,而是相同的问题(。 我通过使用"等待任务"修复了它。配置等待(假("在我的所有任务上。 看看这是否适合您。

祝你好运。

在此博客中,您可以找到类似问题的解决方案 - 在全新 Windows 8.1 安装上使用 SpeechSynthesizer 时出现异常。在这种情况下,问题在于CurrentUserLexicon用户的权限条目错误(由SpeechSynthesizer使用(。为了解决这个问题,这篇博文建议从软件''Microsoft''语音''当前用户词典注册表项中删除权限条目"所有应用程序包"。

这只是我的头顶,它还没有经过测试,但你可以做这样的事情:

public ActionResult Speak(string text)
{
var speech = new SpeechSynthesizer();
speech.Speak(text);
byte[] bytes;
using (var stream = new MemoryStream())
{
    speech.SetOutputToWaveStream(stream);
    bytes = stream.ToArray();
}
return File(bytes, "audio/x-wav");
}