#include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h> #include <math.h> #define PCM 1 #define BUFSIZE (1000) #ifndef UCHAR_MAX #define UCHAR_MAX (255) #endif #ifndef UCHAR_MIN #define UCHAR_MIN (0) #endif #ifndef SHRT_MAX #define SHRT_MAX (32767) #endif #ifndef SHRT_MIN #define SHRT_MIN (-32768) #endif #ifndef _MAX_PATH #define _MAX_PATH (255) #endif #define FORMANT_LENGTH (5) #define IIR_LENGTH (3) /*---------------------------*/ /* 発音ブロック データ構造体 */ /*---------------------------*/ typedef struct Voice_datum { double ffb[FORMANT_LENGTH];/*フォルマント(子音)*/ double volb;/*子音の音量*/ unsigned long lengthb[3];/*子音の入り方:フェードイン・長さ・フェードアウト*/ unsigned long lengthv[3];/*母音の入り方:空白・フェードイン・フォルマント移動帯*/ double ffv[FORMANT_LENGTH];/*フォルマント(母音)*/ double volv;/*母音の音量*/ double fc;/*基本周波数*/ unsigned long length;/*全声部の長さ*/ int conti;/*連結するかどうか。*/ }VoiceData; typedef struct Speak_tuple { double fc; double vol; unsigned long length; int vnum; int vnum2;/*2重母音用*/ int cnum; int conti; }SpeakData; /*-----------------------------*/ /* Waveファイル エラーチェック */ /*-----------------------------*/ void openerror(char *filename) { fprintf(stderr, "Can't open file: %s\n", filename); exit(1); } void formaterror(FILE *fp) { fprintf(stderr, "File format error : %ld\n", ftell(fp)); exit(1); } short datacheck(short data, short max, short min) { // オーバーフローする場合 + か - を表示 if (data > max) { fprintf(stderr, "+"); data = max; } if (data < min) { fprintf(stderr, "-"); data = min; } return data; } /*--------------------------------------*/ /* 16 bit データ -32768~32767 (無音 0) */ /* 8 bit データ 0~255 (無音 128) */ /*--------------------------------------*/ void _write_1ch1byte(short *data, short *dummy,FILE *wav, unsigned long count) { for(;count>0;(count--,data++)) { datacheck(*data, UCHAR_MAX, UCHAR_MIN); fputc(*data, wav); } } void _write_1ch2byte(short *data, short *dummy,FILE *wav, unsigned long count) { for(;count>0;(count--,data++)) { datacheck(*data, SHRT_MAX, SHRT_MIN); fwrite(data, sizeof(short), 1, wav); } } void _write_2ch1byte(short *left, short *right, FILE *wav, unsigned long count) { for(;count>0;(count--,left++,right++)) { datacheck(*left, UCHAR_MAX, UCHAR_MIN); datacheck(*right, UCHAR_MAX, UCHAR_MIN); fputc(*left, wav); fputc(*right, wav); } } void _write_2ch2byte(short *left, short *right, FILE *wav, unsigned long count) { for(;count>0;(count--,left++,right++)) { datacheck(*left, SHRT_MAX, SHRT_MIN); datacheck(*right, SHRT_MAX, SHRT_MIN); fwrite(left, sizeof(short), 1, wav); fwrite(right, sizeof(short), 1, wav); } } /*----------------------*/ /* wav ファイル書き出し */ /*----------------------*/ void wavwrite(unsigned long length, short *chL, short *chR, char *filename, unsigned short ch, unsigned short bytes, unsigned long sr) { unsigned long file_size, var_long; unsigned short var_short; FILE *file_wave; void (*datawrite[2][2])(short *left, short *right, FILE *wav, unsigned long count)= { {_write_1ch1byte,_write_1ch2byte}, {_write_2ch1byte,_write_2ch2byte} }; file_size = length * ch * bytes; if ((file_wave = fopen(filename, "wb")) == NULL) { openerror(filename); } fwrite("RIFF", 1, 4, file_wave); /* ファイルサイズ */ var_long = file_size + 36; fwrite(&var_long, sizeof(long), 1, file_wave); /* WAVEヘッダ */ fwrite("WAVE", 1, 4, file_wave); /* chunkID (fmt チャンク) */ fwrite("fmt ", 1, 4, file_wave); /* chunkSize (fmt チャンクのバイト数 無圧縮 wav は 16) */ var_long = 16; fwrite(&var_long, sizeof(long), 1, file_wave); /* wFromatTag (無圧縮 PCM = 1) */ var_short = PCM; fwrite(&var_short, sizeof(short), 1, file_wave); /* dwChannels (モノラル = 1, ステレオ = 2) */ fwrite(&ch, 2, 1, file_wave); /* dwSamplesPerSec (サンプリングレート(Hz)) */ fwrite(&sr, sizeof(long), 1, file_wave); /* wdAvgBytesPerSec (Byte/秒) */ var_long = bytes * ch * sr; fwrite(&var_long, sizeof(long), 1, file_wave); /* wBlockAlign (Byte/サンプル*チャンネル) */ var_short = bytes * ch; fwrite(&var_short, sizeof(short), 1, file_wave); /* wBitsPerSample (bit/サンプル) */ var_short = bytes * 8; fwrite(&var_short, sizeof(short), 1, file_wave); /* chunkID (data チャンク) */ fwrite("data", 1, 4, file_wave); /* chunkSize (データ長 Byte) */ fwrite(&file_size, 4, 1, file_wave); /* ヘッダ (44 Byte) 書き込みここまで */ if (ftell(file_wave) != 44) { fprintf(stderr, "%s : wav header write error\n", filename); exit(1); } /* waveformData (データ) 書き込み */ datawrite[ch-1][bytes-1](chL,chR,file_wave,length); fclose(file_wave); } /*----------------------*/ /* 各種波形生成エンジン */ /*----------------------*/ double SineWave(unsigned long T,unsigned long sr,double vol) { /*Sine curve*/ static double t; t += ((double)T / (double)sr); t = (t - floor(t)); return sin(2*M_PI*t) * vol; } double Wave(unsigned long T,unsigned long sr,double vol) { /*Rosenburg波を出力する関数*/ static double t; double t1 = 0.4; /*声門解放期における声門拡張期の割合*/ double t2= 0.4; /*一連の運動における声門解放期の割合*/ t += ((double)T / (double)sr); t = (t - floor(t)); if(t <= t1*t2){ return vol*( (3*t*t)/(t1*t1*t2*t2) - (2*t*t*t)/(t1*t1*t1*t2*t2*t2)); }else if(t <t2){ return vol*(1 - ((t/t2-t1)*(t/t2-t1))/((1-t1)*(1-t1))); }else{ return 0; } } double Wave2(unsigned long T,unsigned long sr,double vol) { static double t; double w; t += ((double)T / (double)sr); t = (t - floor(t)); w = sin(2*M_PI*t); w = (w<0)? - sqrt(-w) : sqrt(w); return w * vol; } double Wave3(unsigned long T,unsigned long sr,double vol) { /*のこぎり波を出力する関数*/ static double t; t += ((double)T / (double)sr); t = (t - floor(t)); return (t - 0.5)*2 * vol; } double GaussNoise(double vol) { /*擬似的なノイズ出力*/ return vol* ((double)rand() / (double)(RAND_MAX) - 0.5); } /*-------------------------*/ /* IIRフィルター作成・適用 */ /*-------------------------*/ void IIR_resonator(double fc, double Q, double *a, double *b) { fc = tan(M_PI * fc) / (2.0 * M_PI); a[0] = 1.0 + 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc; a[1] = (8.0 * M_PI * M_PI * fc * fc - 2.0) / a[0]; a[2] = (1.0 - 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc) / a[0]; b[0] = 2.0 * M_PI * fc / Q / a[0]; b[1] = 0.0; b[2] = -2.0 * M_PI * fc / Q / a[0]; a[0] = 0.0; } void IIR_LPF(double fc, double Q, double a[], double b[]) { fc = tan(M_PI * fc) / (2.0 * M_PI); a[0] = 1.0 + 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc; a[1] = (8.0 * M_PI * M_PI * fc * fc - 2.0) / a[0]; a[2] = (1.0 - 2.0 * M_PI * fc / Q + 4.0 * M_PI * M_PI * fc * fc) / a[0]; b[0] = 4.0 * M_PI * M_PI * fc * fc / a[0]; b[1] = 8.0 * M_PI * M_PI * fc * fc / a[0]; b[2] = 4.0 * M_PI * M_PI * fc * fc / a[0]; a[0] = 0.0; } void IIR_apply(double *a,double *b,double *tap,double *out,double base) { int k; for(k=IIR_LENGTH-1;k>0;k--) { tap[k]= tap[k-1]; out[k]= out[k-1]; } tap[0]=base;out[0] = 0; for(k=0;k<IIR_LENGTH;k++) { out[0] += (b[k] * tap[k]); } for(k=0;k<IIR_LENGTH;k++) { out[0] -= a[k] * out[k]; } } /*--------------------*/ /* さまざまの補助関数 */ /*--------------------*/ double _slide(unsigned long a,unsigned long b,double x1,double x2) { double w; if((b==0) || (a > b)) { return x2; }else{ w = ((double)a)/((double)b); } //w = (w<0.5)? w*w*2 : -2 * w * (w - 2) -1; //w = sin(M_PI * (w -0.5)) * 0.5 + 0.5; return (1-w) * x1 + w * x2; } /*------------------*/ /* 音声生成エンジン */ /*------------------*/ double *Voice_production(unsigned long chunks,double *wave_output,unsigned long sr,VoiceData *Voice1) { unsigned long h,i,j; double av[IIR_LENGTH],bv[IIR_LENGTH],ab[IIR_LENGTH],bb[IIR_LENGTH],ac[IIR_LENGTH],bc[IIR_LENGTH]; double voicev,tapv[FORMANT_LENGTH][IIR_LENGTH]={0},outv[FORMANT_LENGTH][IIR_LENGTH]={0},basev; double voiceb,tapb[FORMANT_LENGTH][IIR_LENGTH]={0},outb[FORMANT_LENGTH][IIR_LENGTH]={0},baseb; double tapc[IIR_LENGTH]={0},outc[IIR_LENGTH]={0}; for(h=0;h<chunks;h++) { printf("開始(音声生成%d)\n",h); for(i=0;i<Voice1[h].length;i++){ wave_output[i] = 0; /*母音*/ if(i > Voice1[h].lengthv[0]){ if((i < (Voice1[h].lengthv[0] + Voice1[h].lengthv[1]))){ basev = Wave(Voice1[h].fc,sr,_slide(i-Voice1[h].lengthv[0],(Voice1[h].lengthv[0] + Voice1[h].lengthv[1]),0,Voice1[h].volv)); }else if(/*(Voice1[h].conti !=0 ) &&*/(h+1<chunks) && (i > (Voice1[h].lengthv[0] + Voice1[h].lengthv[1]+ Voice1[h].lengthv[2]))){ basev = Wave(_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].length -(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].fc,Voice1[h+1].fc),sr,_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].length -(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].volv,Voice1[h+1].volv)); }else{ basev = Wave(Voice1[h].fc,sr,Voice1[h].volv); } //printf("%f",basev); IIR_LPF(3000.0 / 44100,0.7,ac,bc); IIR_apply(ac,bc,&tapc[0],&outc[0],basev); basev = outc[0]; //printf("%f\n",basev); voicev = 0; for(j=0;j< FORMANT_LENGTH ;j++){ if((Voice1[h].conti != 0) && (h+1 < chunks) && (i> Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2])){ IIR_resonator(_slide(i-(Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2] ),Voice1[h].length - (Voice1[h].lengthv[0] + Voice1[h].lengthv[1] + Voice1[h].lengthv[2]),Voice1[h].ffv[j],Voice1[h+1].ffv[j])/44100,7,av,bv); }else{ IIR_resonator(Voice1[h].ffv[j]/44100,7,av,bv); } IIR_apply(av,bv,tapv[j],outv[j],basev); voicev += outv[j][0]; } if ((i > Voice1[h].lengthv[0])){ wave_output[i] += _slide(i - Voice1[h].lengthv[0],Voice1[h].lengthv[1],0,voicev)/ FORMANT_LENGTH ; }else{ wave_output[i] += voicev/ FORMANT_LENGTH ; } } /*子音*/ if(i < Voice1[h].lengthb[0] + Voice1[h].lengthb[1] + Voice1[h].lengthb[2]){ if(i< Voice1[h].lengthb[0]){ baseb = GaussNoise(_slide(i,Voice1[h].lengthb[0],0,Voice1[h].volb)); }else if(Voice1[h].lengthb[0] + Voice1[h].lengthb[1] < i){ baseb = GaussNoise(_slide(i - (Voice1[h].lengthb[0] + Voice1[h].lengthb[1]),Voice1[h].length - (Voice1[h].lengthb[0] + Voice1[h].lengthb[1]),Voice1[h].volb,0)); }else{ baseb = GaussNoise(Voice1[h].volb); } voiceb = 0; for(j=0;j< FORMANT_LENGTH ;j++){ IIR_resonator(Voice1[h].ffb[j]/44100,5,ab,bb); IIR_apply(ab,bb,tapb[j],outb[j],baseb); voiceb += outb[j][0]; } wave_output[i] +=voiceb/ FORMANT_LENGTH ; } } wave_output+=i; } printf("実行終了(音声生成)\n"); return wave_output; } /*----------------------*/ /* 母音と子音の列から、 */ /* 発音ブロック列を作る */ /*----------------------*/ unsigned long formants(unsigned long chunks,VoiceData *voices,unsigned long sr,SpeakData *Speak1){ unsigned int h,i; unsigned long total=0; double ffv[][FORMANT_LENGTH + 1]= { //a/0 {800,1300,4500,3500,4500,0.8}, //i/1 {250,1900,4500,3500,4500,0.9}, //u/2 {400,1400,4500,3500,4500,0.9}, //e/3 {450,1800,4500,3500,4500,0.7}, //o/4 {450,900,4500,3500,4500 ,0.8}, //j/5 {300,1900,4500,3500,4500,1.0}, //w/6 {400,800,4500,3500,4500 ,0.6} }; VoiceData consonant[]= { // /00 {{0},0.0,{0},{0,0,500},{0},1,1,0,1}, //k/01 {{1400,2600,3200,3500,4500},0.8,{200,0,1200},{1000,2000,0},{0},1,1,0,1}, //kh/02 {{200,1320,3372,3889,4500},1.0,{200,0,1200},{1000,600,0},{0},1,1,0,1}, //k,/03 {{200,2700,4200,3500,4500},0.9,{200,0,2000},{1800,2000,0},{0},1,1,0,1}, //s/04 {{276,3446,4018,3500,4500},1.0,{4000,0,2000},{3000,2000,0},{0},1,1,0,1}, //sh/05 {{95,3738,4937,3500,4500},1.2,{4000,0,2000},{3000,2000,0},{0},1,1,0,1}, //t/06 {{500,2100,4300,3500,4500},1.0,{0,200,400},{500,400,0},{0},1,1,0,1}, //ch/07 {{3900,4900,5400,3500,4500},0.6,{0,0,2000},{1500,500,0},{0},1,1,0,1}, //ts/08 {{1500,4000,4700,3500,4500},0.6,{0,100,1000},{900,500,0},{0},1,1,0,1}, //h/09 {{250,1000,1400,3500,4500},0.6,{500,1000,3000},{3500,500,0},{0},1,1,0,1}, //h,/10 {{250,400,4500,3500,4500},0.5,{1000,800,3000},{3500,500,0},{0},1,1,0,1}, //p/11 {{241,2369,3604,4556,5473},0.4,{300,0,1000},{1000,1000,0},{0},1,1,0,1}, //n/12 {{0},0,{0},{0,500,0},{500,1359,4151,4031,4825},0.25,1,3000,0}, //m/13 {{0},0,{0},{0,500,500},{250,1200,3294,4308,4500},0.4,1,2000,0}, //r/14 {{0},0,{0},{0,1500,0},{375,2069,3745,3500,4500},0.5,1,2400,1}, //rj/15 {{0},0,{0},{0,1500,0},{248,2358,3847,3500,4500},0.5,1,2400,1}, //g/16 {{0},0,{0},{0,1500,1500},{400,800,2200,3500,4500},1.0,1,3000,1}, //z/17 {{338,2518,4761,3500,4500},0.8,{500,0,2000},{1000,1000,0},{324,1396,2445,3997,4915},0.8,1,3000,1}, //dg/18 {{150,3600,4500,3500,4500},0.6,{500,0,2000},{1000,1000,0},{400,2000,2800,3500,4500},0.6,1,2500,1}, //d/19 {{304,1353,2080,2923,4654},0.6,{300,0,2000},{800,800,0},{480,2106,3361,3880,5443},1.0,1,2500,1}, //b/20 {{200,250,400,3500,4500},0.3,{500,0,1000},{1000,1000,0},{500,1300,2500,3500,4500},0.8,1,2000,1}, }; for(h=0;h<chunks;h++) { *voices = consonant[Speak1[h].cnum]; total += Speak1[h].length; if(Speak1[h].cnum > 11) { (*voices).volb *= (*voices).volv * Speak1[h].vol; (*voices).volv *= Speak1[h].vol; (*voices).fc *= Speak1[h].fc; (*voices).conti = 1; Speak1[h].length -= (*voices).length; voices++; *voices = consonant[0]; } if(Speak1[h].vnum2 >0) { for(i=0;i < FORMANT_LENGTH;i++) { (*voices).ffv[i] = ffv[Speak1[h].vnum2][i]; } (*voices).volv *= ffv[Speak1[h].vnum2][FORMANT_LENGTH] * Speak1[h].vol; (*voices).volb *= (*voices).volv; (*voices).conti = 1; (*voices).length = 6000; (*voices).fc *= Speak1[h].fc; (*voices).conti *= Speak1[h].conti; Speak1[h].length -= (*voices).length; voices++; *voices = consonant[0]; } for(i=0;i < FORMANT_LENGTH;i++) { (*voices).ffv[i] = ffv[Speak1[h].vnum][i]; } (*voices).volv *=ffv[Speak1[h].vnum][FORMANT_LENGTH] * Speak1[h].vol ; (*voices).volb *= (*voices).volv; (*voices).fc *= Speak1[h].fc; (*voices).conti *= Speak1[h].conti; (*voices).length = Speak1[h].length; (*voices).lengthv[2]=(Speak1[h].length-(1500+(*voices).lengthv[0] + (*voices).lengthv[1]))*0.95; voices++; } return total; } /*------------------------*/ /* 1音節分のローマ字から */ /* 母音と子音の組を返す */ /*------------------------*/ unsigned long roman(char *c,SpeakData *speech) { int vnum=0,vnum2=0,cnum,count=0,conti=0; /*配列cは{母音,子音1,子音2}のようにデータが逆順だとする*/ printf("%c %c %c\n",c[0],c[1],c[2]); switch(c[0]) { case 'a':vnum=0;break; case 'i':vnum=1;break; case 'u':vnum=2;break; case 'e':vnum=3;break; case 'o':vnum=4;break; }; switch(c[1]) { case 'k': if(vnum==1) { cnum=1; }else if(vnum == 3){ cnum=2; }else{ cnum=3; }break; case 's': switch(c[2]) { case 't':if(vnum==2){vnum2=0;}else{vnum2=6;}cnum=8;break;/*ツァ・ツィ・ツ・ツェ・ツォ*/ default :cnum=4;break; };break; case 't':cnum=6;break; case 'n':cnum=12;break; case 'h': switch(c[2]) { case 'c':if(vnum==1){vnum2=0;}else{vnum2=5;}cnum=7;break;/*チャ・チ・チュ・チェ・チョ*/ case 's':if(vnum==1){vnum2=0;}else{vnum2=5;}cnum=5;break;/*シャ・シ・シュ・シェ・ショ*/ default :if(vnum==1){cnum=10;}else{cnum=9;}break; };break; case 'm': cnum=13;break; case 'y': if(vnum==1){vnum2=0;conti=1;}else{vnum2=5;} switch(c[2]) { case 'k':cnum=2;break; case 's':cnum=5;break; case 't':cnum=7;break; case 'n':cnum=12;break; case 'h':cnum=10;break; case 'm':cnum=13;break; case 'r':cnum=15;break; case 'g':cnum=16;break; case 'z':cnum=17;break; case 'j':cnum=18;break; case 'd':cnum=18;break; case 'b':cnum=20;break; case 'p':cnum=11;break; default :conti=1;cnum=0;break;/*後で修正する*/ };break; case 'w': if(vnum==2){vnum2=0;conti=1;}else{vnum2=6;} switch(c[2]) { case 'k':cnum=2;break; case 's':cnum=5;break; case 't':cnum=7;break; case 'n':cnum=12;break; case 'h':cnum=10;break; case 'm':cnum=13;break; case 'r':cnum=15;break; case 'g':cnum=16;break; case 'z':cnum=17;break; case 'j':cnum=18;break; case 'd':cnum=18;break; case 'b':cnum=20;break; case 'p':cnum=11;break; default :cnum=0;break;/*後で修正する*/ };break; case 'r':if(vnum==1){cnum=15;}else{cnum=14;};break; case 'g':cnum=16;break; case 'z':cnum=17;break; case 'j':cnum=18;if(vnum!=1){vnum2=5;}break; case 'd':cnum=19;break; case 'b':cnum=20;break; case 'p':cnum=11;break; default :conti=1;cnum=0;break;/*後で修正する*/ }; if(cnum > 11){count += 2;}else{count += 1;} if(vnum2 > 0){count += 1;} (*speech).cnum=cnum; (*speech).vnum=vnum; (*speech).vnum2=vnum2; (*speech).conti=conti; return count; } /*----------------------*/ /* 入力ファイルを読み */ /* 母音と子音の列を作る */ /*----------------------*/ /*---------------------------------------------*/ /* 入力ファイルの形式: */ /* 1行目: */ /* ベース音量、音節長、音程(省略可能) */ /* 2行目以降: */ /* ローマ字+音程(省略可能)+長さ(省略可能) */ /*---------------------------------------------*/ unsigned long linecount(char *filename) { FILE *file_script; unsigned long count=0; int c; if ((file_script = fopen(filename, "rb")) == NULL) { openerror(filename); } while((c = fgetc(file_script)) != EOF){ if(c == '\n'){count++;} } fclose(file_script); return count; } unsigned long makespeech(char *filename,double sr,SpeakData *speech) { FILE *file_script; unsigned long basetic= floor(sr*0.2),tic; double vol=20000,basefc=110,fc; char str[128],c,d,e,word[4]; unsigned long count=1; if ((file_script = fopen(filename, "r")) == NULL) { openerror(filename); } if(fgets(str, 128, file_script)==NULL) { openerror(filename); } sscanf(str,"%lf %lf %d",&vol,&basefc,&basetic); printf("音声データ%lf %lf %d\n",vol,basefc,basetic); while(fgets(str, 128, file_script)!=NULL) { tic = basetic; fc = basefc; sscanf(str,"%s %lf %d",word, &fc, &tic); count+=roman(word,speech); (*speech).fc = fc; (*speech).length = tic; (*speech).vol=vol; printf("%d %d %d %lf %lf %d \n",(*speech).vnum,(*speech).vnum2,(*speech).cnum,(*speech).vol,(*speech).fc,(*speech).length); speech++; } fclose(file_script); printf("%d\n",count); return count; } /*----------*/ /* main関数 */ /*----------*/ int main(void) { unsigned long i,j,k; short *chL,*chR; double *chwaveL,*chwaveR; unsigned long sr = 44100; unsigned short bytes = 2; unsigned short ch = 1; unsigned long points,count; SpeakData *speech; VoiceData *voices; /*サウンド生成*/ speech = (SpeakData *)calloc(linecount("input.txt"),sizeof(SpeakData)); count = makespeech("input.txt",sr,speech); voices = (VoiceData *)calloc(count,sizeof(VoiceData)); printf("子音+母音数 %d\n",count); points = formants(linecount("input.txt"),voices,sr,speech); printf("データ長 %d\n",points); chwaveL = (double *)calloc(points,sizeof(double)); chL = (short *)calloc(points,sizeof(short)); Voice_production(count,chwaveL,sr,voices); for(i=0;i<points;i++){ chL[i] = (short)floor(chwaveL[i]); } wavwrite(points,chL,chR ,"output.wav",ch,bytes,sr); free(speech); free(chwaveL); free(chL); return 0; }"input.txt" の内容を以下のように書くと、チューリップの歌を入力することになる。
1行目は左から、音量(Max 32767)、デフォルト周波数(Hz)、デフォルト長さ(ステップ)
2行目以降は 左から、1音分のローマ字表記(逆順)、周波数(Hz:省略可能)、長さ(ステップ:省略可能)
30000 110 20000 as 146 i 164 at 184 40000 as 146 i 164 at 184 40000 uhc 220 ir 184 up 164 on 146 ah 164 an 184 ag 164 40000 ar 146 ar 164 ar 184 40000 ar 146 ar 164 ar 184 40000 a 220 ak 184 ihs 164 or 146 ik 164 i 184 or 146 40000 od 220 on 220 ah 184 an 220 im 246 et 246 om 220 40000 ik 184 er 184 i 164 ad 164 an 146 80000
ちょっと音声合成について気になったので調べて見たらこのブログが……
返信削除fopenをfopen_sに書き換えたりするのに多少てこずりましたが、
何とかwavを生成できました。
ちょっと発音が濁っていたりと、最近のVOCALOIDなど
とは比べるのは酷ですが、
キチンと聞き取る事ができて感動です。
お礼ついでに、他にも動かしてみたい人のために、
if ((file_wave = fopen(filename, "wb")) == NULL)
if ((file_script = fopen(filename, "rb")) == NULL)
if ((file_script = fopen(filename, "r")) == NULL)
を
fopen_s(&file_wave,filename, "wb");
if ( file_wave == NULL)
fopen_s(&file_script,filename, "rb");
if ( file_script == NULL)
fopen_s(&file_script,filename, "r");
if ( file_script == NULL)
と書き換え、
sscanf(str,"%lf %lf %d",&vol,&basefc,&basetic);
sscanf(str,"%s %lf %d",word, &fc, &tic);
を
sscanf_s(str,"%lf %lf %d",&vol,&basefc,&basetic);
sscanf_s(str,"%s %lf %d",word,4*sizeof(char), &fc, &tic);
と書き換えればおkですね。
(コメ欄汚しすみません)
削除書き忘れ
#include
を
#define _USE_MATH_DEFINES
#include
に書き換え。
環境はWin7 Visual C++ 2012 の場合です。
(当然input.txtファイルは.cppファイルと同じディレクトリに置く。)
コメントありがとうございます。
返信削除そちらさまの制作環境はVC++でしょうか?
ともあれ、記述は確かにそのように直すべきですね。
たぶんIIRフィルタの処理はちょっとおかしいような気はします。
音声合成技術の論文でこのあたりがわからなかったので、
「第Nフォルマントでフィルターした波形」を単純に加算しています。
おそらく各フォルマントを一度にフィルターする仕組みを入れればいいと思いますが。。。
お世話になります。
返信削除環境は、VC++で間違い無いです。
勝手にコメントさせてもらいましたが、
よくよく考えてみれば、
おそらく私の環境で動かないだけでしょうね……。
(VC++でfopenを使おうとすると
「危ないから代わりにfopen_sを使え!」
とエラーを吐くのでコンパイルが通らないorz)
処理がおかしい、というのはノイズ?が乗っている事、でしょうか?(発振してる?)
デジタル信号処理は初めてなので、
IIRフィルタの事など、まだ分からない事も多いですが、
ここ数日暇を見てコードを読ませてもらっています。
しかし、読むだけだと今一分からないので、
自分でもコードを書いてみる……予定です^^
「ん」の波形をつくる場合は、どうすればよいのでしょうか?
返信削除子音 n の発音が「ん」の波形から母音の波形に遷移する形で作ってあるので、
返信削除その辺の処理を引用すればたぶんできるかと。何年も前なので、うろおぼですけれど。
ありがとうございます!
削除今更ながらですが、Rosenberg波を作るWave関数のところ、
返信削除t1*t2でなくてt1+t2ですね。
これ面白いですね。色々やってみたいです。