超快文本到语音(WAV - > MP3)在ASP.NET MVC(Ultra Fast Te

2019-07-31 11:21发布

这个问题本质上是对微软的语音API(SAPI)的服务器工作负载的适用性,以及它是否能够可靠的w3wp内部使用语音合成。 我们有一个使用异步控制器使用本地System.Speech .NET 4的组件(不是Microsoft.Speech一个附带微软语音平台的一部分-运行11版)和lame.exe生成的MP3如下:

       [CacheFilter]
        public void ListenAsync(string url)
        {
                string fileName = string.Format(@"C:\test\{0}.wav", Guid.NewGuid());                       

                try
                {
                    var t = new System.Threading.Thread(() =>
                    {
                        using (SpeechSynthesizer ss = new SpeechSynthesizer())
                        {
                            ss.SetOutputToWaveFile(fileName, new SpeechAudioFormatInfo(22050, AudioBitsPerSample.Eight, AudioChannel.Mono));
                            ss.Speak("Here is a test sentence...");
                            ss.SetOutputToNull();
                            ss.Dispose();
                        }

                        var process = new Process() { EnableRaisingEvents = true };
                        process.StartInfo.FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"bin\lame.exe");
                        process.StartInfo.Arguments = string.Format("-V2 {0} {1}", fileName, fileName.Replace(".wav", ".mp3"));
                        process.StartInfo.UseShellExecute = false;
                        process.StartInfo.RedirectStandardOutput = false;
                        process.StartInfo.RedirectStandardError = false;
                        process.Exited += (sender, e) =>
                        {
                            System.IO.File.Delete(fileName);

                            AsyncManager.OutstandingOperations.Decrement();
                        };

                        AsyncManager.OutstandingOperations.Increment();
                        process.Start();
                    });

                    t.Start();
                    t.Join();
                }
                catch { }

            AsyncManager.Parameters["fileName"] = fileName;
        }

        public FileResult ListenCompleted(string fileName)
        {
            return base.File(fileName.Replace(".wav", ".mp3"), "audio/mp3");
        }

现在的问题是,为什么SpeechSynthesizer需要为了在这样一个单独的线程中运行,返回(这是在其他地方SO报道这里和这里 ),以及是否实施STAThreadRouteHandler这个要求是更有效的/不是上面的方法可扩展性?

二,什么是运行的选项SpeakAsync在ASP.NET(MVC或Web表单)范围内? 我已经试过选项都不似乎工作(见下文更新)。

对于如何改善这种情况的任何其他建议(即必须串行执行给对方,但每个人都有异步支持两个依赖)的欢迎。 我不觉得这个方案是负载下的可持续发展,特别是考虑到已知的内存泄漏的SpeechSynthesizer 。 考虑到运行在不同的堆栈这项服务都在一起。

更新:无论是中SpeakSpeakAsnc选项出现的下工作STAThreadRouteHandler 。 前者产生:

System.InvalidOperationException:异步操作都没有这方面允许的。 页开始的异步操作必须具有异步属性设置为true,并且异步操作只能前PreRenderComplete事件在页面上开始。 在System.Web.LegacyAspNetSynchronizationContext.OperationStarted()在System.ComponentModel.AsyncOperationManager.CreateOperation(对象userSuppliedState)在System.Speech.Internal.Synthesis.VoiceSynthesis..ctor(WeakReference的speechSynthesizer)在System.Speech.Synthesis.SpeechSynthesizer.get_VoiceSynthesizer( )在System.Speech.Synthesis.SpeechSynthesizer.SetOutputToWaveFile(字符串路径,SpeechAudioFormatInfo formatInfo)

后者的结果:

System.InvalidOperationException:异步操作方法“听”不能同步执行。 在System.Web.Mvc.Async.AsyncActionDescriptor.Execute(ControllerContext controllerContext,IDictionary`2参数)

这似乎是一个自定义的STA线程池(与ThreadStatic COM对象的实例)是一个更好的办法: http://marcinbudny.blogspot.ca/2012/04/dealing-with-sta-coms-in-web.html

更新#2:它似乎并不像System.Speech.SpeechSynthesizer需要STA处理,似乎只要你遵循运行在MTA线程精细Start/Join模式。 这里有一个新的版本,能够正确使用SpeakAsync (问题有处置不能过早!),并打破了WAV生成和MP3代成两个独立的请求:

[CacheFilter]
[ActionName("listen-to-text")]
public void ListenToTextAsync(string text)
{
    AsyncManager.OutstandingOperations.Increment();   

    var t = new Thread(() =>
    {
        SpeechSynthesizer ss = new SpeechSynthesizer();
        string fileName = string.Format(@"C:\test\{0}.wav", Guid.NewGuid());

        ss.SetOutputToWaveFile(fileName, new SpeechAudioFormatInfo(22050,
                                                                   AudioBitsPerSample.Eight,
                                                                   AudioChannel.Mono));
        ss.SpeakCompleted += (sender, e) =>
        {
            ss.SetOutputToNull();
            ss.Dispose();

            AsyncManager.Parameters["fileName"] = fileName;
            AsyncManager.OutstandingOperations.Decrement();
        };

        CustomPromptBuilder pb = new CustomPromptBuilder(settings.DefaultVoiceName);
        pb.AppendParagraphText(text);
        ss.SpeakAsync(pb);               
    });

    t.Start();
    t.Join();                    
}

[CacheFilter]
public ActionResult ListenToTextCompleted(string fileName)
{
    return RedirectToAction("mp3", new { fileName = fileName });
}

[CacheFilter]
[ActionName("mp3")]
public void Mp3Async(string fileName) 
{
    var process = new Process()
    {
        EnableRaisingEvents = true,
        StartInfo = new ProcessStartInfo()
        {
            FileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"bin\lame.exe"),
            Arguments = string.Format("-V2 {0} {1}", fileName, fileName.Replace(".wav", ".mp3")),
            UseShellExecute = false,
            RedirectStandardOutput = false,
            RedirectStandardError = false
        }
    };

    process.Exited += (sender, e) =>
    {
        System.IO.File.Delete(fileName);
        AsyncManager.Parameters["fileName"] = fileName;
        AsyncManager.OutstandingOperations.Decrement();
    };

    AsyncManager.OutstandingOperations.Increment();
    process.Start();
}

[CacheFilter]
public ActionResult Mp3Completed(string fileName) 
{
    return base.File(fileName.Replace(".wav", ".mp3"), "audio/mp3");
}

Answer 1:

I / O是在服务器上非常昂贵。 多少多个流WAV,书写你认为你可以得到一个服务器的硬盘上? 为什么没有把一切都在内存中,只写的MP3时,它的完全处理? MP3的要小得多,在I / O将从事的时间量小。 你甚至可以更改代码,如果你想直接返回流给用户而不是保存到MP3。

我如何可以使用LAME到WAV编码为一个mp3 C#



Answer 2:

这个问题现在是有点老了,但是这是我在做什么,它一直伟大的工作迄今:

    public Task<FileStreamResult> Speak(string text)
    {
        return Task.Factory.StartNew(() =>
        {
            using (var synthesizer = new SpeechSynthesizer())
            {
                var ms = new MemoryStream();
                synthesizer.SetOutputToWaveStream(ms);
                synthesizer.Speak(text);

                ms.Position = 0;
                return new FileStreamResult(ms, "audio/wav");
            }
        });
    }

可以帮助别人...



文章来源: Ultra Fast Text to Speech (WAV -> MP3) in ASP.NET MVC