【问题标题】:Unable to create MULTIPLE TTS "wav" files using MS-SAPI 5.1 in C#无法在 C# 中使用 MS-SAPI 5.1 创建多个 TTS“wav”文件
【发布时间】:2010-12-05 06:14:45
【问题描述】:

大家好!

我正在做一个项目,我将不得不使用 TTS 创建名称的 WAV 文件。

我在 Windows Server 2003 上安装了 MS-SAPI 5.1 SDK,并使用 C# 编写 TTS 程序。除了默认的 Microsoft Sam 语音外,我还在服务器上安装了来自 NeoSpeech TTS 的语音。

我遇到的问题是,程序生成的工作 WAV 文件不超过 1 个

更具体地说,如果我向程序发送 4 个名称,程序会创建 4 个 WAV 文件。但是,只有名字被正确转换。文件大小大于 1 kb,并且文件也在媒体播放器中播放。

其他 3 个文件已创建,但大小为 1 kb,无法在任何媒体播放器中使用。

我是 C# 和 MS-SAPI 的新手,但我相信我在创建代码方面做得不错。我花了几天的时间试图弄清楚这一点,但我现在没有精力了。

非常感谢您对此问题的任何见解。感谢您的宝贵时间。

这是我的代码:

using System;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using SpeechLib;
using System.Threading;

namespace TTS_Text_To_Wav
{
    class Gender
    {
        public static String MALE = "Male";
        public static String FEMALE = "Female";
    }

    class Languages
    {
        public static String ENGLISH = "409;9";
        public static String SPANISH = "40a";
    }

    class Vendor
    {
        public static String VOICEWARE = "Voiceware";
        public static String MICROSOFT = "Microsoft";
    }

    class SampleTTS
    {
        static void Main(string[] args)
        {
            SampleTTS processor = null;

            try
            {
                processor = new SampleTTS();

                // get unprocessed items
                ArrayList unProcessedItems = new ArrayList();
                unProcessedItems.Add("Kate");
                unProcessedItems.Add("Sam");
                unProcessedItems.Add("Paul");
                unProcessedItems.Add("Violeta");

                if (unProcessedItems != null)
                {
                    foreach (string record in unProcessedItems)
                    {
                        // convert text to wav
                        processor.ConvertStringToSpeechWav(record, "c:/temp/" + record + ".wav", Vendor.VOICEWARE, Gender.MALE, Languages.ENGLISH);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        void ConvertStringToSpeechWav(String textToConvert, String pathToCreateWavFile, String vendor, String gender, String language)
        {
            SpVoice voice = null;
            SpFileStream spFileStream = null;

            try
            {
                spFileStream = new SpFileStream();
                voice = new SpVoice();

                spFileStream.Format.Type = SpeechAudioFormatType.SAFT8kHz16BitMono;
                spFileStream.Open(pathToCreateWavFile, SpeechStreamFileMode.SSFMCreateForWrite, false);

                voice.Voice = voice.GetVoices("Vendor=" + vendor + ";Gender=" + gender, "Language=" + language).Item(0);
                voice.AudioOutputStream = spFileStream;
                voice.Speak(textToConvert, SpeechVoiceSpeakFlags.SVSFlagsAsync | SpeechVoiceSpeakFlags.SVSFPurgeBeforeSpeak);
                voice.WaitUntilDone(Timeout.Infinite);
            }
            catch (Exception e)
            {
                throw new Exception("Error occured in ConvertStringToSpeechWav()\n" + e.Message);
            }
            finally
            {
                if (spFileStream != null)
                {
                    spFileStream.Close();
                }
            }
        }
    }
}

编辑:

我似乎注意到了一些新的行为。该代码适用于系统上的 Microsoft 语音。似乎只有 NeoSpeech 声音才有这个问题。

这是否意味着我的代码正确而声音有问题?一方面,我从客户那里得到了声音,所以我对此无能为力。其次,这些是生产就绪的声音。我很确定它们已经过很好的测试,否则我们会听说很多。

我仍然倾向于相信我编写的代码有问题。

还有其他建议吗?我在这里进行了真正的修复,我们将不胜感激。

【问题讨论】:

    标签: c# wav text-to-speech sapi


    【解决方案1】:

    虽然我没有发现导致 TTS 问题的明显原因,但您可以使用一些最佳实践和代码简化。

    首先,包含 Main()、SampleTTS 的类不需要实例化即可调用 ConvertStringToSpeechWav():

    class SampleTTS
    {
        static void Main(string[] args)
        {
            SampleTTS processor = null;
    
            try
            {
                processor = new SampleTTS();
    

    Sample TTS 类可以改写如下:

    class SampleTTS
    {
        static void Main(string[] args)
        {
            try
            {
                // get unprocessed items
                List<String> unProcessedItems = new List<String>();
                unProcessedItems.Add("Kate");
                unProcessedItems.Add("Sam");
                unProcessedItems.Add("Paul");
                unProcessedItems.Add("Violeta");
    
                foreach (string record in unProcessedItems)
                {
                    // convert text to wav
                    ConvertStringToSpeechWav(record, "c:/temp/" + record + ".wav", Vendor.VOICEWARE, Gender.MALE, Languages.ENGLISH);
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    

    注意,我还将列表从 ArrayList -&gt; List&lt;String&gt; 更改为最佳实践,因为 List(T) 的性能优于 ArrayList 并且是类型安全的。我还删除了if (unProcessedItems != null check),因为您已经在实例化上面的列表,所以它要么不为空,要么抛出异常。

    最后,每次调用 ConvertStringToSpeechWav() 时,您都会创建一个新的语音对象:

    voice = new SpVoice();
    

    并让 GC 清理它。您是否尝试过像上面建议的 PauloPinto 那样调用 GC.Collect() 来查看它是否有效?您不必为了让某些东西工作而坚持严格的编码原则。目标应该始终是编写干净且有原则的代码,但更重要的是让您的代码处于工作状态,然后根据需要进行重构。

    我希望这会有所帮助。

    干杯。

    【讨论】:

    • 感谢 cmets。我使用java,所以我不太确定从静态主调用非静态方法。我同意你的观点,ArrayList -&gt; List&lt;String&gt; 更好,因为它是类型安全的。我已经进行了空检查,因为理想情况下数据将来自数据库,但我必须调整代码以进行发布。我还没有尝试过 GC.Collect()。当我有机会时,我会试一试。但是看到您提到我正在为每个调用创建一个 SpVoice() 对象,我开始认为我应该只创建一个对象并重新使用该对象来创建所有文件?
    • GC.Collect() 为我工作。我知道这并不理想,但我的解决方案现在正在运行。谢谢。
    【解决方案2】:

    自从我做 TTS 以来已经有一段时间了,但我记得 Speak 方法是异步的,所以后续调用可能在第一次播放时被阻塞。

    您似乎是通过使用“SpeechVoiceSpeakFlags.SVSFlagsAsync”标志明确执行此操作的,因此请先尝试更改。

    【讨论】:

    • 感谢您对 Doobi 的评论。在查看 MSDN 后,我将代码更改为:SpeechVoiceSpeakFlags.SVSFDefault。不幸的是,这也会产生相同的结果。不知何故,没有生成后续文件。
    【解决方案3】:

    我遇到了类似的问题,除了我使用的是来自不同供应商(不是 NeoSpeech)的声音,而且该问题仅在成功生成大约 300 个左右的 wav 文件后才出现。

    但症状是一样的:所有不起作用的 wav 文件的大小都小于 1K。

    我注意到将失败的行移到列表顶部仍然会产生类似的结果:最初的 300 左右行成功了(即使其中一些行在之前的运行中失败了)。所以问题不在于行本身,而在于处理了多少。

    我找不到任何“重置”语音系统的方法,所以我尝试每 100 行调用一次垃圾收集器。成功了!

    所以我建议你试试:

    GC.Collect();

    在您的 ConvertStringToSpeechWav 函数的末尾。

    【讨论】:

    • 对 GC 来说并不是一个很好的做法。收集每“一些”行,我怀疑它是否真的解决了问题,如果确实解决了问题,那么代码应该有些奇怪。
    • 感谢保罗的评论。这次我想站在戴维德一边。我可以理解,在生产期限内,您会认为任何解决方案都是可以接受的,但这不是解决问题的正确方法。我将继续寻找正确而优雅的问题解决方案,即使 GC.Collect() 暂时解决了您的问题,我也建议您这样做。 @DavidePiras 我已经在上面发布了我的代码,想知道您是否可以查看它并让我知道我的代码是否有问题。谢谢两位,您的时间!
    • @DavidePiras 我同意这不是“好的做法”,但我仍然会说工作程序比非工作程序更好。调用 GC 解决了问题的事实表明,MS-SAPI SDK 没有正确释放隐藏的资源。所以不好的做法可能来自内心深处。 @karthic 那么我的建议没有用吗?我明白你为什么不想在生产环境中使用它,但它可能会给你另一个线索,说明出了什么问题。
    • 我运行的是 64 位笔记本电脑,声音是 32 位的,所以我唯一可以进行测试的地方就是实际的服务器。我和我的首席技术官谈过这个问题,他甚至拒绝让我尝试:)(这是意料之中的)。我们现在使用 AT&T 语音,这些语音与我上面的代码配合得很好。我现在有理由相信我从客户那里得到的声音是旧的或错误的。也许你也应该尝试其他声音。我认为 SAPI SDK 没有错误。做得不好的总是第三方供应商。
    猜你喜欢
    • 2010-09-10
    • 2010-11-26
    • 1970-01-01
    • 2013-01-17
    • 2016-07-22
    • 2023-03-13
    • 2011-08-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多