Невозможно создать НЕСКОЛЬКО WAV-файлов TTS с помощью MS-SAPI 5.1 на C #

Привет, ребята!

Я работаю над проектом, в котором мне нужно будет создавать файлы WAV имен с использованием TTS.

У меня установлен MS-SAPI 5.1 SDK на Windows Server 2003, и я использую C # для написания программы TTS. Помимо голоса Microsoft Sam по умолчанию, у меня на сервере установлены голоса из NeoSpeech TTS.

У меня возникла проблема: программа не создает более 1 рабочего файла WAV.

Чтобы быть более конкретным, если я отправлю программе 4 имени, программа создаст 4 файла WAV. Однако правильно конвертируется только имя. Размер файла превышает 1 КБ, и файл также воспроизводится в медиаплеере.

Остальные 3 файла созданы, но имеют размер 1 КБ и не работают ни в одном медиаплеере.

Я новичок как в 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.

Означает ли это, что мой код правильный и что-то не так с голосами? Во-первых, я получил голос от своих клиентов, поэтому я ничего не могу с этим поделать. Во-вторых, это голоса, готовые к производству. Я почти уверен, что они хорошо протестированы, иначе мы бы много о нем слышали.

Я все еще склонен верить, что с кодом, который я написал, что-то не так.

Есть ли другие предложения? Я здесь по-настоящему поправляюсь, и любая помощь будет оценена по достоинству.


person k427h1c    schedule 05.12.2010    source источник


Ответы (3)


Хотя я не вижу ничего явного, что вызывает проблему с 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 -> List<String>, потому что List (T) работает лучше, чем ArrayList, и является типобезопасным. Я также удалил if (unProcessedItems != null check), поскольку вы уже создаете экземпляр списка выше, поэтому он либо не будет нулевым, либо вызовет исключение.

Наконец, вы создаете новый голосовой объект каждый раз, когда вызывается ConvertStringToSpeechWav():

voice = new SpVoice();

и позволяя GC очистить его. Вы пробовали вызвать GC.Collect (), как предложил PauloPinto выше, просто чтобы посмотреть, работает ли он? Необязательно придерживаться жестких принципов кодирования только для того, чтобы что-то заработало. Цель всегда должна заключаться в том, чтобы код был чистым и принципиальным, но в большей степени - привести код в рабочее состояние, а затем провести рефакторинг по мере необходимости.

Надеюсь, что-то из этого поможет.

Ваше здоровье.

person Daniel R.    schedule 19.02.2011
comment
Спасибо за комментарии. Я использую java, поэтому я не слишком уверен в вызове нестатических методов из static main. И я согласен с вами, что ArrayList -> List<String> лучше, потому что он безопасен по типу. Я поставил эту нулевую проверку, потому что в идеале данные должны поступать из БД, но мне пришлось адаптировать код для публикации. Я не пробовал GC.Collect (). Я попробую, когда у меня будет возможность. Но, увидев, что вы упомянули, что я создаю объект SpVoice () для каждого вызова, я начал думать, что может быть мне просто создать объект один раз и повторно использовать этот объект для создания всех файлов? - person k427h1c; 22.02.2011
comment
GC.Collect () работал у меня. Я знаю, что это не идеально, но теперь мое решение работает. Спасибо. - person Nick; 24.01.2012

Прошло некоторое время с тех пор, как я делал TTS, но насколько я помню, метод Speak является асинхронным, поэтому последующие вызовы, вероятно, блокируются во время воспроизведения первого.

Похоже, вы делаете это явно, используя флаг SpeechVoiceSpeakFlags.SVSFlagsAsync, поэтому сначала попробуйте изменить его.

person Doobi    schedule 05.12.2010
comment
Спасибо за ваш комментарий, Дуби. Я изменил код на этот после просмотра MSDN: SpeechVoiceSpeakFlags.SVSFDefault. К сожалению, это дает тот же результат. Каким-то образом последующие файлы не создаются. - person k427h1c; 05.12.2010

У меня была аналогичная проблема, за исключением того факта, что я использовал голоса от другого поставщика (не NeoSpeech), и что проблема появилась только после создания примерно 300 или около того успешных файлов wav.

Но симптом был тот же: все файлы wav, которые не работали, были размером менее 1 КБ.

Я заметил, что перемещение неудачных строк в верхнюю часть списка по-прежнему дает аналогичный результат: начальные 300 или около того строк были успешными (хотя некоторые из этих строк не прошли в предыдущем запуске). Таким образом, проблема заключалась не в самих строках, а в том, сколько их было обработано.

Я не мог найти никакого способа «сбросить» речевую систему, поэтому я пытался вызывать сборщик мусора каждые 100 строк. Это сработало!

Поэтому я предлагаю вам попробовать:

GC.Collect();

в конце вашей функции ConvertStringToSpeechWav.

person Paulo Pinto    schedule 18.02.2011
comment
не очень хорошая практика для GC.Collect каждые несколько строк, я сомневаюсь, что это действительно решает проблему, и если это так, должно быть что-то странное с кодом. - person Davide Piras; 18.02.2011
comment
Спасибо за комментарий Пауло. На этот раз я бы хотел встать на сторону Давиде. Я понимаю, что при соблюдении сроков производства вы можете подумать, что любое решение приемлемо, но это неправильный способ решения проблемы. Я буду продолжать искать правильное и элегантное решение проблемы и предлагаю вам сделать то же самое, даже если GC.Collect () пока решает вашу проблему. @DavidePiras Я разместил свой код выше и хотел бы знать, можете ли вы взглянуть на него и сообщить мне, если что-то не так с моим кодом. Спасибо обоим за уделенное время! - person k427h1c; 18.02.2011
comment
@DavidePiras Я согласен, что это не очень хорошая практика, но все же я бы сказал, что рабочая программа лучше неработающей. Тот факт, что вызов GC решает проблему, предполагает наличие скрытых ресурсов, которые не освобождаются должным образом MS-SAPI SDK. Так что плохая практика, вероятно, исходит изнутри. @karthic Так мое предложение не сработало? Я понимаю, почему вы не хотите использовать его в производственной среде, но он может дать вам еще один ключ к пониманию того, что идет не так. - person Paulo Pinto; 18.02.2011
comment
Я использую 64-битный ноутбук, и голоса 32-битные, поэтому единственное место, где я могу проводить свои тесты, - это настоящий сервер. Я поговорил об этом со своим техническим директором, и он отказался позволить мне даже попробовать :) (что было ожидаемо). Теперь мы используем голоса AT&T, которые отлично работают с моим приведенным выше кодом. Теперь у меня есть причина полагать, что голоса, которые я слышал от клиента, были старыми или ошибочными. Может быть, стоит попробовать и другие голоса. Я не думаю, что в SAPI SDK есть ошибка. Это всегда сторонние поставщики, которые не очень хорошо работают. - person k427h1c; 20.02.2011