今回は母音のみ。基本原理はこんな感じ。フォルマント式音声合成と呼ばれる。
- 基本周波数を元に原音を作る。歌声でラ(220Hz)の音なら220Hzの周波数で鳴らす。
- フォルマント(周波数のピーク)を元に原音を加工する。フォルマントになっている周波数領域を強調し、他を弱める。
加えて次のような処理が強く求められる
- 母音の連続をさせるために、フォルマント、基本周波数、音量の変動を行う処理。
以下のソース「あいうえお」としゃべる音声を出力するものである。
C言語ソース("voicesyn.c")
#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 SAMPLE_LENGTH (44100) #define FORMANT_LENGTH (5) #define IIR_LENGTH (3) 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); // RIFF ヘッダ var_long = file_size + 36; fwrite(&var_long, sizeof(long), 1, file_wave);// ファイルサイズ fwrite("WAVE", 1, 4, file_wave); // WAVE ヘッダ fwrite("fmt ", 1, 4, file_wave); // chunkID (fmt チャンク) var_long = 16; fwrite(&var_long, sizeof(long), 1, file_wave); // chunkSize (fmt チャンクのバイト数 無圧縮 wav は 16) var_short = PCM; fwrite(&var_short, sizeof(short), 1, file_wave); // wFromatTag (無圧縮 PCM = 1) fwrite(&ch, 2, 1, file_wave); // dwChannels (モノラル = 1, ステレオ = 2) fwrite(&sr, sizeof(long), 1, file_wave);// dwSamplesPerSec (サンプリングレート(Hz)) var_long = bytes * ch * sr; fwrite(&var_long, sizeof(long), 1, file_wave);// wdAvgBytesPerSec (Byte/秒) var_short = bytes * ch; fwrite(&var_short, sizeof(short), 1, file_wave);// wBlockAlign (Byte/サンプル*チャンネル) var_short = bytes * 8; fwrite(&var_short, sizeof(short), 1, file_wave); // wBitsPerSample (bit/サンプル) fwrite("data", 1, 4, file_wave);// chunkID (data チャンク) fwrite(&file_size, 4, 1, file_wave);// chunkSize (データ長 Byte) // ヘッダ (44 Byte) 書き込みここまで if (ftell(file_wave) != 44) { fprintf(stderr, "%s : wav header write error\n", filename); exit(1); } datawrite[ch-1][bytes-1](chL,chR,file_wave,length);// waveformData (データ) 書き込み fclose(file_wave); } /*----------------------*/ /* 基本波形作成エンジン */ /*----------------------*/ double SineWave(unsigned long i,unsigned long T,unsigned long sr,double vol) { /*Sine curve*/ return sin(2*M_PI*(double)(T * i)/ (double)sr) * vol; } double Wave(unsigned long i,unsigned long T,unsigned long sr,double vol) { /*Rosenburg波を出力する関数(声門閉鎖期はゼロとする)*/ double tau = 0.75,tau2=0.8; double z = ((double)(T * i)/ (double)sr); double w = (z - floor(z)); if(w <= tau*tau2) { return vol*( (3*w*w)/(tau*tau) - (2*w*w*w)/(tau*tau*tau)-0.5); }else if(w <tau2) { return vol*(1 - (w-tau)/((1-tau)*(1-tau))-0.5); }else{ return vol*(-0.5); } } 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] = 1.0; } /*------------------------*/ /* 音声合成もどきから引用 */ /*------------------------*/ double Simple_Wave(unsigned long i,unsigned long T,unsigned long sr,double vol) { double z = ((double)(T * i)/ (double)sr); return sin(z - floor(z)) * vol; } void Simple_filter(double fc, double Q, double a[], double b[]) { double r = 0.995;//exp(-M_PI * 50. * (1. + fc * fc * 1e-6 / 6.) / 44100.) double al = cos(2.0 * M_PI * fc); a[0] = 1; a[1] = - al; a[2] = r * r; b[0] = (1. - al + r * r); b[1] = 0; b[2] = 0; } double *Voice_production(double *wave_output,unsigned long length, unsigned long sr, double *ff1, double *ff2, void (*IIR)(double fc, double Q, double a[], double b[]), double (*basewave)(unsigned long i,unsigned long T,unsigned long sr,double vol),double fm ,double vol) { unsigned long i,j,k; double a[IIR_LENGTH],b[IIR_LENGTH],voice,tap[FORMANT_LENGTH][IIR_LENGTH]={0},output[FORMANT_LENGTH][IIR_LENGTH]={0}; double w; for(i=0;i<length;i++){ wave_output[i] = basewave(i,fm,sr,vol); voice = 0; for(k=0;k<5;k++){ tap[k][2]= tap[k][1];tap[k][1]= tap[k][0];tap[k][0]=wave_output[i]; output[k][2]= output[k][1];output[k][1]= output[k][0]; output[k][0] = 0; w = (double)(i+1.0) /(double)length; IIR(((1-w) * ff1[k] + w * ff2[k]) /44100,10,a,b); for(j=0;j<3;j++){ output[k][0] += (b[j] * tap[k][j]); } for(j=1;j<3;j++){ output[k][0] += -(a[j] * output[k][j]); } voice += output[k][0]; } //printf("%f",voice); wave_output[i] = voice / k; } return wave_output+i; } /*------------------------*/ /* 音声合成もどきから引用 */ /*------------------------*/ /* {800, 1300, 2500, 3500, 4500, 09},あ {250, 2100, 3100, 3500, 4500, 13},い {250, 1400, 2200, 3500, 4500, 12},う {450, 1900, 2400, 3500, 4500, 09},え {450, 900, 2600, 3500, 4500, 08}};お */ int main(void) { unsigned long i,j,k; short chL[SAMPLE_LENGTH]={0},chR[SAMPLE_LENGTH]={0}; double chwaveL[SAMPLE_LENGTH]={0},chwaveR[SAMPLE_LENGTH]={0}; unsigned long sr = 44100; unsigned short bytes = 2; unsigned short ch = 1; unsigned long points = SAMPLE_LENGTH; double ff[5][FORMANT_LENGTH]={ {800, 1300, 2500, 3500, 4500}, {250, 2100, 3100, 3500, 4500}, {250, 1400, 2200, 3500, 4500}, {450, 1900, 2400, 3500, 4500}, {450, 900, 2600, 3500, 4500} }; double *temp; /*サウンド生成*/ temp = Voice_production(chwaveL,8000,sr,ff[0],ff[1],IIR_resonator,Wave,110,20000); temp = Voice_production(temp,8000,sr,ff[1],ff[2],IIR_resonator,Wave,110,20000); temp = Voice_production(temp,8000,sr,ff[2],ff[3],IIR_resonator,Wave,110,20000); temp = Voice_production(temp,8000,sr,ff[3],ff[4],IIR_resonator,Wave,110,20000); temp = Voice_production(temp,8000,sr,ff[4],ff[0],IIR_resonator,Wave,110,20000); for(i=0;i<points;i++){ chL[i] = (short)floor(chwaveL[i]); } wavwrite(points,chL,chR ,"output.wav",ch,bytes,sr); return 0; }