2012年8月10日

『おおかみこどもの雨と雪』感想

すごくクオリティが高い映画です。しかしながらリアル成分とファンタジー成分の配分がどうにもよくない、と感じました。全体的にはふわふわしており生活感があまりない構成です。背景をリアルに、人物の陰影を浅くして輪郭線をを茶色にしているので、これは意図的でしょう。その割にリアルにしようとしている描写が多いことに違和感があります。それはとくに前半に多く、例えば、狼男と子供を作るところはさらっと流しているのに、その子供が体の具合を悪くしたときに、母親が小児科か動物病院かで迷った末電話で相談して解決するところとかです。娘の雪が小学校の同級生の男子に「犬くさい」と指摘されて、自分が狼人間だということもあってすごく気にするのですが、それなら旦那の狼男も犬くさかったはずだと思いました。あと旦那の狼男は狼の姿で死ぬのですが、その死骸をごみ収集車に放り込んでいるシーンは保健所的にありなんだろうか?頭尾長1メートル越えのようだしそのあたりどうなんだろ? 子供たちは狼になったりヒトになったりするのです。遅かれ早かれアイデンティティの問題に直面することが明らかにわかります。子供たちはそれぞれに、姉の雪はヒトとして、弟の雨は狼として生きることに決めるのですが、その辺を旦那の狼男と相談したんだろうかということが、どうにも引っかかりました。また弟の雨は狼として生きるため、二度と「料理」を食べないわけですが、その辺名残惜しくはないのだろうか?例えば「平成狸合戦ぽんぽこ」では人間にまぎれるかどうかでという状況で「料理」に言及した狸がいました。まあ「おおかみこどもの雨と雪」と「平成狸合戦ぽんぽこ」の違いは同属ネットワークの有無なのかもしれません。 この映画では、本人たちはきちんと自分の生きるアイデンティティを見出したわけですが、親は選択肢を与えただけなので、それは運が良かったに過ぎません。ドラマ性を出すために、あるいは性善説にもとづいてか、こういったシチュエーションで親は選択肢だけ与え、ビジョンを示さないことが通例ですが、それって本当に正しい態度なのでしょうか?姉の雪はヒトとして生きますが、結婚して子供をつくった場合、似たような問題が発生しませんかね?

2012年5月1日

音声合成(3)

音声データ入力を自動化する処理を追加した。
#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

2011年10月17日

音声合成(2)

前回のコードでは基本周波数を変動しつつしゃべらせると波形がおかしくなる。それを改善する。サインカーブを描くコードは以下のように書かれる。(math.hをincludeすることが前提)
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;
}
それを次のように修正する。時刻 i を必要としなくなり、代わりに静的変数として energy を用いる。
double SineWave(unsigned long T,unsigned long sr,double vol)
{
    /*Sine curve*/
    static double energy;
    energy += ((double)T / (double)sr);
    energy = (energy - floor(energy));
    return sin(2*M_PI*energy) * vol;
}
この2つのコードは、基本周波数が変動しないときには同じ値を返す。変更後の関数では基本周波数が変動しても波形が急激に変動することはない。よく考えれば当たり前の話であった。

さらに子音の発音処理も追加して、以下のコードをかいた。

#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 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 energy;
    energy += ((double)T / (double)sr);
    energy = (energy - floor(energy));
    return sin(2*M_PI*energy) * vol;
}

double Wave(unsigned long T,unsigned long sr,double vol)
{
    /*Rosenburg波を出力する関数*/
    static double energy;
    double tau = 0.75; /*声門解放期における声門拡張期の割合*/
    double tau2= 0.8;  /*一連の運動における声門解放期の割合*/
    energy += ((double)T / (double)sr); 
    energy = (energy - floor(energy));
    if(energy <= tau*tau2){
        return vol*( (3*energy*energy)/(tau*tau) - (2*energy*energy*energy)/(tau*tau*tau)-0.5);
    }else if(energy <tau2){
        return vol*(1 - (energy-tau)/((1-tau)*(1-tau))-0.5);
    }else{
        return vol*(-0.5);
    }
}

double GaussNoise(double vol)
{
    /*擬似的なノイズ出力*/
    return vol* (double)(rand() + rand()) / (double)(RAND_MAX);
}

/*-------------------------*/
/* 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] = 1.0;
}


void IIR_apply(double a[],double b[],double tap[],double output[],double base)
{
    int k;
    for(k=IIR_LENGTH-1;k>0;k--)
    {
        tap[k]= tap[k-1];
        output[k]= output[k-1];
    }
    tap[0]=base;output[0] = 0;
    for(k=0;k<IIR_LENGTH;k++)
    {
        output[0] += (b[k] * tap[k]);
    }
    for(k=1;k<IIR_LENGTH;k++)
    {
        output[0] -= a[k] * output[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];
    double voicev,tapv[FORMANT_LENGTH][IIR_LENGTH]={0},outputv[FORMANT_LENGTH][IIR_LENGTH]={0},basev;
    double voiceb,tapb[FORMANT_LENGTH][IIR_LENGTH]={0},outputb[FORMANT_LENGTH][IIR_LENGTH]={0},baseb;
    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);
                }
                voicev = 0;
                for(j=0;j<3;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,20,av,bv);
                    }else{
                        IIR_resonator(Voice1[h].ffv[j]/44100,20,av,bv);
                    }
                    IIR_apply(av,bv,tapv[j],outputv[j],basev);
                    voicev += outputv[j][0];
                }
                if ((i > Voice1[h].lengthv[0])){
                    wave_output[i] +=  _slide(i,Voice1[h].lengthv[1],0,voicev)/3.0;
                }else{
                    wave_output[i] += voicev /3.0;
                }
            }
            /*子音*/
            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<3;j++){
                    IIR_resonator(Voice1[h].ffb[j]/44100,7,ab,bb);
                    IIR_apply(ab,bb,tapb[j],outputb[j],baseb);
                    voiceb += outputb[j][0];
                }
                wave_output[i] +=voiceb/3.0;
            }
        }
        wave_output+=i;
    }
    printf("実行終了(音声生成)\n");
    return wave_output;
}

/*----------------------*/
/* 母音と子音の列から、 */
/* 発音ブロック列を作る */
/*----------------------*/

VoiceData *formants(unsigned long chunks,VoiceData *voices,unsigned long sr,SpeakData *Speak1){
    unsigned long h,i;
    double ffv[][FORMANT_LENGTH]=
    {
        //a/0
        {800,1300,2500,3500,4500},
        //i/1
        {250,1900,2600,3500,4500},
        //u/2
        {400,1400,2200,3500,4500},
        //e/3
        {450,1800,2400,3500,4500},
        //o/4
        {450,900,2600,3500,4500},
    };
    VoiceData consonant[]=
    {
        ///00
        {{0},0.0,{0},{0,0,500},{0},1,1,0,1},
        //k/01
        {{800,1100,1400,3500,4500},0.8,{200,0,2200},{2200,200,0},{0},1,1,0,1},
        //kh/02
        {{800,3200,3700,3500,4500},0.8,{0,0,2200},{1600,800,0},{0},1,1,0,1},
        //k,/03
        {{400,2300,4600,3500,4500},0.8,{0,0,2400},{1800,600,0},{0},1,1,0,1},
        //s/04
        {{250,400,4500,3500,4500},0.5,{500,0,5500},{4000,2000,0},{0},1,1,0,1},
        //sh/05
        {{500,2100,4300,3500,4500},0.6,{500,0,5500},{4000,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,10,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},
        //y/11
        {{0},0,{0},{0,300,0},{200,2100,3100,3500,4500},0.6,1,3000,1},
        //m/12
        {{0},0,{0},{300,900,300},{200,250,300, 3500, 4500},0.4,1,1500,1},
        //w/13
        {{0},0,{0},{0,1500,1500},{400,800,2200,3500,4500},1.0,1,3000,1},
        //r/14
        {{0},0,{0},{0,2000,0},{300,2600,4900,3500,4500},0.8,1,3000,1},
        //rj/15
        {{0},0,{0},{0,1200,1200},{300,2600,4900,3500,4500},0.8,1,2400,1},
        //n/16
        {{0},0,{0},{500,1000,0},{250,1400,2500,3500,4500},0.3,1,3000,1},
        //b/17
        {{200,250,400,3500,4500},0.3,{500,0,1000},{1000,1000,0},{500,1300,2500,3500,4500},0.8,1,2000,1},
        //z/18
        {{150,250,4900,3500,4500},0.6,{500,0,2000},{1000,1000,0},{500,1300,2500,3500,4500},0.8,1,3000,1},
        //dg/19
        {{150,3600,4500,3500,4500},0.6,{500,0,2000},{1000,1000,0},{400,2000,2800,3500,4500},0.6,1,2500,1},
    };
        
    for(h=0;h<chunks;h++)
    {        
        *voices = consonant[Speak1[h].cnum];
        if(Speak1[h].cnum > 10)
        {
            (*voices).volb *= 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];
        }
        for(i=0;i < FORMANT_LENGTH;i++)
        {
            (*voices).ffv[i] = ffv[Speak1[h].vnum][i];
        }
        (*voices).volb *= Speak1[h].vol;
        (*voices).volv *= Speak1[h].vol;
        (*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.5;
        voices++;
    }
    return voices;
}
/*----------------------*/
/* アルファベット列から */
/* 母音と子音の列を作る */
/*----------------------*/
/*
SpeakData *makespeech(SpeakData *speech,char str1[])
{
    
    
}
*/
/*----------*/
/* 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;
    
    VoiceData voices[10],*ptr;
    SpeakData speech[10]={
        {65*2,18000,8000,0,0,1},
        {75*2,26000,8000,3,0,1},
        {80*2,22000,8000,1,0,1},
        {50*2,20000,8000,2,0,1},
        {65*2,18000,8000,3,0,1},
        {75*2,26000,8000,4,0,1},
        {80*2,22000,8000,0,0,1},
        {50*2,20000,8000,4,0,1},
        };
    
    ptr = formants(8,voices,sr,speech);
    /*サウンド生成*/
    
    points=64000;
    chwaveL = (double *)calloc(points,sizeof(double));
    chL = (short *)calloc(points,sizeof(short));
    printf("実行終了(準備)\n");
    
    Voice_production(8,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(chwaveL);
    free(chL);
    return 0;
}

このコードをコンパイルして実行すると、「あえいうえおあお」としゃべるWAVEファイルが出力される。変数speechを変更することでさまざまな値をしゃべらせることができる。詳しい解説は完成版ができたときにまとめて行う。

2011年10月3日

音声合成

音声合成が結構簡単にできると聞いて、遊んでみることにした。

今回は母音のみ。基本原理はこんな感じ。フォルマント式音声合成と呼ばれる。

  1. 基本周波数を元に原音を作る。歌声でラ(220Hz)の音なら220Hzの周波数で鳴らす。
  2. フォルマント(周波数のピーク)を元に原音を加工する。フォルマントになっている周波数領域を強調し、他を弱める。
加えて次のような処理が強く求められる
  1. 母音の連続をさせるために、フォルマント、基本周波数、音量の変動を行う処理。

以下のソース「あいうえお」としゃべる音声を出力するものである。

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;
}