Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Речевые Технологии > Синтез речи в hts_engine.exe 1.05


Автор: webcoder88 12.12.15, 08:36
Утилита 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

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

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

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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 (, : 186)

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

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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 */

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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 и передать в эту функцию.

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

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    /* 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 добавлены строки:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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 12.12.15, 09:41
Цитата
Ради интереса, я изучаю эту тему уже несколько месяцев. Основная задача - улучшение качества синтезированного голоса.


Для улучшения синтезированного голоса нужно использовать больший объём тренировочных данных, более точное моделирование кепстра с помощью нейронных сетей (недоступно в 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

Автор: webcoder88 13.12.15, 02:07
Статьи на русском языке:

Вокодеры

Вокодеры и синтезаторы речи

Одновременная передача голоса и данных через канал тональной частоты

Курсы лекций

Автор: webcoder88 23.12.15, 05:26
Если вы знакомы с пакетом 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 25.12.15, 01:59
Несколько слов о методах восстановления правильной работы pitch.exe.
На вход вокодера hts необходимо подавать логарифм основного тона, сформированный по алгоритму RAPTOR, т.е использовать ключ -a с параметром 0(ноль).
Данный алгоритм реализуется кодом из из файла SPTK\pitch\snack\jkGetF0.c.
Вывод расчитанных значений осуществляется в стандартное устройство вывода stdout и реализован следующим образом:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
      
    // 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();

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

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    // 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():

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    //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 заголовочный файл и объявляются новые переменные:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    #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 25.12.15, 07:57
Вы можете сравнить синтезированную речь в sptk и hts по одинаковому спектру и основному тону.
В архиве hts_vocoder.exe, файлы основного тона и спектра для cmu_us_arctic_slt_a0001.raw.
При замене параметрических файлов необходимо учитывать значения частоты дискретизации (48) и период фрейма (240) (или 16 и 80).
hts_vokoder.rar (, : 183)

Автор: webcoder88 06.01.16, 11:42
Команды для создания файла основного логарифма тона и спектра из любого файла с частотой дискретизации 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.
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    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

Автор: webcoder88 29.01.16, 06:29
Ольга Яковлева писала, что модель длительности не реализована.
Файл меток, сгенерированный в RHVoice 0.4. Искажения устраняются в коде RHVoice путем корректировки длительности lf0
См функцию static void fix_f0(cst_utterance *u,HTS_Engine *e) {}

С др. стороны, я тренировал голос Елены в HTS, смысл слов улавливается с трудом, но модель длительности правильная. :)
lab.rar (, : 159)

Добавлено
командные файлы, нормальный темп
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 02.02.16, 12:33
Треск исчезает при снижении параметра частоты дескритизациидо 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

Автор: webcoder88 10.02.16, 08:02
Треск в hts_engine.exe убирается путем увеличения аудиобуфера с 1600 до 4800 байт или больше. Размер буфера задается ключем -z (-z 4800)
В flite+hts_engine размер аудиобуфера задается и меняется в исходнике. Требуется правка и перекомпиляция.

Автор: webcoder88 07.03.16, 02:15
Исходники и архив проекта hts_engine для отладки доступен на https://github.com/webcoder88/HTS_Engine/

Автор: webcoder88 27.03.16, 07:12
Обновление для голоса SLT голосового движка RHVoice доступно на https://github.com/webcoder88/arctic_slt_voice/
После обработки значений means и variances в lf0.pdf голос стал ближе к оригиналу.

Автор: nsh 28.03.16, 14:29
webcoder88, я Вам выложил http://alphacephei.com/test/tts/db-tts.zip базу sense&sensibility 10 часов речи (1Gb размер), попробуйте из неё лучше натренировать голос, гораздо лучше должен быть по качеству.

Автор: webcoder88 01.06.16, 07:34
nsh
Спасибо. Это руссская или английская база?

Автор: nsh 02.06.16, 16:21
английская

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)