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