今回は母音のみ。基本原理はこんな感じ。フォルマント式音声合成と呼ばれる。
- 基本周波数を元に原音を作る。歌声でラ(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;
}
0 件のコメント:
コメントを投稿