System.Speech.Synthesis hangs with high CPU on 2012 R2

☆樱花仙子☆ 提交于 2019-12-04 22:51:54

I think the issue is the return type. IIS Express is letting you get away with it, but IIS is not:

Task<FileContentResult>

So if you try:

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;
}

I bet you also need to add the audio/wav MIME Type in IIS.

I have had this experience with server 2012R2 before (not the synth api granted, but same issue). I fixed it by using "await task.ConfigureAwait(false)" on all my tasks. See if that works for you.

Good luck.

At this blog you can find a solution to a similar problem - exception when using SpeechSynthesizer on fresh Windows 8.1 installation. The problem in that case is with wrong permission entry for CurrentUserLexicon user (which is used by SpeechSynthesizer. To resolve, this blog post suggests to remove permission entry "ALL APPLICATION PACKAGES" from Software\Microsoft\Speech\CurrentUserLexicon registry key.

I found that I can reproduce the issue on other servers, including Azure VMs, so I ruled out the possibility of an issue with our particular environment.

Also, I found that I could get the code to work fine on 2012 R2 if I ran the application pool under an identity that was an admin on the server and had previously logged into the server. After a very long process of ruling out permissions issues I decided it must be something in the logging in process that occurs that enables the TTS API calls to work correctly. (Whatever it is, I wasn't able to find it digging through procmon traces). So fortunately the ApplicationPoolIdentity can have similar login magic applied by opening "Advanced Settings" for the app pool in IIS and setting Load User Profile to True.

The identity that runs the app pool also needs permission to read HKU\.Default\Software\Microsoft\Speech which can be granted to ApplicationPoolIdentity by using the local server for the location and IIS APPPOOL\.Net v4.5 for the username (where .Net v4.5 is the name of the application pool).

Once read permission to the reg key is granted, and the app pool is configured to load user profile, the above code works fine. Tested on Azure VMs and vanilla 2012 R2 from MSDN ISOs.

This is just off the top of my head and it hasn't been tested but you may be able to do something like this:

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");
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!