Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.144.105.101] |
|
Сообщ.
#1
,
|
|
|
Утилита 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 Я пытаюсь разобраться в алгоритме синтеза речи по базе данных голоса. Если со структурой файлов базы данных голса более-менее все понятно, то алгоритм восстановления речи по меткам и кепстрам из базы данных требует хорошего знания принципов работы вокодера. Необходимо создать программу для восстановления звукового корпуса произвольного вектора базы данных и его вывода в виде графика. Ради интереса, я изучаю эту тему уже несколько месяцев. Основная задача - улучшение качества синтезированного голоса. Приглашаю всех заинтересованных в этой темы. Пока понятно следующее: 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 байт, скачиваний: 198) Добавлено Ниже приводится формат заголовка pdf-файла, полученный на основе информации из HTS_Model_load_pdf HTS_Model_load_pdf: 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 */ 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 и передать в эту функцию. Ниже приведен код измененной функции. Асемблерная вставка используется для разъименовывания указателей. Мне так понятнее. /* 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 добавлены строки: 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 |
Сообщ.
#2
,
|
|
|
Цитата Ради интереса, я изучаю эту тему уже несколько месяцев. Основная задача - улучшение качества синтезированного голоса. Для улучшения синтезированного голоса нужно использовать больший объём тренировочных данных, более точное моделирование кепстра с помощью нейронных сетей (недоступно в 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 |
Сообщ.
#3
,
|
|
|
Сообщ.
#4
,
|
|
|
Если вы знакомы с пакетом 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 байт. |
Сообщ.
#5
,
|
|
|
Несколько слов о методах восстановления правильной работы pitch.exe.
На вход вокодера hts необходимо подавать логарифм основного тона, сформированный по алгоритму RAPTOR, т.е использовать ключ -a с параметром 0(ноль). Данный алгоритм реализуется кодом из из файла SPTK\pitch\snack\jkGetF0.c. Вывод расчитанных значений осуществляется в стандартное устройство вывода stdout и реализован следующим образом: // 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(); Для восстановления правильной работы можно использовать следующий вариант: // 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(): //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 заголовочный файл и объявляются новые переменные: #include <windows.h> //... DWORD RW; HANDLE hOut; //перед этим кодом: #if 0 int Get_f0(Sound *sound, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[]) { Добавлено С генерацией спектра в SPTK предлагаю разобраться самостоятельно. Советую запастись терпением. Опубликую в следующем году. |
Сообщ.
#6
,
|
|
|
Вы можете сравнить синтезированную речь в sptk и hts по одинаковому спектру и основному тону.
В архиве hts_vocoder.exe, файлы основного тона и спектра для cmu_us_arctic_slt_a0001.raw. При замене параметрических файлов необходимо учитывать значения частоты дискретизации (48) и период фрейма (240) (или 16 и 80). Прикреплённый файлhts_vokoder.rar (97,64 Кбайт, скачиваний: 196) |
Сообщ.
#7
,
|
|
|
Команды для создания файла основного логарифма тона и спектра из любого файла с частотой дискретизации 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. 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 |
Сообщ.
#8
,
|
|
|
Ольга Яковлева писала, что модель длительности не реализована.
Файл меток, сгенерированный в RHVoice 0.4. Искажения устраняются в коде RHVoice путем корректировки длительности lf0 См функцию static void fix_f0(cst_utterance *u,HTS_Engine *e) {} С др. стороны, я тренировал голос Елены в HTS, смысл слов улавливается с трудом, но модель длительности правильная. Прикреплённый файлlab.rar (3,25 Кбайт, скачиваний: 173) Добавлено командные файлы, нормальный темп 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 |
Сообщ.
#9
,
|
|
|
Треск исчезает при снижении параметра частоты дескритизациидо 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 |
Сообщ.
#10
,
|
|
|
Треск в hts_engine.exe убирается путем увеличения аудиобуфера с 1600 до 4800 байт или больше. Размер буфера задается ключем -z (-z 4800)
В flite+hts_engine размер аудиобуфера задается и меняется в исходнике. Требуется правка и перекомпиляция. |
Сообщ.
#11
,
|
|
|
Исходники и архив проекта hts_engine для отладки доступен на https://github.com/webcoder88/HTS_Engine/
|
Сообщ.
#12
,
|
|
|
Обновление для голоса SLT голосового движка RHVoice доступно на https://github.com/webcoder88/arctic_slt_voice/
После обработки значений means и variances в lf0.pdf голос стал ближе к оригиналу. |
Сообщ.
#13
,
|
|
|
webcoder88, я Вам выложил http://alphacephei.com/test/tts/db-tts.zip базу sense&sensibility 10 часов речи (1Gb размер), попробуйте из неё лучше натренировать голос, гораздо лучше должен быть по качеству.
|
Сообщ.
#14
,
|
|
|
nsh
Спасибо. Это руссская или английская база? |
Сообщ.
#15
,
|
|
|
английская
|