Подтвердить что ты не робот

Как найти аудиоформат выбранного голоса SpeechSynthesizer

В текстовом приложении с помощью С# я использую класс SpeechSynthesizer, он имеет событие с именем SpeakProgress, которое запускается для каждого произносимого слова. Но для некоторых голосов параметр e.AudioPosition не синхронизируется с выходным аудиопотоком, а выходной файл волны воспроизводится быстрее, чем показывает эта позиция (см. этот связанный вопрос).

В любом случае, я пытаюсь найти точную информацию о скорости передачи битов и другой информации, относящейся к выбранному голосу. Как я уже сказал, если я могу инициализировать волновой файл с этой информацией, проблема синхронизации будет решена. Однако, если я не могу найти такую ​​информацию в SupportedAudioFormat, я не знаю другого способа их найти. Например, голос "Microsoft David Desktop" не поддерживает поддерживаемый формат в VoiceInfo, но, похоже, он поддерживает формат PCM 16000 hz, 16 бит.

Как найти аудиоформат выбранного голоса SpeechSynthesizer

 var formats = CurVoice.VoiceInfo.SupportedAudioFormats;

 if (formats.Count > 0)
 {
     var format = formats[0];
     reader.SetOutputToWaveFile(CurAudioFile, format);
 }
 else
 {
        var format = // How can I find it, if the audio hasn't provided it?           
        reader.SetOutputToWaveFile(CurAudioFile, format );
}
4b9b3361

Ответ 1

Обновление:. Этот ответ был изменен после исследования. Первоначально я предлагал из памяти, что SupportedAudioFormats, скорее всего, только из (возможно, неправильно сконфигурированных) данных реестра; расследование показало, что для меня, в Windows 7, это определенно так, и подкрепляется в Windows 8.

Проблемы с поддержкойAudioFormats

System.Speech обертывает почтенный COM-речевой API (SAPI), а некоторые голоса - от 32 до 64 бит или могут быть неправильно сконфигурированы (в 64-битном реестре машин, HKLM/Software/Microsoft/Speech/Voices vs HKLM/Software/Wow6432Node/Microsoft/Speech/Voices.

Я указал ILSpy на System.Speech и его класс VoiceInfo, и я уверен, что SupportedAudioFormats происходит исключительно из данных реестра, следовательно, можно получить нулевые результаты при перечислении SupportedAudioFormats, если ваша TTS движок неправильно зарегистрирован для вашего приложения. Платформа target (x86, Any или 64 bit) или если поставщик просто не предоставляет эту информацию в реестре.

Голоса могут поддерживать разные, дополнительные или меньшие форматы, чем до речевого движка (кода), а не реестра (данных). Так что это может быть выстрел в темноте. Стандартные голоса в Windows часто бывают более последовательными в этом отношении, чем голоса третьих лиц, но они по-прежнему не всегда с пользой обеспечивают SupportedAudioFormats.

Поиск этой информации на жестком пути

Я нашел, что все еще можно получить текущий формат текущего голоса, но это зависит от отражения для доступа к внутренним файлам оберток SAPI System.Speech.

Следовательно, это довольно хрупкий код! И я бы не рекомендовал использовать его в производстве.

Примечание: приведенный ниже код требует, чтобы вы вызывали Speak() один раз для настройки; потребуется больше вызовов для принудительной настройки без Speak(). Тем не менее, я могу назвать Speak("") сказать ничего, и это работает нормально.

Реализация:

[StructLayout(LayoutKind.Sequential)]
struct WAVEFORMATEX
{
    public ushort wFormatTag;
    public ushort nChannels;
    public uint nSamplesPerSec;
    public uint nAvgBytesPerSec;
    public ushort nBlockAlign;
    public ushort wBitsPerSample;
    public ushort cbSize;
}

WAVEFORMATEX GetCurrentWaveFormat(SpeechSynthesizer synthesizer)
{
    var voiceSynthesis = synthesizer.GetType()
                                    .GetProperty("VoiceSynthesizer", BindingFlags.Instance | BindingFlags.NonPublic)
                                    .GetValue(synthesizer, null);

    var ttsVoice = voiceSynthesis.GetType()
                                 .GetMethod("CurrentVoice", BindingFlags.Instance | BindingFlags.NonPublic)
                                 .Invoke(voiceSynthesis, new object[] { false });

    var waveFormat = (byte[])ttsVoice.GetType()
                                     .GetField("_waveFormat", BindingFlags.Instance | BindingFlags.NonPublic)
                                     .GetValue(ttsVoice);

    var pin = GCHandle.Alloc(waveFormat, GCHandleType.Pinned);
    var format = (WAVEFORMATEX)Marshal.PtrToStructure(pin.AddrOfPinnedObject(), typeof(WAVEFORMATEX));
    pin.Free();

    return format;
}

Использование:

SpeechSynthesizer s = new SpeechSynthesizer();
s.Speak("Hello");
var format = GetCurrentWaveFormat(s);
Debug.WriteLine($"{s.Voice.SupportedAudioFormats.Count} formats are claimed as supported.");
Debug.WriteLine($"Actual format: {format.nChannels} channel {format.nSamplesPerSec} Hz {format.wBitsPerSample} audio");

Чтобы протестировать его, я переименовал раздел Microsoft Anna AudioFormats в раздел HKLM/Software/Wow6432Node/Microsoft/Speech/Voices/Tokens/MS-Anna-1033-20-Dsk/Attributes, в результате чего SpeechSynthesizer.Voice.SupportedAudioFormats не было элементов при запросе. Ниже приведен вывод в этой ситуации:

0 formats are claimed as supported.
Actual format: 1 channel 16000 Hz 16 audio