voices tts spanish microsoft language how espaƱol c# asp.net-mvc text-to-speech windows-server-2012-r2 speech-synthesis

c# - tts - System.Speech.Synthesis se cuelga con una CPU alta en 2012 R2



windows text to speech voices (5)

Tengo una aplicación MVC de asp.net que tiene una acción de controlador que toma una cadena como entrada y envía un archivo wav de respuesta del discurso sintetizado. Aquí hay un ejemplo simplificado:

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

La aplicación (y este método de acción en particular) se ejecuta bien en un entorno de servidor en servidores 2008 R2, servidores 2012 (no R2) y mi PC con 8.1 dev. También funciona bien en una máquina virtual estándar de Azure 2012 R2. Sin embargo, cuando lo implemento en tres servidores 2012 R2 (su eventual inicio permanente), el método de acción nunca produce una respuesta HTTP: el proceso del trabajador IIS maximiza uno de los núcleos de la CPU de forma indefinida. No hay nada en el visor de eventos y no hay nada que me salte cuando veo el servidor con Procmon. Me he adjuntado al proceso con la depuración remota, y el synth.Speak(text) nunca regresa. Cuando se synth.Speak(text) la synth.Speak(text) , inmediatamente veo el proceso w3wp.exe fuera de control en el administrador de tareas del servidor.

Mi primera inclinación fue creer que algún proceso estaba interfiriendo con la síntesis de voz en general en los servidores, pero el Narrador de Windows funciona correctamente y una aplicación de consola simple como esta también funciona correctamente:

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

Así que obviamente no puedo culpar a la síntesis de voz del servidor en general. ¿Entonces tal vez hay un problema en mi código, o algo extraño en la configuración de IIS? ¿Cómo puedo hacer que esta acción del controlador funcione correctamente en estos servidores?

Esta es una forma sencilla de probar el método de acción (solo tiene que obtener el valor de url correcto para el enrutamiento):

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


Creo que el problema es el tipo de retorno. IIS Express le permite salirse con la suya, pero IIS no lo es:

Task<FileContentResult>

Así que si lo intentas:

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

Apuesto a que también debe agregar el tipo de audio / wav MIME en IIS.


Descubrí que puedo reproducir el problema en otros servidores, incluidas las máquinas virtuales de Azure, por lo que descarté la posibilidad de un problema con nuestro entorno particular.

Además, descubrí que podía hacer que el código funcionara bien en 2012 R2 si ejecutaba el grupo de aplicaciones con una identidad que era un administrador en el servidor y que había iniciado sesión anteriormente en el servidor . Después de un proceso muy largo de descartar problemas de permisos, decidí que debe haber algo en el proceso de inicio de sesión que ocurra que permita que las llamadas a la API TTS funcionen correctamente. (Sea lo que sea, no pude encontrarlo cavando a través de las huellas de procón). Así que, afortunadamente, ApplicationPoolIdentity puede tener una magia de inicio de sesión similar al abrir "Configuración avanzada" para el grupo de aplicaciones en IIS y configurar Load User Profile en True .

La identidad que ejecuta el grupo de aplicaciones también necesita permiso para leer HKU/.Default/Software/Microsoft/Speech que se puede otorgar a ApplicationPoolIdentity usando el servidor local para la ubicación y IIS APPPOOL/.Net v4.5 para el nombre de usuario (donde .Net v4.5 es el nombre del grupo de aplicaciones).

Una vez que se otorga el permiso de lectura a la clave reg, y el grupo de aplicaciones está configurado para cargar el perfil de usuario, el código anterior funciona bien. Probado en máquinas virtuales Azure y vanilla 2012 R2 de ISO de MSDN.


En peterluzc.blogspot.ru/2014/01/… , puede encontrar una solución a un problema similar: una excepción al usar SpeechSynthesizer en una instalación nueva de Windows 8.1. El problema en ese caso es con una entrada de permiso incorrecta para el usuario CurrentUserLexicon (que utiliza SpeechSynthesizer. Para resolver, esta publicación de blog sugiere eliminar la entrada de permiso "TODOS LOS PAQUETES DE SOLICITUD" de la clave de registro Software / Microsoft / Speech / CurrentUserLexicon.


Esto está fuera de mi cabeza y no se ha probado, pero es posible que pueda hacer algo como esto:

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


He tenido esta experiencia con el servidor 2012R2 antes (no la API de sintetizador concedida, sino el mismo problema). Lo arreglé utilizando "await task.ConfigureAwait (false)" en todas mis tareas. A ver si eso funciona para ti.

Buena suerte.