На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
Модераторы: RaD, nsh
  
> Sapi для HTS Voice, Sapi для HTS Voice и утилиты
    В настоящее время в Интернете доступен проект SALB. Описание с его главной стр.
    Проект SALB - это sapi-интерфейс для синтезаторов речи, использующих марковские модели голосов, созданных в HTS. Вы можете посмотреть Релиз на https://github.com/m-toman/SALB/releases и страницу проекта на github-е по адресу https://github.com/m-toman/SALB. И тд.

    Проект использует новые возможности компиляторра 11-й версии (auto и тд), поэтому собирается только в VC++ 2012 и выше.
    Я собрал в 2008Express свой проект Flite+hts на основе Flite 2001 года. sapi-драйвер одновременно воспроизводит английские голоса от RHVoice c разной частотой дескритизации (16 и 48кГц). "Допипиливается".

    Предлагаю исправленный вариант register_vox.cpp для обоих проектов (имя в Flite) и командные файлы для регистрации dll, а также для установки и
    удаления голосов.

    _register-alan.bat
    ExpandedWrap disabled
      rem запрос прав администратора, см. во вложении.
       
      cd %~dp0
      echo %CD%
       
      @echo on
      regsvr32 "%CD%\FliteCMUKalDiphone.dll"
      "%CD%\register-vox.exe" "%CD%\data\voices\Alan" "HTS Voice Alan" "en-us" "Female" "Adult" "%CD%\alan.log"
       
      @echo off
      pause


    _unregister_Alan.bat
    ExpandedWrap disabled
      rem запрос прав администратора, см. во вложении.
      cd %~dp0
      echo %CD%
       
      @echo on
       
      regsvr32 "/u" "%CD%\FliteCMUKalDiphone.dll"
      "%CD%\register-vox.exe" "/u" "HTS Voice Alan"
       
      @echo off
      pause

    Прикреплённый файлПрикреплённый файлregister_vox.rar (12,83 Кбайт, скачиваний: 113)
    Сообщение отредактировано: webcoder88 -
      Обновление для flite+hts_engine-1.02 до flite+hts_engine-1.02.02. Исходный код проекта flite+hts_engine можно взять на гитхабе.
      Командная строка для запуска:
      flite_hts_engine -v alan -p 240 -s 48000 -a 0.52 input.txt
      Прикреплённый файлПрикреплённый файлflite_hts_engine_1.02.02_upd.part01.rar (58,59 Кбайт, скачиваний: 159)
      Прикреплённый файлПрикреплённый файлflite_hts_engine_1.02.02_upd.part02.rar (52,79 Кбайт, скачиваний: 157)
        webcoder88, ты ты не забыл, что без ключа /D команда cd диск не меняет?

        Вторую строчку в обоих файлах надо писать
        ExpandedWrap disabled
          cd /D %~dp0
        Всё написанное выше это всего лишь моё мнение, возможно ошибочное.
          amk
          Я взял файлы из проекта SALB. :)
          Есть желающие добавить русский язык в парсер Flite. Код можно взять в RHVoice, но есть некоторые нюансы:
          -разработчица, Linux-разработчица, в Линуксе ничего нет видимо, но с др. стороны разработчики не могут разобраться;
          -разработчица переписала часть функций, чем упростила алгоритм обработки;
          -модель длительности берется из фестивалевской модели языка (каталог languages), без к-го не работают даже английские голоса;
          -используется "рыжий" фикс длительности;
          -можно сделать воспроизведение анси-текста на кодовой странице 1251, без уникода;
          -в RHVoice отсутствует обработка интонации, поэтому русские голоса похожи на монотонное бормотание.
          В RHVoice проделана большая работа, но результат еще не достигнут.
          Можно обойтись без файлов Фестиваля, доделать модель русского языка на с++, интонацию. Русские голоса должны воспроизводиться так же как и английские с качеством, близким к оригиналу (хотя бы для тренировочных предложений).

          Добавлено
          Я создавал метки RHVoice для русского голоса и воспроизводил их в hts_engine, который одинаково хорошо воспроизводи метки любого языка. Почти все гласные восп-ся с увеличенной длительностью. Замена исходного файла длительности на мой ничего не изменила. Из чего следует, что парсер RHVoice выдает некорректные метки.
          Вся необходимая информация для генерации голоса содержиться в файле меток. Это длительность, ударение, интонация. Можно попытаться подбрать вручную. :)
          Остальное зависит от качества тренированной HMM-модели.

          Добавлено
          Sapi-драйвер для английских голосов, без исх. кода. Может отсутствовать синхронизация выделенного и произносимого текста (в панели управления->речь), пока подделка. Тестировался только на x86, проверен на антивирусе, но лучше запускать на виртуалке.
          https://www.sendspace.com/file/hp1gbh
          Сообщение отредактировано: webcoder88 -
            Драйвер SAPI своими руками.

            Здесь приводится описание готового sapi-драйвера. На Харбар пока не тянет.

            Шаг 1.

            Прежде чем что-то создавать, стоит поискать готовое или почти готовое решение в Интернете. Это позволит съэкономить очень много времени.
            В настоящее время в Интернете можно легко найти проекты с открытым исходным кодом: FLite, RHVoice, SALB.
            Проект SALB - это sapi-интерфейс для синтезаторов речи, использующих марковские модели голосов и созданных в HTS. Вы можете посмотреть релиз на https://github.com/m-toman/SALB/releases и странице проекта на github-е по адресу https://github.com/m-toman/SALB.
            Данный проект работает с файлом голоса, сохраненным в формате hts_engine_API версии 1.08...1.10 (модель голоса одним файлом), но для совместимости с голосами RHVoice нужно применить версию 1.05..06.
            Для сборки проекта потребуются следующие компоненты:
            - WinDDK версии 7.1.0 или выше для сборки sapi-драйвера.
            - ATL (входит в состав WinDDK);
            - Студия VC++ 2012...2015.
            Поддериваются аглийские и немецкие голоса (на уровне языковой спецификации, есть документация).

            RHVoice - русский синтезатор речи с открытым кодом, но разработан в среде LInux.
            Для сборки потребуются следующие компоненты:
            - база русской речи и спецификация языка, https://developer.berlios.de/projects/festlang;
            - HTS скрипты для тренировки голоса, http://hts.sp.nitech.ac.jp;
            - hts_engine API для синтеза речи в реальном времени, http://hts-engine.sourceforge.net/;
            - анализатор текста Flite, http://www.speech.cs.cmu.edu/flite;
            - пакет русской лексики от Игоря Порецкого, http://poretsky.homelinux.net/packages/
            - библиотека libunistring для работы с уникодом, http://www.gnu.org/software/libunistring/;
            - библиотека sonic для преобразования форматов звуковых файлов и синтеза быстрой речи, git://vinuxproject.org/sonic
            - парсер expat XML для поддержки SSML, http://expat.sourceforge.net
            - библиотека PCRE (версии 8.10 или выше), http://pcre.org;
            - libsox для некоторых задач постобработки, http://sox.sourceforge.net;

            - WinDDK версии 7.1.0 или выше для сборки sapi-драйвера.
            - Студия VC++ 2008...2015
            - sygwin для эмуляции командного процессора Linux;
            - совместимые версии phiton и scons для запуска скриптов сборки проекта и установки голоса.
            Скорее всего, это не предел по кол-ву библиотек и компонентов и могло быть еще больше, просто у разработчицы не хватило фантазии.

            FLIte - это проект 2001 года с открытым исходным кодом и проектом sapi-драйвера на ATL. Максимальная совместимость и минимум классов.
            К недостаткам относятся: механический голос и поддержка спецификации английского языка на уровне кода.
            Для сборки проекта потребуются следующие компоненты:
            - WinDDK версии 7.1.0 или выше для сборки sapi-драйвера.
            - ATL (входит в состав WinDDK);
            - Студия VC++ 6.0...2015.

            Я остановил свой выбор на проекте sapi Flite.
            Для начала стоит Загрузить архив flite-1.2-release.tar.bz2 со страницы проекта и разархивировать в любой каталог. Далее необходи открыть проект flite+hts_engine-1.02/sapi в студии VC++
            2008...2015. Новая студия предложит преобразование проекта, тк он создан в VC++ 6.0. После преобразования проект можно собрать и проверить работу полученного дифонного движка. Регистрацию драйвера выполяет сама студия. См. события после построения.

            Следующий шаг - интеграция hts_engine_API версии 1.06 и удаление ненужных файлов.
            Сообщение отредактировано: webcoder88 -
              Шаг 2.

              Преже всего, необходимо переделать проект flite+hts_engine-1.02 в flite+hts_engine-1.02.02, т.е добавить в него новую функцию LoadeVoice(), далее скопировать каталог Sapi из проекта Flite в каталог проекта flite+hts_engine-1.02.02.
              Тк каталоги проектов имеют разную структуру, то вам потребуется исправить пути к файлам во всех проектах решения. Это можно сделать проще. Все файлы Flite компактно расположены в одноименном каталоге. Например, берете проект cmu_us_kal. Удалите все Source-файлы проекта, а потом выделите файлcmu_us_kal.c в каталоге \flite\lang\cmu_us_kal (Кроме него там должен быть файл voxdefs.h), перенесите и отпустите на значек каталога Source Files в Обозревателе решения.
              Аналогично исправьте все проекты в Обозревателе решения сверху вниз, исключая все файлы в Обозревателе решения и перенося все Header- и Source-файлы из соответствующих каталогов.
              Состав каталога flite\lang:
              cmu_us_kal
              cmulex
              usenglish
              Проект register-vox можно оставить без изменения.
              Далее в решение следует добавить новый пустой проект hts_engine_API, сняв галочку создать отдельный каталог проекта. В корневом каталоге Sapi должен появиться еще один каталог проекта hts_engine_API. Добавьте в проект заголовочные и исходные файлы через Обозреватель решения, путем переноса из каталога flite+hts_engine-1.02.02s_ok\hts_engine_API\lib
              Далее необходимо заменить 1 include-файл, тк драйвер использует более старую версию файла \flite\include\cst_tokenstream.h. Переименуйте его в cst_tokenstream_new.h и скопируйте одноименный файл из исходного проекта Flite 2001 года.
              Для сборки драйвера Свойства всех проектов должны иметь одинаковую настройку компилятора: Создание кода-библиотека времени выполнения-Многопоточная(MT), тначе не соберется из-за конфликта библиотек (иногда можно собрать, если исключать конфликтные
              библиотеки в настройках Линкера, но это лишнее).
              Проекты, относящиеся к flite и hts_engine_API должны собраться без проблем. В проекте
              FliteTTSEngineObj необходимо исправить код, в определении класса и самом классе.
              Самый простой путь - закоментировать все строки, вызывающие ошибку.

              В каждом проекте нужно прописать пути к заголовочным файлам, указать библиотеку времени выполнения MT и выходные каталоги для готовых библиотек.
              Начнем с поекта cmulex, тк он не зависит от других проектов.
              Идете в свойства проекта cmulex (правый щелчек в Обозревателе), Компилятор(С/С++) - Общие - Дополнительные каталоги включения: ..\..\flite\include; ..\..\flite\lang\usenglish\;..\..\flite\src\synth
              Это относительные пути, что намного короче и профессиональнее, чем абсолютные пути.
              ..\ говорит компилятору поднятся на один каталог выше, а ..\..\ - поднятся на два уровня выше.
              А как узнать, насколько нужно подниматься? Все просто. Каталог этого проекта расположен в flite+hts_engine-1.02.02\sapi\usenglish. Чтобы добраться до файлов включения, нужно подняться на 1 уровень, в каталог flite+hts_engine-1.02.02\sapi, где находятся каталоги всех проектов. Это только проекты, а их заголовочные и исходные файлы расположены в соответствующих подкаталогах в flite+hts_engine-1.02.02.
              Таким образом, параметр ..\..\flite\include; говорит компилятору поднятся от текущего пути на 2 уровня, зайти в каталог flite, далее зайти в его подкаталог include и искать здесь заголовочные файлы.
              Теперь параметры Библиотекаря. Общие - Выходной файл - .\Release\usenglish.lib
              .\ - текущий каталог.
              Аналогично изменяются параметры остальных проектов.
              hts_engine_API:
              Компилятор.
              ..\..\hts_engine_API\include
              Линкер.
              .\Release\$(ProjectName).lib

              flite:
              ..\..\include;..\..\flite\include
              .\Release\flite.lib

              cmulex:
              ..\..\include;..\..\flite\include
              .\Release\cmulex.lib.\Release\cmulex.lib

              cmu_us_kal:
              ..\..\flite\include,..\..\flite\lang\cmulex,..\..\flite\lang\usenglish
              .\Release\cmu_us_kal.lib

              FliteTTSEngineObj:
              Компилятор.
              ..\..\flite\include;..\..\flite\src\utils;..\..\hts_engine_API\include;E:\WinDDK\7600.16385.1\inc\atl71;..\..\include
              Линкер.
              Выходной файл .\Release\FliteTTSEngineObj.lib
              Дополнительные зависимости cmu_us_kal.lib ..\flite\Release\flite.lib ..\hts_engine_API\Release\hts_engine_API.lib
              Дополнительные каталоги библиотек "..\..\lib";"..\cmu_us_kal\Release";E:\WinDDK\7600.16385.1\lib\ATL\i386

              FliteCMUKalDiphone:
              Компилятор.
              ..\..\include;..\..\hts_engine_API\include;E:\WinDDK\7600.16385.1\inc\atl71;..\..\flite\include;..\..\flite\lang\cmu_us_kal;..\flitettsengineobj
              Линкер.
              Выходной файл .\Release\FliteTTSEngineObj.lib
              Дополнительные зависимости cmu_us_kal.lib ..\flite\Release\flite.lib ..\hts_engine_API\Release\hts_engine_API.lib
              Дополнительные каталоги библиотек "..\..\lib";"..\cmu_us_kal\Release";E:\WinDDK\7600.16385.1\lib\ATL\i386


              Путь E:\WinDDK\7600.16385.1\lib\ATL\i386 исправьте на свой.
              Сообщение отредактировано: webcoder88 -
                register-vox:
                Компилятор.
                E:\WinDDK\7600.16385.1\inc\atl71
                Компоновщик.
                ../Release/register-vox.exe
                E:\WinDDK\7600.16385.1\lib\ATL\i386

                Новый драйвер будет читать параметры и путь к файлам голоса из реестра, поэтому начнем с проекта register-vox. Заменим код файла register-vox.сpp на немного исправленный код из проекта SALB, тк лучше алгоритм. Кроме того, запись параметров производится одновременно и в ключ реестра и в его подключ Attributes. Это сильно облегчит извлечение пути во время инициализации sapi-драйвера.

                После сборки и запуска _register-alan.bat в реестре появится новый ключ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\HTS Voice Alan
                Экспорт ключа в текстовый файл:
                ExpandedWrap disabled
                  Windows Registry Editor Version 5.00
                   
                  [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\HTS Voice Alan]
                  "CLSID"="{72CEF72B-809E-4CB4-A63C-ADC7938CFD5B}"
                  @="HTS Voice Alan"
                  "409"="HTS Voice Alan"
                  "Language"="en-us"
                  "Voxpath"="С:\\flite+hts_engine-1.02.02\\sapi2008\\FliteCMUKalDiphone\\Release\\data\\voices\\Alan"
                  "Textrules"=""
                  "Logfile"="С:\\_flite+hts_engine-1.02.02\\sapi2008\\FliteCMUKalDiphone\\Release\\alan.log"
                   
                  [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\HTS Voice Alan\Attributes]
                  "Gender"="Female"
                  "Name"="HTS Voice Alan"
                  "Language"="409"
                  "Age"="Adult"
                  "Vendor"="FTW"



                Далее можно переходить к сборке FliteTTSEngineObj.
                В FliteTTSEngineObj.h добавляется строка #include "flite_hts_engine.h" и коментируется строка flite_init(); --> //flite_init();

                В опр. класса после деструктора ~ добавляются новые методы и данные:
                ExpandedWrap disabled
                  // Flite+HTS methods
                  public:
                  static void Flite_HTS_Engine_create_label(Flite_HTS_Engine * f, cst_item * item, char *label);
                  void Flite_HTS_Engine_initialize(Flite_HTS_Engine * f,
                                                   HTS_Boolean use_lpf,
                                                   int sampling_rate,
                                                   int fperiod,
                                                   double alpha,
                                                   int stage,
                                                   double beta,
                                                   int audio_buff_size,
                                                   double uv_threshold,
                                                   HTS_Boolean use_log_gain,
                                                   double gv_weight_mgc, double gv_weight_lf0,
                                                   double gv_weight_lpf);
                   
                  void Flite_HTS_Engine_load_voice(Flite_HTS_Engine * f,  char* voice_path);
                  void Flite_HTS_Engine_clear(Flite_HTS_Engine * f);
                   
                  /* ISpObjectWithToken methods */
                  public:
                      STDMETHODIMP SetObjectToken(ISpObjectToken * pToken);
                      STDMETHODIMP GetObjectToken(ISpObjectToken** ppToken)
                          { return SpGenericGetObjectToken(ppToken, vox_token); }
                   
                  /* ISpTTSEngine methods */
                  public:
                      STDMETHOD(Speak)(DWORD dwSpeakFlags,
                               REFGUID rguidFormatId, const WAVEFORMATEX * pWaveFormatEx,
                               const SPVTEXTFRAG* pTextFragList, ISpTTSEngineSite* pOutputSite);
                      STDMETHOD(GetOutputFormat)(const GUID * pTargetFormatId,
                                     const WAVEFORMATEX * pTargetWaveFormatEx,
                                     GUID * pDesiredFormatId,
                                     WAVEFORMATEX ** ppCoMemDesiredWaveFormatEx);
                   
                   
                   
                      void get_actions_and_do_them();
                      HTS_Boolean HTS_Engine_create_gstream2(HTS_Engine * engine);
                      HTS_Boolean HTS_GStreamSet_create2(HTS_GStreamSet * gss, HTS_PStreamSet * pss, int stage,
                          HTS_Boolean use_log_gain, int sampling_rate, int fperiod, double alpha, double beta,
                          HTS_Boolean * stop, double volume, HTS_Audio * audio);
                   
                  /* Implementation stuff */
                  protected:
                      /* These get set by a subclass's constructor.  That's not the
                             proper C++ way to do this, but I do not care. */
                      cst_voice *(*regfunc)(const char *);
                      void (*unregfunc)(cst_voice *);
                      int (*phonemefunc)(cst_item *);
                      int (*visemefunc)(cst_item *);
                      int (*featurefunc)(cst_item *);
                      cst_val *(*pronouncefunc)(SPPHONEID *);
                   
                      /* SAPI's use of the term "token" is quite unfortunate. */
                      CComPtr<ISpObjectToken> vox_token;
                      CComPtr<ISpObjectToken> attr_token;
                      cst_voice *curr_vox;
                   
                      /* Synthesis state variables */
                      cst_utterance *curr_utt;
                      cst_relation *tok_rel;
                      const SPVTEXTFRAG *curr_frag;
                      ISpTTSEngineSite *site;
                      ULONGLONG bcount;
                      int sentence_start;
                      int sentence_skip;
                      int aborted;
                   
                   
                      const char *sztoken;
                      cst_tokenstream *ts;
                   
                      FILE *file_list[num_hts_data_files];
                         /* engine */
                      //static
                      Flite_HTS_Engine engine;
                   
                   
                  private:
                   //...
                  static int open_hts_data_files(const char *path,FILE *file_list[num_hts_data_files]);
                  static void close_hts_data_files(FILE *file_list[num_hts_data_files]);



                В начале FliteTTSEngineObj.cpp после существующих инклудов нужно добавить код
                ExpandedWrap disabled
                  #include <clocale>
                   
                  #define MAXBUFLEN 1024
                   
                  #include "config.c"
                   
                  char buff[30];  //for debug output
                   
                  typedef struct
                  {
                    const char *name;
                    const char *open_mode;
                    int is_optional;
                  } hts_data_file_info;
                   
                   
                   
                  static const hts_data_file_info hts_data_list[num_hts_data_files]={
                    {"dur.pdf","rb",0},
                    {"tree-dur.inf","r",0},
                    {"mgc.pdf","rb",0},
                    {"tree-mgc.inf","r",0},
                    {"mgc.win1","r",0},
                    {"mgc.win2","r",0},
                    {"mgc.win3","r",0},
                    {"lf0.pdf","rb",0},
                    {"tree-lf0.inf","r",0},
                    {"lf0.win1","r",0},
                    {"lf0.win2","r",0},
                    {"lf0.win3","r",0},
                    {"lpf.pdf","rb",0},
                    {"tree-lpf.inf","r",0},
                    {"lpf.win1","r",0},
                    {"gv-mgc.pdf","rb",0},
                    {"tree-gv-mgc.inf","r",0},
                    {"gv-lf0.pdf","r",0},
                    {"tree-gv-lf0.inf","r",0},
                    {"gv-mgc-lpf.pdf","r",0},
                    {"tree-gv-lpf.inf","r",0},
                    {"gv-switch.inf","r",0},
                    {"voice.params","r",0},
                    {"voice.info","r",0}};
                   
                  static cst_val *sapi_tokentowords(cst_item *i);
                   
                  int CFliteTTSEngineObj::open_hts_data_files(const char *path,FILE *file_list[num_hts_data_files])
                  {
                   
                    int result=1;
                    int i,l;
                    
                    //printf("*****111***************\n\n");
                  #ifdef WIN32
                    char sep='\\';
                  #else
                    char sep='/';
                  #endif
                    char *full_path;
                    char *name;
                    memset(file_list,0,num_hts_data_files*sizeof(FILE*));
                    l=strlen(path);
                    if(l==0)
                      return 0;
                      
                        //printf("****222****************\n\n");
                    full_path=(char *)calloc(l+20,sizeof(char));
                    if(full_path==NULL)
                      return 0;
                    memcpy(full_path,path,l);
                    if(full_path[l-1]!=sep)
                      {
                        full_path[l]=sep;
                        name=full_path+l+1;
                      }
                    else
                      name=full_path+l;
                    for(i=0;i<num_hts_data_files;i++)
                      {
                        strcpy(name,hts_data_list[i].name);
                        //printf("lib.c filename = %s\n", name);
                        //printf("lib.c full_path = %s\n", full_path);
                        
                        file_list[i]=fopen(full_path,hts_data_list[i].open_mode);
                        //printf("lib.c file_list[%d] = %d\n", i, file_list[i]);
                        
                        if (i<15){ // error if no any file from first 14 files
                        if((file_list[i]==NULL)&&(!hts_data_list[i].is_optional))
                          {
                            result=0;
                            break;
                          }
                        }
                      }
                    free(full_path);
                    if(!result)
                      close_hts_data_files(file_list);
                    return result;
                  }
                   
                  void CFliteTTSEngineObj::close_hts_data_files(FILE *file_list[num_hts_data_files])
                  {
                    int i;
                    for(i=0;i<num_hts_data_files;i++)
                      {
                        if(file_list[i]!=NULL)
                          {
                            fclose(file_list[i]);
                            file_list[i]=NULL;
                          }
                      }
                  }
                   
                  typedef struct
                  {
                    int id;
                    HTS_Engine *engine;
                    int is_free;
                  } engine_resource;
                   
                   
                   
                  void CFliteTTSEngineObj::Flite_HTS_Engine_create_label(Flite_HTS_Engine * f, cst_item * item,
                                                            char *label)
                  {
                     char seg_pp[8];
                     char seg_p[8];
                     char seg_c[8];
                     char seg_n[8];
                     char seg_nn[8];
                     char endtone[8];
                     int sub_phrases = 0;
                     int lisp_total_phrases = 0;
                     int tmp1 = 0;
                     int tmp2 = 0;
                     int tmp3 = 0;
                     int tmp4 = 0;
                   
                     /* load segments */
                     strcpy(seg_pp, ffeature_string(item, "p.p.name"));
                     strcpy(seg_p, ffeature_string(item, "p.name"));
                     strcpy(seg_c, ffeature_string(item, "name"));
                     strcpy(seg_n, ffeature_string(item, "n.name"));
                     strcpy(seg_nn, ffeature_string(item, "n.n.name"));
                   
                     /* load endtone */
                     strcpy(endtone,
                            ffeature_string(item,
                                            "R:SylStructure.parent.parent.R:Phrase.parent.daughtern.R:SylStructure.daughtern.endtone"));
                   
                     if (strcmp(seg_c, "pau") == 0) {
                        /* for pause */
                        if (item_next(item) != NULL) {
                           sub_phrases =
                               ffeature_int(item,
                                            "n.R:SylStructure.parent.R:Syllable.sub_phrases");
                           tmp1 =
                               ffeature_int(item,
                                            "n.R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_syls");
                           tmp2 =
                               ffeature_int(item,
                                            "n.R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_words");
                           lisp_total_phrases =
                               ffeature_int(item,
                                            "n.R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_phrases");
                        } else {
                           sub_phrases =
                               ffeature_int(item,
                                            "p.R:SylStructure.parent.R:Syllable.sub_phrases");
                           tmp1 =
                               ffeature_int(item,
                                            "p.R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_syls");
                           tmp2 =
                               ffeature_int(item,
                                            "p.R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_words");
                           lisp_total_phrases =
                               ffeature_int(item,
                                            "p.R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_phrases");
                        }
                        sprintf(label,
                                "%s^%s-%s+%s=%s@x_x/A:%d_%d_%d/B:x-x-x@x-x&x-x#x-x$x-x!x-x;x-x|x/C:%d+%d+%d/D:%s_%d/E:x+x@x+x&x+x#x+x/F:%s_%d/G:%d_%d/H:x=x^%d=%d|%s/I:%d=%d/J:%d+%d-%d",
                                strcmp(seg_pp, "0") == 0 ? "x" : seg_pp,
                                strcmp(seg_p, "0") == 0 ? "x" : seg_p,
                                seg_c,
                                strcmp(seg_n, "0") == 0 ? "x" : seg_n,
                                strcmp(seg_nn, "0") == 0 ? "x" : seg_nn,
                                ffeature_int(item, "p.R:SylStructure.parent.R:Syllable.stress"),
                                ffeature_int(item, "p.R:SylStructure.parent.R:Syllable.accented"),
                                ffeature_int(item,
                                             "p.R:SylStructure.parent.R:Syllable.syl_numphones"),
                                ffeature_int(item, "n.R:SylStructure.parent.R:Syllable.stress"),
                                ffeature_int(item, "n.R:SylStructure.parent.R:Syllable.accented"),
                                ffeature_int(item,
                                             "n.R:SylStructure.parent.R:Syllable.syl_numphones"),
                                ffeature_string(item,
                                                "p.R:SylStructure.parent.parent.R:Word.gpos"),
                                ffeature_int(item,
                                             "p.R:SylStructure.parent.parent.R:Word.word_numsyls"),
                                ffeature_string(item,
                                                "n.R:SylStructure.parent.parent.R:Word.gpos"),
                                ffeature_int(item,
                                             "n.R:SylStructure.parent.parent.R:Word.word_numsyls"),
                                ffeature_int(item,
                                             "p.R:SylStructure.parent.parent.R:Phrase.parent.lisp_num_syls_in_phrase"),
                                ffeature_int(item,
                                             "p.R:SylStructure.parent.parent.R:Phrase.parent.lisp_num_words_in_phrase"),
                                sub_phrases + 1, lisp_total_phrases - sub_phrases,
                                endtone,
                                ffeature_int(item,
                                             "n.R:SylStructure.parent.parent.R:Phrase.parent.lisp_num_syls_in_phrase"),
                                ffeature_int(item,
                                             "n.R:SylStructure.parent.parent.R:Phrase.parent.lisp_num_words_in_phrase"),
                                tmp1, tmp2, lisp_total_phrases);
                     } else {
                        /* for no pause */
                        tmp1 = ffeature_int(item, "R:SylStructure.pos_in_syl");
                        tmp2 =
                            ffeature_int(item, "R:SylStructure.parent.R:Syllable.syl_numphones");
                        tmp3 = ffeature_int(item, "R:SylStructure.parent.R:Syllable.pos_in_word");
                        tmp4 =
                            ffeature_int(item,
                                         "R:SylStructure.parent.parent.R:Word.word_numsyls");
                        sub_phrases =
                            ffeature_int(item, "R:SylStructure.parent.R:Syllable.sub_phrases");
                        lisp_total_phrases =
                            ffeature_int(item,
                                         "R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_phrases");
                        sprintf(label,
                                "%s^%s-%s+%s=%s@%d_%d/A:%d_%d_%d/B:%d-%d-%d@%d-%d&%d-%d#%d-%d$%d-%d!%d-%d;%d-%d|%s/C:%d+%d+%d/D:%s_%d/E:%s+%d@%d+%d&%d+%d#%d+%d/F:%s_%d/G:%d_%d/H:%d=%d^%d=%d|%s/I:%d=%d/J:%d+%d-%d",
                                strcmp(seg_pp, "0") == 0 ? "x" : seg_pp, strcmp(seg_p,
                                                                                "0") ==
                                0 ? "x" : seg_p, seg_c, strcmp(seg_n, "0") == 0 ? "x" : seg_n,
                                strcmp(seg_nn, "0") == 0 ? "x" : seg_nn, tmp1 + 1, tmp2 - tmp1,
                                ffeature_int(item, "R:SylStructure.parent.R:Syllable.p.stress"),
                                ffeature_int(item, "R:SylStructure.parent.R:Syllable.p.accented"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.p.syl_numphones"),
                                ffeature_int(item, "R:SylStructure.parent.R:Syllable.stress"),
                                ffeature_int(item, "R:SylStructure.parent.R:Syllable.accented"),
                                tmp2, tmp3 + 1, tmp4 - tmp3, ffeature_int(item,
                                                                          "R:SylStructure.parent.R:Syllable.syl_in")
                                + 1, ffeature_int(item,
                                                  "R:SylStructure.parent.R:Syllable.syl_out") + 1,
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.ssyl_in") + 1,
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.ssyl_out") + 1,
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.asyl_in") + 1,
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.asyl_out") + 1,
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.lisp_distance_to_p_stress"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.lisp_distance_to_n_stress"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.lisp_distance_to_p_accent"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.lisp_distance_to_n_accent"),
                                ffeature_string(item,
                                                "R:SylStructure.parent.R:Syllable.syl_vowel"),
                                ffeature_int(item, "R:SylStructure.parent.R:Syllable.n.stress"),
                                ffeature_int(item, "R:SylStructure.parent.R:Syllable.n.accented"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.R:Syllable.n.syl_numphones"),
                                ffeature_string(item,
                                                "R:SylStructure.parent.parent.R:Word.p.gpos"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Word.p.word_numsyls"),
                                ffeature_string(item, "R:SylStructure.parent.parent.R:Word.gpos"),
                                tmp4, ffeature_int(item,
                                                   "R:SylStructure.parent.parent.R:Word.pos_in_phrase")
                                + 1, ffeature_int(item,
                                                  "R:SylStructure.parent.parent.R:Word.words_out"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Word.content_words_in")
                                + 1, ffeature_int(item,
                                                  "R:SylStructure.parent.parent.R:Word.content_words_out")
                                + 1, ffeature_int(item,
                                                  "R:SylStructure.parent.parent.R:Word.lisp_distance_to_p_content"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Word.lisp_distance_to_n_content"),
                                ffeature_string(item,
                                                "R:SylStructure.parent.parent.R:Word.n.gpos"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Word.n.word_numsyls"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.p.lisp_num_syls_in_phrase"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.p.lisp_num_words_in_phrase"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.lisp_num_syls_in_phrase"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.lisp_num_words_in_phrase"),
                                sub_phrases + 1, lisp_total_phrases - sub_phrases,
                                strcmp(endtone, "0") == 0 ? "NONE" : endtone,
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.n.lisp_num_syls_in_phrase"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.n.lisp_num_words_in_phrase"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_syls"),
                                ffeature_int(item,
                                             "R:SylStructure.parent.parent.R:Phrase.parent.lisp_total_words"),
                                lisp_total_phrases);
                     }
                  }




                Парметр "Voxpath" читается в функции
                ExpandedWrap disabled
                  STDMETHODIMP
                  CFliteTTSEngineObj::SetObjectToken(ISpObjectToken* pToken)
                  {
                      USES_CONVERSION;
                      CSpDynamicString voxpath;
                      char *avoxpath;
                      HRESULT hr;
                      const cst_val *ttwv;
                   
                      if (!SUCCEEDED(hr = SpGenericSetObjectToken(pToken, vox_token)))
                          return hr;
                      if (!SUCCEEDED(vox_token->GetStringValue(L"Voxpath", &voxpath)))
                          avoxpath = NULL; /* It isn't always necessary */
                      else
                          avoxpath = W2A(voxpath);  //Unicode to Ansi
                   
                      if (avoxpath==NULL){
                          MessageBoxA(0,(char*)"Не указан каталог голоса в реестре (Voxpath). Переустановите программу.",0,0);
                          return E_INVALIDARG;
                      }
                   
                      //MessageBoxW(0,voxpath,0,0);
                   
                     /* initialize */
                   
                      if (curr_vox){
                          (*unregfunc)(curr_vox);
                          }
                   
                      if ((curr_vox = (*regfunc)(avoxpath)) == NULL)
                          return E_INVALIDARG; /* or something */
                   
                      // otherwise darts out mistake after select of other voice in SAPI5 TTSAPP
                      if (curr_vox){
                          (*unregfunc)(curr_vox);
                          }
                   
                      if ((curr_vox = (*regfunc)(avoxpath)) == NULL)
                          return E_INVALIDARG; // or something
                   
                   
                   
                   
                     /* initialize */
                      if (engine.engine.global.sampling_rate>0){
                                  HTS_Engine_refresh(&engine.engine);
                                  Flite_HTS_Engine_clear(&engine);
                      }
                   
                      Flite_HTS_Engine_initialize(&engine, use_lpf, sampling_rate, fperiod, alpha,
                                                 stage, beta, audio_buff_size, uv_threshold,
                                                 use_log_gain, gv_weight_mgc, gv_weight_lf0,
                                                 gv_weight_lpf);
                                                 //*/
                     /* load */
                     Flite_HTS_Engine_load_voice(&engine, avoxpath);
                   
                     // Здесь для проверки работы можно добавить код для синтеза речи по любой англ. текстовой строке
                   
                   
                      //if ((ttwv = feat_val(curr_vox->features, "tokentowords_func"))) {
                      //  feat_set(curr_vox->features, "old_tokentowords_func", ttwv);
                      //  feat_set(curr_vox->features, "tokentowords_func",
                      //       itemfunc_val(sapi_tokentowords));
                      //}
                   
                   
                      return hr;
                  }


                Если все сделано правильно, то можно проверить работу драйвера в апплете Речь Панели управления Windows.
                Синтез нельзя будет остановить кнопкой Стоп, нельзя поменять темп речи, но можно закрыть окно и подождать пока закончится синтезированная речь.

                Далее постараюсь описать назначение некоторых функций старого драйвера, обработку событий от интерфейсных элементов в новом драйвере.
                Прикреплённый файлПрикреплённый файлregister_vox.zip (2,46 Кбайт, скачиваний: 110)
                  Шаг 3
                  Краткий обзор драйвера Flite. Функции приводятся в порядке их вызова.
                  Файл FliteTTSEngineObj.cpp

                  STDMETHODIMP CFliteTTSEngineObj::SetObjectToken(ISpObjectToken* pToken) - Инициализация драйвера: чтение параметров реестра, инициализация структур вокодера и структуры текущего высказывания.

                  STDMETHODIMP CFliteTTSEngineObj::GetOutputFormat(const GUID * pTargetFormatId,
                  const WAVEFORMATEX * pTargetWaveFormatEx,
                  GUID * pDesiredFormatId,
                  WAVEFORMATEX ** ppCoMemDesiredWaveFormatEx) - функция инициализации структуры WAVEFORMATEX, определяющей формат аудиоданных.

                  STDMETHODIMP CFliteTTSEngineObj::Speak(DWORD dwSpeakFlags,
                  REFGUID rguidFormatId,
                  const WAVEFORMATEX * pWaveFormatEx,
                  const SPVTEXTFRAG* pTextFragList,
                  ISpTTSEngineSite* pOutputSite) - основная функция, содержащая цикл обработки событий драйвера: SPVA_Speak, SPVA_SpellOut, SPVA_Pronounce, SPVA_Silence, SPVA_Bookmark, а также цикл обрабоки событий, поступивших от пользователя. Эти события обрабатываются функциями:
                  speak_frag() - создание потока токенов (слов) и синтез речи по полученному тексту (фрагу);
                  spell_frag() - создание потока токенов(букв) для синтеза по буквам;
                  pronounce_frag() - создание потока токенов(фонем) для синтеза по фонемам;
                  silence_frag() - записать паузу в выходной звуковой поток, длит-ть паузы задается в теге, в мсек;
                  set_bookmark() - создание потока токенов(закладок) для синтеза по закладкам;
                  get_actions_and_do_them - функция с циклом обработки событий от интерфейсных элементах (действиях пользователя):
                  SPVES_ABORT, SPVST_SENTENCE, SPVES_RATE, SPVES_VOLUME.
                  Два последних события возникают при изменении пользователем темпа речи и громкости соответственно.
                  Здесь, Фраг - весь текст, переданный из приложения: предложение или текст из набора предложений.

                  speak_frag() - функция для создания потока токенов полученного текста и синтеза речи в цикле по токенам.
                  Я особо не вникал, как складываются потоки токенов, но синтезируются они с помощью этой функции.

                  Здесь же устанавливается значения позиции токена в тексте и его длина и обрабатываются действия пользователя
                  (снова вызывается get_actions_and_do_them()).
                  Далее эти значения копируются в структуру Высказывания curr_utt и заполняются остальные параметры Высказывания.
                  Синтез заполненной структуры для текущего токена происходит в synth_one_utt();

                  synth_one_utt() - функция синтеза речи по заполненной структуре текущего Высказывания;

                  Пока следует отметить, что в новом драйвере можно и нужно исключить функцию synth_one_utt(),
                  а синтез функциями HTS_engine сделать непосредственно в speak_frag().
                  Однако очистка потоков движка занимает много времени и приводит к задержкам между произносимыми токенами (словами) в 1-3 сек.
                  В конечном варианте, пришлось отказаться от цикла формирования потока токенов и их раздельного синтеза и вернуться к циклу создания меток и их синтеза.
                  Действия пользователя можно обрабатывать в цикле синтеза меток функцией get_actions_and_do_them() или кодом из нее.
                  Темп и громкость речи регулируется путем установки 2-х переменных, объявленных в классе.
                  Их значения обрабатыаются непосредственно перед синтезом меток.

                  Сложнее всего описать алгоритм формирования событий для синхронизации подстветки проговариваемых слов.
                    Вариант кода синтеза речи и обработки действий пользователя
                    ExpandedWrap disabled
                      /* HTS_GStreamSet_create: generate speech */
                      /* (stream[0] == spectrum && stream[1] == lf0) */
                      HTS_Boolean
                      CFliteTTSEngineObj::HTS_GStreamSet_create2(HTS_GStreamSet * gss, HTS_PStreamSet * pss, int stage, HTS_Boolean use_log_gain, int sampling_rate, int fperiod, double alpha, double beta, HTS_Boolean * stop, double volume, HTS_Audio * audio)
                      {
                       //...
                            int lab_duration = 0;
                            int nstate = engine.engine.sss.nstate; //=5
                            for (int j=0; j<nstate; j++){  
                                lab_duration = lab_duration +  engine.engine.sss.duration[i*nstate+j];
                            }
                            //itoa( lab_duration, buff, 10);
                            //    MessageBoxA(0,buff,(char*)"lab_duration",0); //***
                       
                              for (j = 0; j < lab_duration && (*stop) == FALSE; j++){
                                if (gss->nstream >= 3)
                                   lpf = &gss->gstream[2].par[i][0];
                       
                                get_actions_and_do_them();
                                      if (aborted)
                                          goto stop_synth;
                                      if (sentence_skip == 0) {
                                          //send_sentence_event(ts->token_pos
                                          //          + curr_frag->ulTextSrcOffset);
                                          HTS_Vocoder_synthesize(&v, gss->gstream[0].static_length - 1, gss->gstream[1].par[k][0],
                                            &gss->gstream[0].par[k][0], nlpf, lpf, alpha, beta, volume, &gss->gspeech[k * fperiod], audio);
                                          k++; // next frame
                       
                                      } else {
                                          --sentence_skip;
                                          site->CompleteSkip(1);
                                      }
                              
                              } // end for on lab_duration
                       
                       
                      //...
                      }


                    Алах Акбар!
                      Хотя я писал драйвер под тибетские мантры, Аллах больше Тибета.
                      Аллах Велик, поэтому кому-то в Аллахе это обязательно пригодится.


                      Шаг 4
                      Если вы сделали все правильно, то ваш драйвер должен проговаривать английский текст любым зарегистрированным английским голосом. Но остался один баг,
                      который проявляется только при смене голоса в аплете Речь Панели управления и приводит к ошибке приложения. Я нашел один способ пофиксить его, но
                      предлагаю вам самостоятельно найти более правильное решение.

                      Теперь осталось доделать синхронное подсветку проговариваемого текста. Хотя Майкрософт предлагает специальное приложение TTSApp.exe, будем проверять
                      драйвер в том же апплете Речь. Отмечу, что приложение TTSApp.exe позволяет выбрать голос, проговорить любой текст, сохранить синтезированную речь в файл,
                      вывести события TTS в спец. окне, менять значение темпа речи и громкости.

                      Для лучшего понимания работы sapi-драйвера лучше почитать статьи, посвященные SAPI-интерфейсу.
                      В одной замечательной статье (сохраненной где-то на жестком диске) описана модель событий драйвера, хотя с первого раза не совсем ясно, как добавленные
                      события подсвечивают нужные слова. Просто следует запомнить, что нужно правильно добавить события, а драйвер сам асинхронно в удобное время обработает весь набор добавленных событий.
                      Механизм добавления и обработки событий будем проверять в функции speak_frag.
                      Здесь я привожу измененный вариант функции.

                      ExpandedWrap disabled
                        void
                        CFliteTTSEngineObj::speak_frag()
                        {
                            //cst_tokenstream *ts; // global now
                            const char *token; // global now
                            cst_item *t;
                            char *text;
                            size_t len;
                         
                            int i;
                         
                            cst_item *s = NULL;
                            char **label_data = NULL;
                            int label_size = 0;
                         
                            setlocale(LC_CTYPE,(const char*)"");
                            len = wcstombs(NULL, curr_frag->pTextStart, curr_frag->ulTextLen);
                            text = cst_alloc(char, len+1);
                            wcstombs(text, curr_frag->pTextStart, curr_frag->ulTextLen);
                         
                                //MessageBoxA(0,text,(char*)"text",0); //***
                         
                            ////v 1
                           utt_set_input_text(curr_utt,text);
                          
                           ts = my_ts_open_string(curr_utt, text);
                           //cst_free(text); // move to func. end
                         
                            while (!ts_eof(ts)) {
                                sztoken = ts_get(ts);
                                t = append_new_token(ts, token, tok_rel);
                                //item_set_int(t, "token_pos",
                                //       ts->token_pos + curr_frag->ulTextSrcOffset);
                                item_set_int(t, "token_pos",
                                         curr_frag->ulTextSrcOffset);
                                item_set_int(t, "token_length", strlen(sztoken));
                                set_local_prosody(t, &curr_frag->State);
                         
                                    //  itoa(item_feat_int(t, "token_pos") + curr_frag->ulTextSrcOffset, buff, 10); //если не устанавливалась, то сброс программы
                                    //MessageBoxA(0,buff,(char*)"token_length",0); //***
                         
                                    SPEVENT evt;
                                    SpClearEvent(&evt);
                                    evt.eEventId = SPEI_WORD_BOUNDARY;
                                    evt.elParamType = SPET_LPARAM_IS_UNDEFINED;
                                    evt.ullAudioStreamOffset = 0; //bcount + offset;
                                    evt.wParam = strlen(sztoken);
                                    evt.lParam = ts->token_pos + curr_frag->ulTextSrcOffset;
                                    site->AddEvents(&evt, 1);
                                    //Sleep(1000);
                         
                                    
                                   if (relation_head(tok_rel)) { //if (relation_head(tok_rel) && utt_break(ts, token, tok_rel)) {
                         
                                    if (aborted)
                                        goto pod_bay_doors;
                                    if (sentence_skip == 0) {
                                        //send_sentence_event(ts->token_pos
                                        //          + curr_frag->ulTextSrcOffset);
                                        //MessageBoxA(0,(char*)"need_synth_one_utt",0,0); //***
                                        //synth_one_utt();
                                        //start_new_utt();
                         
                         
                         
                         
                                    } else {
                                        --sentence_skip;
                                        site->CompleteSkip(1);
                                        //start_new_utt();
                                    }
                                   }
                                
                                //itoa(ts->token_pos + curr_frag->ulTextSrcOffset, buff, 10);
                                //MessageBoxA(0,buff,token,0); //***
                         
                            }
                         
                        pod_bay_doors: /* Open the POD bay doors, KAL... */
                         
                         
                         
                           cst_free(text);
                           delete_tokenstream(ts); //<----
                           delete_utterance(curr_utt);
                           curr_utt = NULL;
                           tok_rel = NULL;
                           return;
                        }


                      Следует отметить, что поток токенов создается и работает независимо от функции высказывания utt и подключается к ней в случае необходимости.
                      Если вы соберете и проверите код, то не заметите никаких изменений, хотя драйвер правильно обрабатывает события и подсвечивает текст.
                      Для того, чтобы заметить подсветку, необходимо ввести временную задержку, например в 1 сек. Для этого раскоментируйте строку //Sleep(1000); и снова проверьте его работу.
                      драйвера. Теперь видно, что подсветка работает, но текст не проговаривается.

                      В конечном варианте я заменил этот цикл на типичный код синтеза текста из flite+hts.
                      Здесь привожу почти законченный код.
                      ExpandedWrap disabled
                        void
                        CFliteTTSEngineObj::speak_frag()
                        {
                            //cst_tokenstream *ts; // global now
                            const char *token; // global now
                            cst_item *t;
                            char *text;
                            size_t len;
                         
                            int i;
                         
                            cst_item *s = NULL;
                            char **label_data = NULL;
                            int label_size = 0;
                         
                            setlocale(LC_CTYPE,(const char*)"");
                            len = wcstombs(NULL, curr_frag->pTextStart, curr_frag->ulTextLen);
                            text = cst_alloc(char, len+1);
                            wcstombs(text, curr_frag->pTextStart, curr_frag->ulTextLen);
                         
                           //MessageBoxA(0,text,(char*)"text",0); //***
                         
                            ////v 1
                         
                           utt_set_input_text(curr_utt,text); //   flite_do_synth(curr_utt, curr_vox, utt_synth);
                          
                           ts = my_ts_open_string(curr_utt, text);
                         
                           if (curr_utt == NULL)
                              return;
                                            
                           for (s = relation_head(utt_relation(curr_utt, "Segment")); s; s = item_next(s))
                              label_size++;
                           if (label_size <= 0)
                              return;
                           label_data = (char **) calloc(label_size, sizeof(char *));
                         
                           for (i = 0, s = relation_head(utt_relation(curr_utt, "Segment")); s;
                                s = item_next(s), i++) {
                              label_data[i] = (char *) calloc(MAXBUFLEN, sizeof(char));
                              Flite_HTS_Engine_create_label(&engine, s, label_data[i]);
                           }
                         
                           if (phoneme_alignment)       // modify label
                              HTS_Label_set_frame_specified_flag(&engine.engine.label, TRUE);
                           if (speech_speed != 1.0)     // modify label
                           HTS_Label_set_speech_speed(&engine.engine.label, speech_speed);
                          
                           if (volume!=100)             // set volume  
                              hts_volume = volume*0.01;
                              //hts_volume = hts_volume/100; //err--->hts_volume=0
                              HTS_Engine_set_volume(&engine.engine, hts_volume );
                         
                         
                           // speech synthesis part
                           HTS_Engine_load_label_from_string_list(&engine.engine, label_data, label_size);
                         
                           HTS_Engine_create_sstream(&engine.engine);
                           HTS_Engine_create_pstream(&engine.engine);
                           HTS_Engine_create_gstream(&engine.engine);
                         
                         
                        pod_bay_doors: /* Open the POD bay doors, KAL... */
                         
                           HTS_Engine_refresh(&engine.engine);
                         
                           for (i = 0; i < label_size; i++)
                              free(label_data[i]);
                           free(label_data);
                         
                           cst_free(text);
                           delete_tokenstream(ts); //<----
                           delete_utterance(curr_utt);
                           curr_utt = NULL;
                           tok_rel = NULL;
                           return;
                        }



                      Обработку событий от интефейса и подсветку текста можно выполнить только в функции HTS_Engine_create_gstream(&engine.engine);
                      Для этого потребуется объявить ее в классе и внести в код сооответстующие изменения.
                        Несколько слов о регулировке темпа речи.
                        Если перенести таблицу преобразования из исходного кода Flite, то регулятор будет работать с точностью до наоборот. При движении влево темп речи будет увеличиваться, при движении вправо - уменьшаться. Самое простое решение, которое приходит в голову, это взять обратную величину от значения в таблице и отправить его в движок flite+hts. Но такое решение не оптимально, тк график функции 1/x сильно отличается от прямой линии.
                        Поэтому лучшим вариантом будет испавить таблицу вручную, те перевернуть числовой ряд на 180 градусов. После замены проверка регулятора показала, что темп речи изменяется в более широком диапазоне, чем необходимо на практике. Уменьшаем шаг приращения темпа речи на меньшее значение и методом подбора получаем окончательный вариант таблицы.

                        ExpandedWrap disabled
                          /* Each step is prev +(1/20) */
                          static const double sapi_ratetab_foo[21] = {
                             0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95,
                             1.0,
                             1.05, 1.1, 1.15, 1.2, 1.25, 1.3, 1.35, 1.4, 1.45, 1.5
                          };
                          static const double *sapi_ratetab = sapi_ratetab_foo + 10;



                        Функции преобразования значения регулятора в значение темпа речи:
                        ExpandedWrap disabled
                          static double
                          convert_sapi_rate(int r)
                          {
                              if (r < -10)
                                  r = -10;
                              else if (r > 10)
                                  r = 10;
                              return sapi_ratetab[r];
                          }


                        Таблица static const double sapi_pitchtab_foo[21] для преобразования pitch используется без изменения.

                        Далее понадобится дудочка как фильме "Прометей" и похоже, что Сирианская.
                        Сообщение отредактировано: webcoder88 -
                          Шаг 5

                          Следует исключить внесение каких-либо изменений в библиотеки сторонних разработчиков, а вместо этого переносить изменяемые функции в код своего проекта.
                          Поэтому для обработки действий пользователя и подсветки текста одновременно с синтезом речи следует исправить код функции speak_frag(), те заменить строку HTS_Engine_create_gstream(&engine.engine);

                          на HTS_Engine_create_gstream2(&engine.engine); // <--- синтез речи и обработка событий.

                          Объявляем функцию HTS_Engine_create_gstream2 в секции public класса CFliteTTSEngineObj и копируем код функци HTS_Engine_create_gstream из hts_engine_API в файл FliteTTSEngineObj.cpp с заменой имени

                          на новое HTS_Engine_create_gstream2. Эта функция ничего не делает кроме вызова др. функцию HTS_GStreamSet_create, поэтому также меняем ее имя на HTS_GStreamSet_create2.
                          Далее аналогично объявляем HTS_GStreamSet_create2 в секции public класса CFliteTTSEngineObj, копируем ее код из hts_engine_API в файл FliteTTSEngineObj.cpp и добавляем в конце имени цифру 2


                          ExpandedWrap disabled
                            /* HTS_Engine_create_gstream: synthesis speech */
                            HTS_Boolean
                            CFliteTTSEngineObj::HTS_Engine_create_gstream2(HTS_Engine * engine)
                            {
                               return HTS_GStreamSet_create2(&engine->gss, &engine->pss, engine->global.stage, engine->global.use_log_gain, engine->global.sampling_rate, engine->global.fperiod,engine->global.alpha, engine->global.beta, &engine->global.stop, engine->global.volume, engine->global.audio_buff_size > 0 ? &engine->audio : NULL);
                            }





                          ExpandedWrap disabled
                            /* HTS_GStreamSet_create: generate speech */
                            /* (stream[0] == spectrum && stream[1] == lf0) */
                            HTS_Boolean
                            CFliteTTSEngineObj::HTS_GStreamSet_create2(HTS_GStreamSet * gss, HTS_PStreamSet * pss, int stage, HTS_Boolean use_log_gain, int sampling_rate, int fperiod, double alpha, double beta, HTS_Boolean *
                             
                            stop, double volume, HTS_Audio * audio)
                            {
                               int i, j, k;
                               int msd_frame;
                               HTS_Vocoder v;
                               int nlpf = 0;
                               double *lpf = NULL;
                             
                               /* check */
                               if (gss->gstream || gss->gspeech) {
                                  //HTS_error(1, "HTS_GStreamSet_create: HTS_GStreamSet is not initialized.\n");
                                   MessageBoxW(0, L"HTS_GStreamSet_create: HTS_GStreamSet is not initialized.\n",L"HTS_Error",0);
                                  return FALSE;
                               }
                             
                               /* initialize */
                               gss->nstream = HTS_PStreamSet_get_nstream(pss);
                               gss->total_frame = HTS_PStreamSet_get_total_frame(pss);
                               gss->total_nsample = fperiod * gss->total_frame;
                               gss->gstream = (HTS_GStream *) HTS_calloc(gss->nstream, sizeof(HTS_GStream));
                               for (i = 0; i < gss->nstream; i++) {
                                  gss->gstream[i].static_length = HTS_PStreamSet_get_static_length(pss, i);
                                  gss->gstream[i].par = (double **) HTS_calloc(gss->total_frame, sizeof(double *));
                                  for (j = 0; j < gss->total_frame; j++)
                                     gss->gstream[i].par[j] = (double *) HTS_calloc(gss->gstream[i].static_length, sizeof(double));
                               }
                               gss->gspeech = (short *) HTS_calloc(gss->total_nsample, sizeof(short));
                             
                               /* copy generated parameter */
                               for (i = 0; i < gss->nstream; i++) {
                                  if (HTS_PStreamSet_is_msd(pss, i)) {      /* for MSD */
                                     for (j = 0, msd_frame = 0; j < gss->total_frame; j++)
                                        if (HTS_PStreamSet_get_msd_flag(pss, i, j)) {
                                           for (k = 0; k < gss->gstream[i].static_length; k++)
                                              gss->gstream[i].par[j][k] = HTS_PStreamSet_get_parameter(pss, i, msd_frame, k);
                                           msd_frame++;
                                        } else
                                           for (k = 0; k < gss->gstream[i].static_length; k++)
                                              gss->gstream[i].par[j][k] = LZERO;
                                  } else {                  /* for non MSD */
                                     for (j = 0; j < gss->total_frame; j++)
                                        for (k = 0; k < gss->gstream[i].static_length; k++)
                                           gss->gstream[i].par[j][k] = HTS_PStreamSet_get_parameter(pss, i, j, k);
                                  }
                               }
                             
                               /* check */
                               if (gss->nstream != 2 && gss->nstream != 3) {
                                  //HTS_error(1, "HTS_GStreamSet_create: The number of streams should be 2 or 3.\n");
                                  MessageBoxW(0, L"HTS_GStreamSet_create: The number of streams should be 2 or 3.\n",L"HTS_Error",0);
                                  HTS_GStreamSet_clear(gss);
                                  return FALSE;
                               }
                               if (HTS_PStreamSet_get_static_length(pss, 1) != 1) {
                                  //HTS_error(1, "HTS_GStreamSet_create: The size of lf0 static vector should be 1.\n");
                                  MessageBoxW(0, L"HTS_GStreamSet_create: The size of lf0 static vector should be 1.\n",L"HTS_Error",0);
                                  HTS_GStreamSet_clear(gss);
                                  return FALSE;
                               }
                               if (gss->nstream >= 3 && gss->gstream[2].static_length % 2 == 0) {
                                  //HTS_error(1, "HTS_GStreamSet_create: The number of low-pass filter coefficient should be odd numbers.");
                                  MessageBoxW(0, L"HTS_GStreamSet_create: The number of low-pass filter coefficient should be odd numbers.", L"HTS_Error",0);
                                  HTS_GStreamSet_clear(gss);
                                  return FALSE;
                               }
                             
                               /* synthesize speech waveform */
                               HTS_Vocoder_initialize(&v, gss->gstream[0].static_length - 1, stage, use_log_gain, sampling_rate, fperiod);
                               if (gss->nstream >= 3)
                                  nlpf = (gss->gstream[2].static_length - 1) / 2;
                             
                             
                               cst_item *s = NULL;
                               int sps;
                               sps = get_param_int(curr_vox->features, "sample_rate", sampling_rate); //16000
                             
                             
                                // цикл синтеза по меткам
                               for (i = 0, k = 0, s = relation_head(utt_relation(curr_utt, "Segment")); s;
                                    s = item_next(s), i++) { //k-номер фрейма
                             
                                  int offset;
                                  SPEVENT evt;
                                  cst_item *token;
                                  //const cst_val *bmark;
                             
                                  offset = (int) (ffeature_float(s, "p.end") * sps * sizeof(short));
                             
                                  for (int j=0; j<nstate; j++){
                             
                                      if (gss->nstream >= 3)
                                         lpf = &gss->gstream[2].par[i][0];
                             
                                      get_actions_and_do_them();
                                            if (aborted)
                                                goto stop_synth;
                                            if (sentence_skip == 0) {
                                                //send_sentence_event(ts->token_pos
                                                //          + curr_frag->ulTextSrcOffset);
                                                HTS_Vocoder_synthesize(&v, gss->gstream[0].static_length - 1, gss->gstream[1].par[k][0],
                                                  &gss->gstream[0].par[k][0], nlpf, lpf, alpha, beta, volume, &gss->gspeech[k * fperiod], audio);
                                                k++; // next frame
                             
                                            } else {
                                                --sentence_skip;
                                                site->CompleteSkip(1);
                                            }
                                    
                             
                             
                               } // end for on item
                             
                            stop_synth:
                               HTS_Vocoder_clear(&v);
                               if (audio)
                                  HTS_Audio_flush(audio);
                             
                               return TRUE;
                            }


                          Последняя функция тянет за собой HTS_calloc(). Копируем ее в файл FliteTTSEngineObj.cpp.

                          ExpandedWrap disabled
                                            // snarfed from HTS_Engine
                             
                            /* HTS_calloc: wrapper for calloc */
                            char *HTS_calloc(const size_t num, const size_t size)
                            {
                            #ifdef FESTIVAL
                               char *mem = (char *) safe_wcalloc(num * size);
                            #else
                               char *mem = (char *) calloc(num, size);
                            #endif                          /* FESTIVAL */
                             
                               if (mem == NULL)
                                  MessageBoxW(0, L"HTS_calloc: Cannot allocate memory.\n",L"HTS_Error",0);
                             
                               return mem;
                            }


                          Если собрать проект, то драйвер может обрабатывать события от элементов интерфейса одновременно с синтезом речи.
                          Осталось добавить подсветку произносимого текста. Для этого необходимо организовать цикл по токенам, в котором будем определять общую длительность метки в фреймах, а затем синтезировать речь по этим фреймам в цикле по длительности.


                          ExpandedWrap disabled
                             for (i = 0, k = 0, s = relation_head(utt_relation(curr_utt, "Segment")); s;
                                    s = item_next(s), i++) { //k-номер фрейма
                            //...
                            }



                          Каждая метка имеет 5 состояний с разной длительностью (в фреймах), дающих при суммирровании общую длительность фонемы в фреймах.

                          ExpandedWrap disabled
                                // цикл синтеза по меткам
                                // необходимо определить общую длительность метки в фреймах
                                // и синтезировать речь по этим фреймам, тоже в цикле по длительности
                             
                               for (i = 0, k = 0, s = relation_head(utt_relation(curr_utt, "Segment")); s;
                                    s = item_next(s), i++) { //k-номер фрейма
                             
                             
                                  // get label duration
                                  int lab_duration = 0;
                                  int nstate = engine.engine.sss.nstate; //=5
                                  for (int j=0; j<nstate; j++){  
                                      lab_duration = lab_duration +  engine.engine.sss.duration[i*nstate+j];
                                  }
                                  //itoa( lab_duration, buff, 10);
                                  //    MessageBoxA(0,buff,(char*)"lab_duration",0); //***
                             
                             for (j = 0; j < lab_duration && (*stop) == FALSE; j++){
                                    // синтез речи
                                    }
                             
                             // подсветка токена
                            }



                          Законченный вариант функции:


                          ExpandedWrap disabled
                            /* HTS_GStreamSet_create: generate speech */
                            /* (stream[0] == spectrum && stream[1] == lf0) */
                            HTS_Boolean
                            CFliteTTSEngineObj::HTS_GStreamSet_create2(HTS_GStreamSet * gss, HTS_PStreamSet * pss, int stage, HTS_Boolean use_log_gain, int sampling_rate, int fperiod, double alpha, double beta, HTS_Boolean *
                             
                            stop, double volume, HTS_Audio * audio)
                            {
                               int i, j, k;
                               int msd_frame;
                               HTS_Vocoder v;
                               int nlpf = 0;
                               double *lpf = NULL;
                             
                               /* check */
                               if (gss->gstream || gss->gspeech) {
                                  //HTS_error(1, "HTS_GStreamSet_create: HTS_GStreamSet is not initialized.\n");
                                   MessageBoxW(0, L"HTS_GStreamSet_create: HTS_GStreamSet is not initialized.\n",L"HTS_Error",0);
                                  return FALSE;
                               }
                             
                               /* initialize */
                               gss->nstream = HTS_PStreamSet_get_nstream(pss);
                               gss->total_frame = HTS_PStreamSet_get_total_frame(pss);
                               gss->total_nsample = fperiod * gss->total_frame;
                               gss->gstream = (HTS_GStream *) HTS_calloc(gss->nstream, sizeof(HTS_GStream));
                               for (i = 0; i < gss->nstream; i++) {
                                  gss->gstream[i].static_length = HTS_PStreamSet_get_static_length(pss, i);
                                  gss->gstream[i].par = (double **) HTS_calloc(gss->total_frame, sizeof(double *));
                                  for (j = 0; j < gss->total_frame; j++)
                                     gss->gstream[i].par[j] = (double *) HTS_calloc(gss->gstream[i].static_length, sizeof(double));
                               }
                               gss->gspeech = (short *) HTS_calloc(gss->total_nsample, sizeof(short));
                             
                               /* copy generated parameter */
                               for (i = 0; i < gss->nstream; i++) {
                                  if (HTS_PStreamSet_is_msd(pss, i)) {      /* for MSD */
                                     for (j = 0, msd_frame = 0; j < gss->total_frame; j++)
                                        if (HTS_PStreamSet_get_msd_flag(pss, i, j)) {
                                           for (k = 0; k < gss->gstream[i].static_length; k++)
                                              gss->gstream[i].par[j][k] = HTS_PStreamSet_get_parameter(pss, i, msd_frame, k);
                                           msd_frame++;
                                        } else
                                           for (k = 0; k < gss->gstream[i].static_length; k++)
                                              gss->gstream[i].par[j][k] = LZERO;
                                  } else {                  /* for non MSD */
                                     for (j = 0; j < gss->total_frame; j++)
                                        for (k = 0; k < gss->gstream[i].static_length; k++)
                                           gss->gstream[i].par[j][k] = HTS_PStreamSet_get_parameter(pss, i, j, k);
                                  }
                               }
                             
                               /* check */
                               if (gss->nstream != 2 && gss->nstream != 3) {
                                  //HTS_error(1, "HTS_GStreamSet_create: The number of streams should be 2 or 3.\n");
                                  MessageBoxW(0, L"HTS_GStreamSet_create: The number of streams should be 2 or 3.\n",L"HTS_Error",0);
                                  HTS_GStreamSet_clear(gss);
                                  return FALSE;
                               }
                               if (HTS_PStreamSet_get_static_length(pss, 1) != 1) {
                                  //HTS_error(1, "HTS_GStreamSet_create: The size of lf0 static vector should be 1.\n");
                                  MessageBoxW(0, L"HTS_GStreamSet_create: The size of lf0 static vector should be 1.\n",L"HTS_Error",0);
                                  HTS_GStreamSet_clear(gss);
                                  return FALSE;
                               }
                               if (gss->nstream >= 3 && gss->gstream[2].static_length % 2 == 0) {
                                  //HTS_error(1, "HTS_GStreamSet_create: The number of low-pass filter coefficient should be odd numbers.");
                                  MessageBoxW(0, L"HTS_GStreamSet_create: The number of low-pass filter coefficient should be odd numbers.", L"HTS_Error",0);
                                  HTS_GStreamSet_clear(gss);
                                  return FALSE;
                               }
                             
                               /* synthesize speech waveform */
                               HTS_Vocoder_initialize(&v, gss->gstream[0].static_length - 1, stage, use_log_gain, sampling_rate, fperiod);
                               if (gss->nstream >= 3)
                                  nlpf = (gss->gstream[2].static_length - 1) / 2;
                             
                             
                               cst_item *s = NULL;
                               int sps;
                               sps = get_param_int(curr_vox->features, "sample_rate", sampling_rate); //16000
                             
                                //ts = my_ts_open_string(curr_utt, text);
                             
                                // цикл синтеза по меткам
                                // необходимо определить общую длительность метки в фреймах
                                // и синтезировать речь по этим фреймам, тоже в цикле по длительности
                             
                               for (i = 0, k = 0, s = relation_head(utt_relation(curr_utt, "Segment")); s;
                                    s = item_next(s), i++) { //k-номер фрейма
                             
                                  int offset;
                                  SPEVENT evt;
                                  cst_item *token;
                                  //const cst_val *bmark;
                             
                                  offset = (int) (ffeature_float(s, "p.end") * sps * sizeof(short));
                                  token = path_to_item(s, "R:SylStructure.parent.parent.R:Token.parent");
                             
                             
                                  // get label duration
                                  int lab_duration = 0;
                                  int nstate = engine.engine.sss.nstate; //=5
                                  for (int j=0; j<nstate; j++){  
                                      lab_duration = lab_duration +  engine.engine.sss.duration[i*nstate+j];
                                  }
                                  //itoa( lab_duration, buff, 10);
                                  //    MessageBoxA(0,buff,(char*)"lab_duration",0); //***
                             
                                    for (j = 0; j < lab_duration && (*stop) == FALSE; j++){
                                      if (gss->nstream >= 3)
                                         lpf = &gss->gstream[2].par[i][0];
                             
                                      get_actions_and_do_them();
                                            if (aborted)
                                                goto stop_synth;
                                            if (sentence_skip == 0) {
                                                //send_sentence_event(ts->token_pos
                                                //          + curr_frag->ulTextSrcOffset);
                                                HTS_Vocoder_synthesize(&v, gss->gstream[0].static_length - 1, gss->gstream[1].par[k][0],
                                                  &gss->gstream[0].par[k][0], nlpf, lpf, alpha, beta, volume, &gss->gspeech[k * fperiod], audio);
                                                k++; // next frame
                             
                                            } else {
                                                --sentence_skip;
                                                site->CompleteSkip(1);
                                            }
                                    
                                    } // end for on lab_duration
                             
                             
                                  /* Word boundaries */
                                    if (token
                                        && item_parent(item_as(s, "SylStructure"))
                                        && item_prev(item_as(s, "SylStructure")) == NULL
                                        && item_prev(item_parent(item_as(s, "SylStructure"))) == NULL
                                        && (token !=
                                        path_to_item(s, "R:SylStructure.parent.parent.p."
                                        "R:Token.parent"))){
                                        //MessageBoxA(0,label_data[i],0,0); //***
                                        //MessageBoxA(0,item_feat_string(token, "name"),(char*)"token_name",0);
                             
                                        sztoken = ts_get(ts);
                                        //item_set_int(token, "token_pos", ts->token_pos);
                                        //item_set_int(token, "token_length", strlen(sztoken));
                             
                                        //itoa(item_feat_int(token, "token_pos") + curr_frag->ulTextSrcOffset, buff, 10);
                                        //MessageBoxA(0,buff,(char*)"token_length",0); //***
                             
                                        //itoa( ts->token_pos, buff, 10);
                                        //MessageBoxA(0,buff,(char*)"token_pos",0); //***
                             
                             
                                        if (sampling_rate==16000) Sleep(210); //synchronizing, need thread :)
                                        SpClearEvent(&evt);
                                        evt.eEventId = SPEI_WORD_BOUNDARY;
                                        evt.elParamType = SPET_LPARAM_IS_UNDEFINED;
                                        evt.ullAudioStreamOffset = 0;                           //bcount + offset;
                                        evt.wParam = strlen(item_feat_string(token, "name"));   //item_feat_int(token, "token_length");
                                        evt.lParam = ts->token_pos;                             //item_feat_int(token, "token_pos");
                                        site->AddEvents(&evt, 1);
                                        }
                               } // end for on item
                             
                            stop_synth:
                               HTS_Vocoder_clear(&v);
                               if (audio)
                                  HTS_Audio_flush(audio);
                             
                               return TRUE;
                            }


                          Добавлено
                          if (sampling_rate==16000) Sleep(210); //synchronizing, need thread :) - этот параметр подобран экспериментально
                          Без этой задержки для 16кГц-вых голосов проскакиваются первые 2 токена (слова). Для 48кГц задержка не нужна.

                          Предлагаю самостоятельно устранить баг при смене голосов. Подсказка: при выборе голоса др. TTS ошибки нет, ошибка возникает при последовательной смене HTS-голосов.
                            Недавно нашлось немного времени на синтезатор. Лучший вариант для Windows https://github.com/m-toman/SALB + русская лексика из RHVoice.
                            На данный момент английские и немецкие голоса и воспроизводятся также как в оригинальном проекте, для русских голосов только метки, тк нет голосов в новом формате. Метки можно прослушать в hts_engine.
                            Новый формат пдф-файлов сильно отличается от предыдущих версий. На создание утилит конвертации форматов потребуется время и программисты.
                            Ссылка для скачивания бетта-версии с интегрированной русской лексикой: salb_tts
                            Постлексика и интонация осталась от англ. языка, поэтому речь с акцентом.

                            Для исследователей
                            cmu_us_arctic_slt.htsvoice, открываете в винхексе, копируете в текстовый редактор, открываете в вордпале или VS2012. Можем видеть, что голос с GV для кепстральных коэффициентов и основного тона:
                            USE_GV[MCP]:1
                            USE_GV[LF0]:1
                            USE_GV[LPF]:0
                            Их можно отключить, изменив 1 на 0 в винхексе. Дальше можете проверить синтез голоса.
                            Дальше вырезать эти файлы из файла голоса. Будет работать.
                            Формат всех файлов, кроме пдф-ок совпадает с файлами модели голоса RHVoice.

                            ExpandedWrap disabled
                              [GLOBAL]
                              HTS_VOICE_VERSION:1.0
                              SAMPLING_FREQUENCY:48000
                              FRAME_PERIOD:240
                              NUM_STATES:5
                              NUM_STREAMS:3
                              STREAM_TYPE:MCP,LF0,LPF
                              FULLCONTEXT_FORMAT:HTS_TTS_ENG
                              FULLCONTEXT_VERSION:1.0
                              GV_OFF_CONTEXT:"*-pau+*","*-h#+*","*-brth+*"
                              COMMENT:
                              [STREAM]
                              VECTOR_LENGTH[MCP]:35
                              VECTOR_LENGTH[LF0]:1
                              VECTOR_LENGTH[LPF]:31
                              IS_MSD[MCP]:0
                              IS_MSD[LF0]:1
                              IS_MSD[LPF]:0
                              NUM_WINDOWS[MCP]:3
                              NUM_WINDOWS[LF0]:3
                              NUM_WINDOWS[LPF]:1
                              USE_GV[MCP]:1
                              USE_GV[LF0]:1
                              USE_GV[LPF]:0
                              OPTION[MCP]:ALPHA=0.55
                              OPTION[LF0]:
                              OPTION[LPF]:
                              [POSITION]
                              DURATION_PDF:0-22203
                              DURATION_TREE:22204-91164
                              STREAM_WIN[MCP]:91165-91170,91171-91185,91186-91200
                              STREAM_WIN[LF0]:91201-91206,91207-91221,91222-91236
                              STREAM_WIN[LPF]:91237-91242
                              STREAM_PDF[MCP]:91243-1141262
                              STREAM_PDF[LF0]:1141263-1241550
                              STREAM_PDF[LPF]:1241551-1242810
                              STREAM_TREE[MCP]:1242811-1373734
                              STREAM_TREE[LF0]:1373735-1741482
                              STREAM_TREE[LPF]:1741483-1741587
                              GV_PDF[MCP]:1741588-1742151
                              GV_PDF[LF0]:1742152-1742179
                              GV_TREE[MCP]:1742180-1742332
                              GV_TREE[LF0]:1742333-1742639
                              [DATA]
                            Сообщение отредактировано: webcoder88 -
                              Вся информация о форматах содержится в исходных кодах hts_engine 1.05 и 1.07
                              Открываете и изучаете файлы HTS_model.c.

                              Как сделать из версии hts_engine 1.07 утилиту распаковки? Очень просто. Надо инициализировать движок и загрузить модель голоса, вызов синтеза комментируется, удаляются ненужные ключи и код их обработки, а также добавить код сохранения файлов на диск.

                              Все необходимое нужно искать в HTS_model.c по строке /* load duration */

                              Исходный вариант для длительности:
                              ExpandedWrap disabled
                                      /* load duration */
                                      pdf_fp = NULL;
                                      tree_fp = NULL;
                                      matched_size = 0;
                                      if (HTS_get_token_from_string_with_separator(temp_duration_pdf, &matched_size, buff2, '-') == TRUE) {
                                         s = (size_t) atoi(buff2);
                                         e = (size_t) atoi(&temp_duration_pdf[matched_size]);
                                         HTS_fseek(fp, (long) s, SEEK_CUR);
                                         pdf_fp = HTS_fopen_from_fp(fp, fsize);
                                     HTS_fseek(fp, start_of_data, SEEK_SET);
                                      }


                              Новая версия:
                              ExpandedWrap disabled
                                 // в начале функции.
                                   int sw;
                                   FILE* fw;
                                   char* buff;
                                   int fsize; // file size
                                 
                                 
                                  ...
                                 
                                      /* load duration */
                                      pdf_fp = NULL;
                                      tree_fp = NULL;
                                      matched_size = 0;
                                      if (HTS_get_token_from_string_with_separator(temp_duration_pdf, &matched_size, buff2, '-') == TRUE) {
                                         s = (size_t) atoi(buff2);
                                         e = (size_t) atoi(&temp_duration_pdf[matched_size]);
                                         fsize = e - s + 1;
                                         HTS_fseek(fp, (long) s, SEEK_CUR);
                                         pdf_fp = HTS_fopen_from_fp(fp, fsize);
                                 
                                 
                                         fw = fopen((char*)"dur.pdf","wb");
                                         buff =  (char*)HTS_calloc(fsize, sizeof(char
                                 
                                         HTS_fread_little_endian( (void*)(buff), 1, fsize, (HTS_File*) pdf_fp);
                                         //HTS_byte_swap(buff, 4, (fsize)/4);
                                         sw =            fwrite(buff, 1, fsize, fw); //<<<--------------
                                         fclose(fw);
                                         free(buff);
                                 
                                         HTS_fseek(fp, start_of_data, SEEK_SET);
                                      }



                              Аналогично правится весь код загрузки файлов.

                              После извлечения файлов голоса можно приступать к их изучению. Откройте в винхексе распакованный dur.pdf и аналогичный файл из голоса arctic_slt. Содержимое файлов отличается.
                              В новом файле нет заголовка, хотя первое слово может быть кол-вом пдфок в файле. Порядок следования байтов прямой. Если присмотреться еще лучше, то можете заметить, что все слова перевернуты и не хватает части заголовка. Добавляете часть заголовка, переворачивате все слова вручную или программно и проверяете файл на валидность.
                              Теперь надо решить вопрос быстрой проверки файлов на валидность. Лучше всего использовать тренированный голос на 48000 Гц, в котором файлы заменяются парами (пдф и дерево) или по одному.

                              Добавлено
                              Дальше файл фильтра. Также не хватает части заголовка, перевернуты слова и векторы мин и вариации длиной 31 (см параметры) идут один за друг, а в старом формате их значения чередуются.
                              Файл mgc.pdf также не очень сложный, но в звуке вного искажений. М/б неправильное чередование векторов и тд.

                              Реально сделать, голос распакован и протестирован. Ссылка на архив с распаковщиком и распакованным голосом: https://drive.google.com/file/d/1EjGxn_oHrP...iew?usp=sharing

                              Предыдущий вариант голоса неправильно произносит часть слов. Архив с правильным файлом продолжительности и исправленной утилитой. Можете скопировать только pdf и inf в каталог с голосом, не запуская утилиту. В командных файлах значение коэф-та а изменено на 0.55.
                              В прикрепленном архиве исправленная версия утилиты(создает правильный файл dur.pdf). Добавлен ключ для распаковки, см. встроенную справку.
                              Исходники залью в конце 2018 года.
                              Прикреплённый файлПрикреплённый файлupd1.rar (46,61 Кбайт, скачиваний: 34)
                              Сообщение отредактировано: webcoder88 -
                                Голос Alan,16кГц в новом формате https://drive.google.com/open?id=1VTAQsviK7...VkvyimCjnvQ55M2
                                Сообщение отредактировано: webcoder88 -
                                  Исходный код упаковщика hts-голосов RHVoice в новый формат 1.07(8). Код без оптимизации, но проверен и протестирован.

                                  Прикреплённый файлПрикреплённый файлhts_pack1.07_2.rar (75,24 Кбайт, скачиваний: 52)
                                  Сообщение отредактировано: webcoder88 -
                                    Кто-нибудь собирал последнюю версию RHVoice в Windows 7...8.1? Поделитесь опытом.

                                    Считаю, что в Linux - это ось командной строки, в которой всегда висит окно консоли для ввода команд, настройки окон, запуска программ, удаления создания файлов и тд. Не ось, а печатная машинка. Поэтому разработчикам Windos трудно перейти на Linux и наоборот. Есть VS для Linux, но все разработчики используют только python+scons.
                                    Я неспеша дорабатываю SALB. Уже воспроизводит русский голос, но еще много недоработок: английский акцент (тк я прикрутил только лексику из RHvoice), иногда неправильное ударение,
                                    пока не работают канал русского языка в 64-битной версия драйвера, хотя английский работает нормально и 64-битный бинарник hts_engine и cli( ошибка доступа к памяти при инициализации звукового интерфейса bp winmm.lib, но в драйвере в канале англ. языка работает без проблем).
                                    Сообщение отредактировано: webcoder88 -
                                      Доработка текстового анализатора SALB и flite для того, чтобы синтезатор произносил знакомую всем фразу "с++" или "c+++ --+=".

                                      В файл us_text.c вносятся следующие изменения:

                                      после кода
                                      ExpandedWrap disabled
                                        else
                                                r = val_append(en_exp_number(aaa),
                                                       cons_val(string_val("slash"),
                                                            en_exp_number(bbb)));
                                         
                                            if ((cst_regex_match(cst_rx_digits,ffeature_string(token,"p.name")))
                                                && (item_prev(token)))  /* don't mistake "0" as a number */
                                                r = cons_val(string_val("and"),r);
                                            cst_free(aaa);
                                            cst_free(bbb);
                                            }


                                      необходимо добавить
                                      ExpandedWrap disabled
                                            // этот код синтезирует первый символ -,+,=
                                            //  - + =       - --- + +++ = ===
                                            else if (cst_streq(name,"-"))
                                            {
                                            item_set_string(token,"punc","");
                                            r = cons_val(string_val("minus"),NULL);
                                            }
                                            else if (cst_streq(name,"+"))
                                            {
                                            item_set_string(token,"punc","");
                                            r = cons_val(string_val("plus"),NULL);
                                            }
                                            else if (cst_streq(name,"="))
                                            {
                                            item_set_string(token,"punc","");
                                            r = cons_val(string_val("it"),NULL);
                                            }


                                      и закоментировать следующий код:

                                      ExpandedWrap disabled
                                            /*
                                        // этот код удаляет "-" из синтезированной речи
                                            else if ((p=(strchr(name,'-'))))
                                            {   /* aaa-bbb */
                                            /*aaa = cst_strdup(name);
                                            aaa[cst_strlen(name)-cst_strlen(p)] = '\0';
                                            bbb = cst_strdup(p+1);
                                            if (cst_regex_match(cst_rx_digits,aaa) &&
                                                cst_regex_match(cst_rx_digits,bbb))
                                            {
                                                    ccc = cst_strdup(name);
                                                item_set_string(token,"name",bbb);
                                                r = us_tokentowords_one(token,bbb);
                                                item_set_string(token,"name",aaa);
                                                r = val_append(us_tokentowords_one(token,aaa),
                                                       cons_val(string_val("to"),r));
                                                item_set_string(token,"name",ccc);
                                                    cst_free(ccc);
                                            }
                                            else
                                                r = val_append(us_tokentowords_one(token,aaa),
                                                       us_tokentowords_one(token,bbb));
                                            cst_free(aaa);
                                            cst_free(bbb);
                                            }*/



                                      Следующий код в исходном файле разбивает токены вида "c++text" на несколько токенов (те для произнесения с++), но программа падает при токенизации кирилицы:
                                      ExpandedWrap disabled
                                            else if ((cst_strlen(name) > 1) && (!cst_regex_match(cst_rx_alpha,name)))
                                            {   /* its not just alphas */
                                            for (i=0; name[i] != '\0'; i++)
                                                if (text_splitable(name,i))
                                                break;
                                            aaa = cst_strdup(name);
                                            aaa[i+1] = '\0';
                                            bbb = cst_strdup(&name[i+1]);
                                            item_set_string(token,"nsw","nide");
                                            r = val_append(us_tokentowords_one(token,aaa),
                                                       us_tokentowords_one(token,bbb));
                                            cst_free(aaa);
                                            cst_free(bbb);
                                            }


                                      Проверочная строка "аз,+=и", кодировка ascii

                                      Запуск в отладчике с английским текстом показал, что программа заходит в блок, если name содержит знаки +-= (признаки токенизации).
                                      Если строка содержит кириллицу, то программа заходит в блок в любом случае.
                                      Ошибка.
                                      Функция cst_regex_match(cst_rx_alpha,name) возвращает false, если в строке нет символов "-+=", другие я не проверял.
                                      Функция hs_regexec(const cst_regex *prog, const char *string); проверяет на валидность и запускает предварительно откомпилированную функцию с объектным кодом. Видимо автор flite не дружит с встроенным ассемблером или защищает свои авторские права. См. файлы regexp.c и cst_regex.h .

                                      "Косяк" с кириллицей исправляется так
                                      //else if ((cst_strlen(name) > 1) && (!cst_regex_match(cst_rx_alpha,name)))

                                      else if ( (cst_strlen(name) > 1) && (strchr(name,'+') || strchr(name,'-') || strchr(name,'=')) )
                                      Сообщение отредактировано: webcoder88 -
                                        Пример синтеза дробей после доработки кода
                                        Прикреплённый файлПрикреплённый файлoutput_1.rar (135,78 Кбайт, скачиваний: 36)
                                        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                        0 пользователей:


                                        Рейтинг@Mail.ru
                                        [ Script Execution time: 0,3277 ]   [ 28 queries used ]   [ Generated: 16.10.19, 20:14 GMT ]