//■WAVファイル構成 // // バイト位置 バイト数 意味 //----------------------------------------------------------------------------------------- // 00 4 byte R' 'I' 'F' 'F' RIFFヘッダ   // 04 4 byte これ以降のファイルサイズ (ファイルサイズ - 8)     // 08 4 byte W' 'A' 'V' 'E' WAVEヘッダ RIFFの種類がWAVEであることをあらわす // 12 4 byte f' 'm' 't' ' ' (←スペースも含む) fmt チャンク フォーマットの定義 // 16 4 byte バイト数 fmt チャンクのバイト数 リニアPCM のとき 16(10 00 00 00) // 20 2 byte フォーマットID 参照 リニアPCM のとき 1(01 00) // 22 2 byte チャンネル数   モノラル のとき 1(01 00) ステレオ のとき 2(02 00) // 24 4 byte サンプリングレート Hz 44.1kHz のとき 44100(44 AC 00 00) // 28 4 byte データ速度 (Byte/sec)   44.1kHz 16bit ステレオ のとき  // 44100×2×2 = 176400(10 B1 02 00) // 30 2 byte ブロックサイズ (Byte/sample×チャンネル数) // 16bit ステレオ のとき2×2 = 4(04 00) // 32 2 byte サンプルあたりのビット数 (bit/sample) WAV フォーマットでは 8bit か // 16bit。16bit のとき 16(10 00) // 34 2 byte 拡張部分のサイズ   リニアPCMのとき存在しない // 38 n byte 拡張部分   リニアPCMのとき存在しない // 38 + n 4 byte d' 'a' 't' 'a' data チャンク 参照   // 42 + n 4 byte バイト数n 波形データのバイト数   // 46 + n m byte 波形データ #include "stdio.h" #include "stdlib.h" #include "math.h" #include "string.h" //*以下3行は再生用 #include "windows.h" //*再生用宣言 #include "MMSystem.h" //*再生用宣言 #pragma comment(lib,"winmm.lib")//*再生用宣言 // #define MAX(a,b) (((a)<(b))?(b):(a)) #define MIN(a,b) (((a)<(b))?(a):(b)) #define PI 3.14159265358979 // 44100Hz, 8bit, 1ch のWAVデータ #define SAMPLFRQ 44100 #define BIT 8 static double t=0; static double param[5][5] = {0}, delay[5][4] = {0}, formant[5]; static double male[5][5] = {// 共鳴周波数の変動値 // F1, F2, F3, F4, F5 {1.70,0.70,1.10,1.00,1.00}, //あ {0.65,1.50,1.10,1.00,1.00}, //い(「い」はもっと工夫が必要かもしれない) {0.80,0.90,0.90,1.00,1.00}, //う {1.20,1.30,1.10,1.00,1.00}, //え {1.15,0.50,1.20,1.00,1.00} //お }; static int size; static char *buf; void intToByte(char Big[], int Little){//32ビット整数格納 Big[0] = Little & 0xff; Big[1] = Little >> 8 & 0xff; Big[2] = Little >> 16 & 0xff; Big[3] = Little >> 24 & 0xff; } void shortToByte(char Big[], int Little){//16整数ビット格納 Big[0] = Little & 0xff; Big[1] = Little >> 8 & 0xff; } int wav_write(const char *filename, char *buf, int size){ int filesize = 44 + size; char *tmp; tmp = (char *) malloc(filesize);//領域確保 if(tmp==NULL) return 1; //■出力データ設定------------------------------------------------------------------------- memcpy(tmp, "RIFF", 4); // ■RIFFヘッダ intToByte(tmp+4,filesize - 8); // これ以降のファイルサイズ (ファイルサイズ - 8) memcpy(tmp+8, "WAVE", 4); // ■WAVEヘッダ memcpy(tmp+12, "fmt ", 4); // ■fmtチャンク intToByte(tmp+16,16 ); // バイト数 fmt チャンクのバイト数(リニアPCM のとき 16(10 00 00 00)) shortToByte(tmp+20, 1); // フォーマットID (リニアPCM のとき 1(01 00)) shortToByte(tmp+22, 1); // チャンネル数 モノラル のとき 1(01 00) intToByte(tmp+24,SAMPLFRQ ); // サンプリングレート intToByte(tmp+28,SAMPLFRQ * (BIT/8)); // データ速度 (Byte/sec) shortToByte(tmp+32, BIT / 8); // ブロックサイズ shortToByte(tmp+34, BIT); // サンプルあたりのビット数 memcpy(tmp+36, "data", 4); // ■data チャンク intToByte(tmp+40,size); // バイト数 memcpy(tmp + 44, buf, size); // 音声複写 //■ファイル出力---------------------------------------------------------------------------- FILE *fp = fopen(filename, "wb"); if (fp == NULL) return 2; fwrite(tmp, filesize, 1, fp); // ファイルへの出力 fclose(fp); // ファイルクローズ free(tmp); // 領域返却 return 0; } int bufSize(int sec){return (SAMPLFRQ*(BIT/8)) * sec;} double genRosenberg(double freq){//■Rosenberg波生成 double tau = 0.90; /* 声門開大期 */ double tau2 = 0.95; /* 声門閉小期 */ double sample = 0.0; t += (double)freq / (double)SAMPLFRQ; t -= floor(t); if (t <= tau ) sample = 3.0*pow(t/tau,2.0)-2.0*pow(t/tau,3.0); else if (t < tau+tau2) sample = 1.0-pow((t-tau)/tau2,2.0); return 2.0*(sample-0.5); } void setBPF(double f, double Q, double param[]){ // BPFフィルタ係数設定 double omega = 2.0 * PI * f / (double)SAMPLFRQ; double alpha = sin(omega) / (2.0 * Q); double a0 = 1.0 + alpha; param[0] = alpha / a0; param[1] = 0.0 / a0; param[2] = -alpha / a0; param[3] = -2.0 * cos(omega) / a0; param[4] = (1.0 - alpha) / a0; } double applyFilter(double base, double param[], double delay[]){// フィルタの適用 double sample = 0.0; sample += param[0] * base; sample += param[1] * delay[0]; sample += param[2] * delay[1]; sample -= param[3] * delay[2]; sample -= param[4] * delay[3]; delay[1] = delay[0]; delay[0] = base; delay[3] = delay[2]; delay[2] = sample; return sample; } void resoFrq(double formant[], int k, double vtlen){// ■共鳴周波数計算 for (int i = 0; i < 5; i++) { formant[i] = (double)((34000.0*(double)(2*i+1))/(4.0*vtlen)); formant[i] *= male[k][i]; } } double genP(double freq, double t){ return 100*t/SAMPLFRQ; } void genWave(char buf[], double formant[], int kk, double freq){// ■波形生成 int jst=750, jed=3000; double JD=(double)(jed-jst); for (int i = 0; i < size; i++) { if(i<5000){//子音部 buf[kk+i]=(char)(127.0*genP(freq, (double)i)*0.5/JD)+128; } else if(i<10000){//フォルマント遷移部 double w1=genP(freq, (double)i); double out = 0.0, in = genRosenberg(freq); for (int j = 0; j < 5; j++) { setBPF(formant[j], 20.0, param[j]); out += applyFilter(in, param[j], delay[j]); } double b, c=(double)(i-5000)*0.01;b=sqrt(1.0-c*c); double w=b*MIN(MAX(out, -1.0), 1.0)+(1-b)*w1*1.0*t; buf[kk+i] = (char)(127.0 * w + 128); } else{//母音部 double out = 0.0, in = genRosenberg(freq); for (int j = 0; j < 5; j++) { setBPF(formant[j], 20.0, param[j]); out += applyFilter(in, param[j], delay[j]); } double B=1; double c=(double)(i-2000)*0.005;if(i<4000) B=sqrt(1.0-c*c); buf[kk+i] = (char)(127.0 * B*MIN(MAX(out, -1.0), 1.0)) + 128; } } } int main(void){ t=0; size = SAMPLFRQ* 1; // 1 音節= 1 秒間とする buf = (char *) malloc(size*5); // 5 音節分のデータ長 double freq = 220.0; // 基本周波数 double vtlen = 15.0; // 声道の長さ(成人男性16.9cm,成人女性で14.1cmを目安) int kk=0; //バッファ格納位置初期値 0 for(int k=0; k<5; k++, kk+=size){ // kの値={ あ:0, い:1, う:2, え:3, お:4} resoFrq(formant, k, vtlen); // 共鳴周波数計算 genWave(buf, formant, kk, freq);// 波形生成 } int r = wav_write("test.wav", buf, size*5);// WAVファイル出力 if (r==0) { printf("\n 正常終了"); PlaySound(TEXT("test.wav"),NULL,SND_FILENAME|SND_ASYNC);//再生 } else if(r==1) printf("\n 領域確保失敗"); else if(r==2) printf("\n ファイルオープンエラー"); free(buf); getchar(); return 0; }