На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: RaD, nsh
  
> Синтез речи в hts_engine.exe 1.05
    Утилита hts_engine.exe 1.05 или 1.06 синтезирует речь, используя файл меток и файлы голоса (alan, elena, anna). В ранних версиях ТТС RHVoice, например,RHVoice 0.4-a2 использовались те же файлы голоса. Исходники утилиты, а также парсера pdf-файлов можно найти в Интернете.
    Ссылка на парсер HTS_PDFparser 1 A parser for hts_engine_API model pdf file https://github.com/naxingyu/HTS_PDFparser

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

    Пока понятно следующее:

    ExpandedWrap disabled
      HTS_engine.с
         HTS_Engine_create_pstream(&engine);  /* generate speech parameter vector sequence */ создать последовательность параметрических векторов речи
         HTS_Engine_create_gstream(&engine);  /* synthesize speech */       синтезировать речь
       
      if (wavfp)
            HTS_Engine_save_riff(&engine, wavfp); // сохранить синтезированную речь в файл
       
      HTS_PSTREAM.C
      /* HTS_PStreamSet_create: parameter generation using GV weight */  создание параметрических векторов
      HTS_Boolean HTS_PStreamSet_create(HTS_PStreamSet * pss, HTS_SStreamSet * sss, double *msd_threshold, double *gv_weight)
      {
      ...
      }
       
      HTS_GSTREAM.C
      /* HTS_GStreamSet_create: generate speech */      генерация и вывод речи
      /* (stream[0] == spectrum && stream[1] == lf0) */
      HTS_Boolean HTS_GStreamSet_create(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)
      {
      ...
      }


    Схему вокодера можно найти в статье Е.Е. Федоров, И.А. Шевцова, Численное исследование шипящих согласных звуков

    Во вложении архив с метками и командный файл.
    Прикреплённый файлПрикреплённый файлlab.rar (794 байт, скачиваний: 182)

    Добавлено
    Ниже приводится формат заголовка pdf-файла, полученный на основе информации из HTS_Model_load_pdf
    HTS_Model_load_pdf:

    ExpandedWrap disabled
      HEADER, Header_Size = 16
      +0: /* MSD flag */
          i=0 при msd_flag = FALSE
          i=1 при msd_flag = TRUE
       
      +4: /* stream size */
       
      +8: /* vector size */
       
      +12:/* the number of pdfs */


    далее /* means and variances */

    ExpandedWrap disabled
      elena
      +0: 0
      +4: 1
      +8: 75
      +12: 273


    Добавлено
    Сохранение меток в файл для последующего использования в hts_engine.exe

    Метки создаются в файле flite+hts_engine-1.02\lib\flite_hts_engine.c функцией void Flite_HTS_Engine_synthesis(Flite_HTS_Engine * f, char *txt, FILE * wavfp).

    Для сохранения в файл необходимо создать дескриптор открытого файла FILE * labfp в flite_hts_engine.c и передать в эту функцию.

    Ниже приведен код измененной функции. Асемблерная вставка используется для разъименовывания указателей. Мне так понятнее.

    ExpandedWrap disabled
      /* Flite_HTS_Engine_synthesis: speech synthesis */
      void Flite_HTS_Engine_synthesis(Flite_HTS_Engine * f, char *txt, FILE * wavfp, FILE * labfp)
      {
         int i;
         cst_voice *v = NULL;
         cst_utterance *u = NULL;
         cst_item *s = NULL;
         char **label_data = NULL;
         int label_size = 0;
      char *label_d2=NULL;
       
      char szln[]={0x0a,0};  
       
         /* text analysis part */
         v = REGISTER_VOX(NULL);
         if (v == NULL)
            return;
         u = flite_synth_text(txt, v);
         if (u == NULL)
            return;
         for (s = relation_head(utt_relation(u, "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(u, "Segment")); s;
              s = item_next(s), i++) {
            label_data[i] = (char *) calloc(MAXBUFLEN, sizeof(char));
            Flite_HTS_Engine_create_label(f, s, label_data[i]);
       
         }
       
         /* save Labels to file _out.lab */
       
         //fwrite((char*)"hhhh", sizeof(char), 4, labfp); //ok
         for (i = 0; i<label_size; i++){
          _asm{
          mov ecx, dword ptr[label_data]
          mov eax, dword ptr [i]
          imul eax,4
          add ecx, eax
          mov eax, [ecx]
          mov dword ptr[label_d2],eax
          }
         fwrite(label_d2, sizeof(char), strlen(label_d2), labfp);
         fwrite(szln, sizeof(char), 1, labfp);
         }
       
         /* speech synthesis part */
         HTS_Engine_load_label_from_string_list(&f->engine, label_data, label_size);
         HTS_Engine_create_sstream(&f->engine);
         HTS_Engine_create_pstream(&f->engine);
         HTS_Engine_create_gstream(&f->engine);
         if (wavfp != NULL)
            HTS_Engine_save_riff(&f->engine, wavfp);
         HTS_Engine_refresh(&f->engine);
       
         for (i = 0; i < label_size; i++)
            free(label_data[i]);
         free(label_data);
       
         delete_utterance(u);
         UNREGISTER_VOX(v);
      }


    В файл flite_hts_engine добавлены строки:

    ExpandedWrap disabled
      int main(int argc, char **argv)
      {
         int i;
         FILE *txtfp = stdin, *wavfp = NULL;
         FILE *labfp = NULL;   /*for labels*/
       ...
       
         /* read command */
         while (--argc) {
      ...
               case 'o':
                  wavfp = Getfp(*++argv, "wb");
                  --argc;
                  break;
               case 'z':                                         // <------- new
                  labfp = Getfp(*++argv, "w");           // <------- new
                  --argc;                                       // <------- new
                  break;                                        // <------- new  
               case 'h':
                  Usage();
                  break;
      ...
       
       /* synthesis */
         if (fgets(buff, INPUT_BUFF_SIZE, txtfp) != NULL && strlen(buff) > 0){
            Flite_HTS_Engine_synthesis(&engine, buff, wavfp, labfp);
         }
       
      ...
         fclose(labfp);  /*labs*/
       
         return 0;
      } //end of func
    Сообщение отредактировано: nsh -
      Цитата
      Ради интереса, я изучаю эту тему уже несколько месяцев. Основная задача - улучшение качества синтезированного голоса.


      Для улучшения синтезированного голоса нужно использовать больший объём тренировочных данных, более точное моделирование кепстра с помощью нейронных сетей (недоступно в open source пока) и более точные вокодеры, например, mixed excitation (реализовано в openmary), STRAIGHT (поддерживается в hts, но под плохой лицензией), aHM (реализовано в covarep).

      Для понимания работы читать код не очень полезно, лучше прочесть следующие статьи.

      An experimental comparison of multiple vocoder types

      Restructuring speech representations using a pitch-adaptivetime±frequency smoothing and an instantaneous-frequency- based F0 extraction: Possible role of a repetitive structure in sounds

      Mel-generalized cepstral analysis - a unified approach to speech spectral estimation
          Если вы знакомы с пакетом SPTK, то можно поэкспериментировать с частотой дискретизации и др. параметрами и оценить качество синтезированной речи.
          Исходный файлы:
          elena_label - любой звуковой файл в формате raw c частотой дискретизации 16..48кГц.
          __label2.f0 - лог основного тона.
          __label2.sp - кепстры от спектра.

          Дополнительная информация содержится в документации SPTK: SPTKref-3.7.pdf и SPTKexamples-3.7.pdf.

          Создание файла параметров data.pitch. При некоторых условиях - это файл, аналогичный __label2.f0.

          x2x +sf elena_label.raw | pitch -a 0 -s 16 -p 80 -L 60 -H 240 -o 1 > data.pitch

          Синтез и воспроизведение синтезированной речи:

          sopr -m 0.4 data.pitch | excite -p 80 | mlsadf -m 24 -a 0.42 -p 80 __label2.sp | tee data.mcep.high.syn | da +f -s 16

          или так:
          sopr -m 0.4 -EXP __label2.f0 | excite -p 80 | mlsadf -m 24 -a 0.42 -p 80 __label2.sp | tee data.mcep.high.syn | da +f -s 16


          c понижением тона (sopr -m2)
          sopr -m 2 data.pitch | excite -p 80 | mlsadf -m 24 -a 0.42 -p 80 __label2.sp | tee data.mcep.high.syn | da +f -s 16


          Преобразование формата с плавающей запятой в формат short (raw):

          x2x +fs < data.mcep.high.syn > elena_label_syn_.raw


          Качество синтезированной речи отличается от речи, получаемой на выходе вокодера hts (__label2.f0 + __label2.f0, без постфильтрации, тк размер файла с коэффициентами фильтра равен нулю).

          Добавлено
          Если вы будете использовать файл, сгенерированный по моим меткам, то следует учесть, что размер __label2.f0 при любых знач. ключа -a утилиты pitch должен быть равен 996 байт.
          Утилита pitch SPTK 3.7, собранная в Windows работает неправильно и выдает файлы со следующим размером (-а 0...2)996, 1000, 997 байт.
          Сообщение отредактировано: webcoder88 -
            Несколько слов о методах восстановления правильной работы pitch.exe.
            На вход вокодера hts необходимо подавать логарифм основного тона, сформированный по алгоритму RAPTOR, т.е использовать ключ -a с параметром 0(ноль).
            Данный алгоритм реализуется кодом из из файла SPTK\pitch\snack\jkGetF0.c.
            Вывод расчитанных значений осуществляется в стандартное устройство вывода stdout и реализован следующим образом:

            ExpandedWrap disabled
                
              // jkGetF0.c
                for (i = 0; i < fnum; i++) {
                    switch (otype) {
                    case 1:                   /* f0 */
                        fwrite(tmp + i, sizeof(float), 1, stdout); // иногда записывает 5 байтов вместо 4
                
                        break;
                    case 2:                   /* log(f0) */
                        if (tmp[i] != 0.0 ) {
                            tmp[i] = (float)log(tmp[i]);
                        } else {
                            tmp[i] = -1.0E10;
                        }
                        fwrite(tmp + i, sizeof(float), 1, stdout); // иногда записывает 5 байтов вместо 4
                        break;
                    default:                  /* pitch */
                        if (tmp[i] != 0.0) {
                            tmp[i] = sample_freq / tmp[i];
                        }
                        fwrite(tmp + i, sizeof(float), 1, stdout); // иногда записывает 5 байтов вместо 4
                        break;
                    }
                }


            Я достаточно быстро нашел неправильно записывемое значение для raw-файла, генерируемого по моим меткам. Это значение с индексом 89.
            Неправильное значение записывет функция fwrite();

            Для восстановления правильной работы можно использовать следующий вариант:

            ExpandedWrap disabled
              // jkGetF0.c
               
                  hOut = GetStdHandle (STD_OUTPUT_HANDLE  );
                  if (hOut == INVALID_HANDLE_VALUE)
                      return 0;
               
               
                for (i = 0; i < fnum; i++) {
                    switch (otype) {
                    case 1:                   /* f0 */
                        //fwrite(tmp + i, sizeof(float), 1, stdout); // иногда записывает 5 байтов вместо 4
                        WriteFile (hOut, tmp + i, sizeof(float), &RW, NULL); //ok
                        break;
                    case 2:                   /* log(f0) */
                        if (tmp[i] != 0.0 ) {
                                          //printf("%f \n", (float)tmp[i]);
                            tmp[i] = (float)log(tmp[i]);
                                          //printf(" %f \n", (float)tmp[i]);
                        } else {
                            tmp[i] = -1.0E10;
                        }
                        //fwrite(tmp + i, sizeof(float), 1, stdout); // иногда записывает 5 байтов вместо 4
                        WriteFile (hOut, tmp + i, sizeof(float), &RW, NULL);
                        break;
                    default:                  /* pitch */
                        if (tmp[i] != 0.0) {
                            tmp[i] = sample_freq / tmp[i];
                        }
                        //fwrite(tmp + i, sizeof(float), 1, stdout); // иногда записывает 5 байтов вместо 4
                        WriteFile (hOut, tmp + i, sizeof(float), &RW, NULL);
                        break;
                    }
                }



            Второй алгоритм из каталога swipe, реализуемый ключом -а 1, выполняет правильный вывод, тк использует др. функцию fwritef():

            ExpandedWrap disabled
              //swipe.c
              #else
                  for (i = 0; i < p.x; i++) {
                     switch(otype) {
                        case 1:      /* f0 */
                           fwritef(&p.v[i], sizeof(p.v[i]), 1, stdout);
                           break;
                        case 2:      /* log(f0) */
                           if (p.v[i] != 0.0)
                              p.v[i] = log(p.v[i]);
                           else
                              p.v[i] = -1.0E10;
                           fwritef(&p.v[i], sizeof(p.v[i]), 1, stdout);
                           break;
                        default:     /* pitch */
                           if (p.v[i] != 0.0)
                              p.v[i] = samplerate / p.v[i];
                           fwritef(&p.v[i], sizeof(p.v[i]), 1, stdout);
                           break;
                     }
                  }
                  freev(p);
              #endif
              }


            Добавлено
            В начале файла jkGetF0.c добавляется 1 заголовочный файл и объявляются новые переменные:

            ExpandedWrap disabled
              #include <windows.h>
              //...
               
              DWORD RW;
              HANDLE hOut;
               
              //перед этим кодом:
              #if 0
              int
              Get_f0(Sound *sound, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])
              {


            Добавлено
            С генерацией спектра в SPTK предлагаю разобраться самостоятельно. Советую запастись терпением. :) Опубликую в следующем году.
            Сообщение отредактировано: webcoder88 -
              Вы можете сравнить синтезированную речь в sptk и hts по одинаковому спектру и основному тону.
              В архиве hts_vocoder.exe, файлы основного тона и спектра для cmu_us_arctic_slt_a0001.raw.
              При замене параметрических файлов необходимо учитывать значения частоты дискретизации (48) и период фрейма (240) (или 16 и 80).
              Прикреплённый файлПрикреплённый файлhts_vokoder.rar (97,64 Кбайт, скачиваний: 176)
                Команды для создания файла основного логарифма тона и спектра из любого файла с частотой дискретизации 48кГц.

                Создание файла логарифма основного тона:

                x2x +sf < cmu_us_arctic_slt_a0001.raw > data.float
                pitch -a 0 -s 48 -p 240 -L 60 -H 240 -o 2 data.float > __label2.f0
                pause

                Создание файла мел-кепстральных коэффициентов спектра:
                x2x +sf < cmu_us_arctic_slt_a0001.raw | frame -l 512 -p 240 | window -l 512 -L 512 | mcep -l 512 -m 24 -a 0.52 > __label2.sp
                pause

                Для команды вуказанном формате основная тонкость состоит в следующем: для правильного синтеза речи в программе hts_vokoder параметр -l должен быть равен параметру -L.
                Значение должно быть кратно 256 и может быть равно n*256. Если указать разные парамеры, то появятся ошибки.
                В некоторых случаях утилиты sptk укажут на необходимость указывать параметр -e, после чего можно указать -l 1200 и -L 2048
                В тренировочных скриптах hts значение пар-ра -e равно 1.0E-08.

                Если параметр -m равен 25, то после генерации файла трассы размер вектора становится равен 26-ти, хотя речь воспроизводится правильно. Поэтому нужно указывать значение, на единицу меньше.

                Добавлено
                x2x +sf < cmu_us_arctic_slt_a0001.raw | frame -l 1200 -p 240 | window -l 1200 -L 2048 | mcep -l 2048 -m 24 -a 0.52 > __label2.sp
                pause

                или

                x2x +sf < cmu_us_arctic_slt_a0001.raw | frame -l 1200 -p 240 | window -l 1200 -L 2048 -w 1 -n 1 | mcep -l 2048 -m 24 -a 0.52 -e 1.0E-08 > __label2.sp
                pause

                воспроизведение
                hts_vocoder.exe -s 48000 -p 240 -a 0.52 -g 0 -l -b 0 -or cmu_us_arctic_slt_a0001_.raw -jm __label2.sp -jf __label2.f0

                Тренировочный скрипт для bash.
                ExpandedWrap disabled
                  mgc:
                  # Extracting MGC or MGC-LSP coefficients from raw audio
                  mkdir -p mgc
                  for raw in raw/cmu_us_arctic_slt_*.raw; do \
                          base=`basename ${raw} .raw`; \
                          min=`${X2X} +sf ${raw} | ${MINMAX} | ${X2X} +fa | head -n 1`; \
                          max=`${X2X} +sf ${raw} | ${MINMAX} | ${X2X} +fa | tail -n 1`; \
                          if [ -s ${raw} -a ${min} -gt -32768 -a ${max} -lt 32767 ]; then \
                              ${X2X} +sf ${raw} > tmp; \
                              if [ 0       -eq 0 ]; then \
                                  echo "Extracting MGC coefficients from ${raw}"; \
                                  c:/usr/local/SPTK/bin/frame -l ${FRAMELEN}    -p ${FRAMESHIFT}  tmp | \
                                  c:/usr/local/SPTK/bin/window -l ${FRAMELEN}    -L ${FFTLEN}      -w 1  -n 1   | \
                                  c:/usr/local/SPTK/bin/mcep -a ${FREQWARP}    -m ${MGCORDER}    -l ${FFTLEN}      -e 1.0E-08 > mgc/${base}.mgc; \
                              else \
                                  echo "Extracting MGC-LSP coefficients from ${raw}"; \
                                  SAMPKHZ=`expr ${SAMPFREQ}    / 1000`; \
                                  if [ 1      -eq 1 ]; then \
                                      GAINOPT="-l"; \
                                  fi; \
                                  c:/usr/local/SPTK/bin/frame -l ${FRAMELEN}    -p ${FRAMESHIFT}  tmp | \
                                  c:/usr/local/SPTK/bin/window -l ${FRAMELEN}    -L ${FFTLEN}      -w 1  -n 1   | \
                                  c:/usr/local/SPTK/bin/mcep -a ${FREQWARP}    -c 0       -m ${MGCORDER}    -l ${FFTLEN}      -e 1.0E-08 -o 4 | \
                                  c:/usr/local/SPTK/bin/lpc2lsp -m ${MGCORDER}    -s ${SAMPKHZ} ${GAINOPT} -n ${FFTLEN}      -p 8 -d 1.0E-08 > mgc/${base}.mgc; \
                              fi; \
                              if [ -n "`c:/usr/local/SPTK/bin/nan mgc/${base}.mgc`" ]; then \
                                  echo " Failed to extract MGC coefficients from ${raw}"; \
                                  rm -f mgc/${base}.mgc; \
                              fi; \
                          fi; \
                      done
                  Ольга Яковлева писала, что модель длительности не реализована.
                  Файл меток, сгенерированный в RHVoice 0.4. Искажения устраняются в коде RHVoice путем корректировки длительности lf0
                  См функцию static void fix_f0(cst_utterance *u,HTS_Engine *e) {}

                  С др. стороны, я тренировал голос Елены в HTS, смысл слов улавливается с трудом, но модель длительности правильная. :)
                  Прикреплённый файлПрикреплённый файлlab.rar (3,25 Кбайт, скачиваний: 155)

                  Добавлено
                  командные файлы, нормальный темп
                  hts_engine.exe -td elena/tree-dur.inf -tf elena/tree-lf0.inf -tm elena/tree-mgc.inf -md elena/dur.pdf -mf elena/lf0.pdf -mm elena/mgc.pdf -dm elena/mgc.win1 -dm elena/mgc.win2 -dm elena/mgc.win3 -df elena/lf0.win1 -df elena/lf0.win2 -df elena/lf0.win3 -s 16000 -p 80 -a 0.42 -g 0 -l -b 0 -ow elena_label.wav -ot elena_label.trace -om __label.sp -of __label.f0 __label.txt

                  командные файлы, замедленный темп (у диктора немного язык заплетается)
                  hts_engine.exe -td elena/tree-dur.inf -tf elena/tree-lf0.inf -tm elena/tree-mgc.inf -md elena/dur.pdf -mf elena/lf0.pdf -mm elena/mgc.pdf -dm elena/mgc.win1 -dm elena/mgc.win2 -dm elena/mgc.win3 -df elena/lf0.win1 -df elena/lf0.win2 -df elena/lf0.win3 -s 16000 -p 160 -a 0.42 -g 0 -l -b 0 -ow elena_label.wav -ot elena_label.trace -om __label.sp -of __label.f0 __label.txt

                  Добавлено
                  В моем тренированном голосе HTS-demo_CMU-ARCTIC-SLT во время синтеза присутствует посторонний шума в виде треска. При воспроизведении сохраненного звукового файла треск не слышен. Когда фильтровать сигнал? Тренировочные данные или кепстры, сохраненные в базе голоса?

                  Добавлено
                  Тренированный голос. При синтезе обучающего предложения, полученная речь очень похожа на оригинал. Остается убрать треск.
                  https://www.sendspace.com/file/qer3vn
                  Сообщение отредактировано: webcoder88 -
                    Треск исчезает при снижении параметра частоты дескритизациидо 32000 или 16000 (кратно изменяется параметр фпериод 160 или 80), но основной тон голоса изменяется.
                    Просьба протестировать на своих системах и отписать результаты. Пока выход один - использовать обучающие файлы с частотой дискр 16000 (32000)Гц.

                    hts_engine.exe -td elena/tree-dur.inf -tf elena/tree-lf0.inf -tm elena/tree-mgc.inf -md elena/dur.pdf -mf elena/lf0.pdf -mm elena/mgc.pdf -dm elena/mgc.win1 -dm elena/mgc.win2 -dm elena/mgc.win3 -df elena/lf0.win1 -df elena/lf0.win2 -df elena/lf0.win3 -s 32000 -p 160 -a 0.42 -g 0 -l -b 0 -ow elena_label.wav -ot elena_label.trace -om __label.sp -of __label.f0 __label.txt
                      Треск в hts_engine.exe убирается путем увеличения аудиобуфера с 1600 до 4800 байт или больше. Размер буфера задается ключем -z (-z 4800)
                      В flite+hts_engine размер аудиобуфера задается и меняется в исходнике. Требуется правка и перекомпиляция.
                        Исходники и архив проекта hts_engine для отладки доступен на https://github.com/webcoder88/HTS_Engine/
                          Обновление для голоса SLT голосового движка RHVoice доступно на https://github.com/webcoder88/arctic_slt_voice/
                          После обработки значений means и variances в lf0.pdf голос стал ближе к оригиналу.
                            webcoder88, я Вам выложил http://alphacephei.com/test/tts/db-tts.zip базу sense&sensibility 10 часов речи (1Gb размер), попробуйте из неё лучше натренировать голос, гораздо лучше должен быть по качеству.
                              nsh
                              Спасибо. Это руссская или английская база?
                              Сообщение отредактировано: webcoder88 -
                                английская
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0538 ]   [ 21 queries used ]   [ Generated: 28.03.24, 20:36 GMT ]