Как мне использовать лексикон с SpeechSynthesizer?

Я выполняю некоторые преобразования текста в речь, и я хотел бы указать некоторые специальные произношения в файле лексикона. Я выполнил дословный пример MSDN AddLexicon, и он произносит предложение, но не использует данную лексику, что-то, похоже, не работает.

Вот приведенный пример:

using System;
using Microsoft.Speech.Synthesis;

namespace SampleSynthesis
{
  class Program
  {
    static void Main(string[] args)
    {

      // Initialize a new instance of the SpeechSynthesizer.
      using (SpeechSynthesizer synth = new SpeechSynthesizer())
      {

        // Configure the audio output. 
        synth.SetOutputToDefaultAudioDevice();

        PromptBuilder builder = new PromptBuilder();
        builder.AppendText("Gimme the whatchamacallit.");

        // Append the lexicon file.
        synth.AddLexicon(new Uri("c:\\test\\whatchamacallit.pls"), "application/pls+xml");

        // Speak the prompt and play back the output file.
        synth.Speak(builder);
      }

      Console.WriteLine();
      Console.WriteLine("Press any key to exit...");
      Console.ReadKey();
    }
  }
}

и файл лексики:

<lexicon version="1.0" 
      xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xsi:schemaLocation="http://www.w3.org/2005/01/pronunciation-lexicon 
        http://www.w3.org/TR/2007/CR-pronunciation-lexicon-20071212/pls.xsd"
      alphabet="x-microsoft-ups" xml:lang="en-US">


  <lexeme>
    <grapheme> whatchamacallit </grapheme>
    <phoneme> W S1 AX T CH AX M AX K S2 AA L IH T </phoneme>
  </lexeme>

</lexicon>

Откроется консоль, произнесен текст, но новое произношение не используется. Я конечно сохранил файл в c:\test\whatchamacallit.pls как указано.

Я пробовал варианты Uri и местоположение файла (например, @"C:\Temp\whatchamacallit.pls", @"file:///c:\test\whatchamacallit.pls"), абсолютные и относительные пути, копирование в папку сборки и т. д.

Я запустил Process Monitor, и файл не доступен. Если бы это была проблема с правами доступа к каталогу / файлу (а это не так), я бы все равно увидел сообщения об отказе в доступе, однако я не регистрирую никаких ссылок вообще, кроме случайных в моем текстовом редакторе. Я вижу файл, к которому обращаются при попытке File.OpenRead,

К сожалению, при использовании мусорного Uri сообщения об ошибках отсутствуют.

При дальнейшем исследовании я понял, что этот пример взят из Microsoft.Speech.Synthesis, тогда как здесь я использую System.Speech.Synthesis. Однако из того, что я могу сказать, они идентичны, за исключением некоторой дополнительной информации и примеров, и оба указывают на одну и ту же спецификацию. Может ли это все еще быть проблемой?

Я проверил, что проект настроен на использование правильной.NET Framework 4.

Я сравнил пример из MSDN с примерами из упомянутой спецификации, а также попробовал их напрямую, но это не помогло. Учитывая, что файл не доступен, я не удивлен.

(Я могу использовать PromptBuilder.AppendTextWithPronunciation просто отлично, но это плохая альтернатива для моего случая использования.)

Пример на MSDN не работает? Как мне использовать лексикон с SpeechSynthesizer?

1 ответ

Решение

После многих исследований и ловушек я могу заверить вас, что ваше предположение просто неверно. По какой-то причине System.Speech.Synthesis.SpeechSynthesizer.AddLexicon() добавляет лексикон во внутренний список, но не использует его вообще. Похоже, никто не пытался использовать его раньше, и эта ошибка осталась незамеченной.

Microsoft.Speech.Synthesis.SpeechSynthesizer.AddLexicon() (который принадлежит Microsoft Speech SDK), с другой стороны, работает должным образом (передает лексикон COM-объекту, который интерпретирует его как объявленный).

Пожалуйста, обратитесь к этому руководству о том, как установить SDK: http://msdn.microsoft.com/en-us/library/hh362873%28v=office.14%29.aspx

Заметки:

  • люди сообщили, что 64-разрядная версия вызывает исключения COM (поскольку библиотека не устанавливается правильно), я подтвердил это на 64-разрядной машине с Windows 7
    • использование версии x86 обходит проблему
  • обязательно установите среду выполнения до SDK
  • также убедитесь, что вы установили язык выполнения (как указано на связанной странице), поскольку в SDK не используется системный речевой механизм по умолчанию

Вы можете использовать System.Speech.Synthesis.SpeechSynthesizer.SpeakSsml() вместо лексики.

Этот код изменяет произношение "синего" на "желтый" и "собака" на "рыба".

SpeechSynthesizer synth = new SpeechSynthesizer();
string text = "This is a blue dog";
Dictionary<string, string> phonemeDictionary = new Dictionary<string, string> { { "blue", "jelow" }, { "dog", "fyʃ" } };
foreach (var element in phonemeDictionary)
{
   text = text.Replace(element.Key, "<phoneme ph=\"" + element.Value + "\">" + element.Key + "</phoneme>");
}
text = "<speak version=\"1.0\" xmlns=\"http://www.w3.org/2001/10/synthesis\" xml:lang=\"en-US\">" + text + "</speak>";
synth.SpeakSsml(text);

Я недавно немного разбирался в этом на Windows 10.

Я обнаружил две вещи.

Любой Голос, который вы используете, должен соответствовать языку в файле Lexicon. Внутри лексикона у вас есть язык:

<lexicon version="1.0"
      xmlns="http://www.w3.org/2005/01/pronunciation-lexicon"
      alphabet="x-microsoft-ups" xml:lang="en-US">

Я обнаружил, что могу назвать свой Лексикон "blue.en-US.pls" и сделать копию с помощью "blue.en-GB.pls". Внутри будет xml:lang="en-GB"

В коде вы будете использовать:

string langFile = Path.Combine(_appPath, $"blue.{synth.Voice.Culture.IetfLanguageTag}.pls");
synth.AddLexicon(new Uri(langFile), "application/pls+xml");

Еще одна вещь, которую я обнаружил, это то, что она вообще не работает с "Microsoft Zira Desktop - English (United States)". Я не знаю почему. Похоже, что это голос по умолчанию в Windows 10.

Получите доступ к голосу по умолчанию и измените его здесь: %windir%\system32\speech\SpeechUX\SAPI.cpl

В противном случае вы сможете установить его с помощью кода:

var voices = synth.GetInstalledVoices();
var voice = voices.First(v => v.VoiceInfo.Name.Contains("David"));   // US. David, Hazel, Zira
synth.SelectVoice(voice.VoiceInfo.Name);

У меня есть Дэвид (Соединенные Штаты) и Хейзел (Великобритания), и он отлично работает с любым из них.

Другие вопросы по тегам