/* HANAC200X オーディオ編集TIPS 第十七回 響け大車輪! サンプルソース (2006/1/25 作成) */ #include #include #define GN_INPUT_FILENAME "DAISHARIN_IN.pcm" //入力ファイル名 #define GN_OUTPUT_FILENAME "DAISHARIN_OUT.pcm" //出力ファイル名 #define SAMPLE_RATE 44100 //サンプリング周波数 #define REV_TIME_MS 6000 //リバーブタイム(ミリ秒) #define DELAY_BUF_MS 600 //ディレイバッファ長(ミリ秒) #define TREMOLO_SPEED 6 //トレモロ速度(Hz) #define DELAY_POINTS 25 //ディレイポイント数 #define HI_DUMP 0.5 //0.0 .. フィルタなし 1.0 ..フィルタ最高(直流のみ) #define IS_STEREO true // true..ステレオ出力 false..モノラル出力 #define GN_PI2 (3.1415926535898 * 2) // πの2倍 #define DELAY_BUF_SMPL (DELAY_BUF_MS * SAMPLE_RATE / 1000) //ディレイバッファ長(サンプル数) #define SAFE_DELETE(p) { if(p) { delete [] (p); (p)=NULL; } } //ノイズ生成関数(0.0〜1.0) double Noise(); //////////////////////////////////////////// //大車輪リバーブクラス //////////////////////////////////////////// class Daisharin { struct DelayPoint { int offset; //時間オフセット double pan; //パンニング // 0..Left 0.5..Center 1..Right double feedback; //フィードバックレベル double tremolo_phase;//トレモロ周期の位相 double lpf_lv; //LPF用の値 }; double * pDelayBuf; //ディレイバッファ DelayPoint * pDelayPoints; //ディレイポイント bool feed_phase; //フィードバックの位相切り替え用 int cnt_points; int buf_size; int buf_index; double reverb_time; //残響時間(ミリ秒) double tremolo_speed; //トレモロ速度(Hz) bool is_stereo; public: Daisharin() { pDelayBuf = NULL; pDelayPoints = NULL; cnt_points = 0; feed_phase = false; buf_index = buf_size = 0; is_stereo = IS_STEREO; } ~Daisharin() { SAFE_DELETE(pDelayBuf); SAFE_DELETE(pDelayPoints); } //ディレイポイントの取得 int GetPoint(int& offset, double& pan) { double noise; noise = Noise(); //0.0〜1.0 //0.1〜1.0へ移動 noise = 0.1 + noise * 0.9; //バッファサイズへスケーリング offset = buf_size * noise ; if (offset >= buf_size) offset = buf_size -1 ; //パンの設定 pan = Noise(); //0〜1 return offset; } //トレモロ波形の生成 double GetTremolo(double phase) { double trem; /* trem = (0.5 + cos(phase)) * 0.5); trem *= trem; trem *= trem; trem = 1.0 - trem ; */ //0〜1へ変更 phase /= GN_PI2; //0.0〜0.1、0.9〜1.0だけをcosでフェードアウトさせる if (phase < 0.1) { trem = (0.5 - cos(phase * 5 * GN_PI2) * 0.5); } else if (phase > 0.9) { phase = 1.0 - phase; trem = (0.5 - cos(phase * 5 * GN_PI2) * 0.5); } else { trem = 1.0; //最大振幅を維持 } return trem; } //データの確保 void Alloc(int bufsize, int points, double time, double speed) { int i; SAFE_DELETE(pDelayBuf); SAFE_DELETE(pDelayPoints); feed_phase = false; buf_index = buf_size = 0; //ディレイバッファの初期化 buf_size = bufsize; if (is_stereo) { pDelayBuf = new double[buf_size*2]; for (i = 0 ; i < buf_size * 2; i++) pDelayBuf[i] = 0.0; } else { pDelayBuf = new double[buf_size]; for (i = 0 ; i < buf_size; i++) pDelayBuf[i] = 0.0; } //リバーブタイム reverb_time = time; //ディレイポイントの初期化 cnt_points = points; pDelayPoints = new DelayPoint[cnt_points]; for (i = 0; i < cnt_points; i++) { GetPoint(pDelayPoints[i].offset, pDelayPoints[i].pan); pDelayPoints[i].feedback = calc_feedback(pDelayPoints[i].offset); pDelayPoints[i].tremolo_phase = i * GN_PI2 / cnt_points ; pDelayPoints[i].lpf_lv = 0; } tremolo_speed = speed / SAMPLE_RATE * GN_PI2; } //実際にリバーブを処理する double Proccess(double input, double& output_l, double& output_r) { int i; double output; double trem; double echo_src; int ind_Buf; output_l = output_r = input; for (i = 0; i < cnt_points; i++) { //////////////////////////////////////// //リバーブレーション //////////////////////////////////////// trem = GetTremolo(pDelayPoints[i].tremolo_phase); //バッファのインデックス算出 ind_Buf = (buf_size + buf_index - pDelayPoints[i].offset) % buf_size; //エコーソースの算出 if (is_stereo) { //ディレイポイントのパンとは逆にする echo_src = pDelayBuf[ind_Buf] * (1-pDelayPoints[i].pan) + pDelayBuf[ind_Buf + buf_size] * pDelayPoints[i].pan; } else { echo_src = pDelayBuf[ind_Buf] ; } //ローパスフィルタ pDelayPoints[i].lpf_lv = echo_src * (1.0 - HI_DUMP) + pDelayPoints[i].lpf_lv * HI_DUMP; //フィードバック値の算出 output = pDelayPoints[i].lpf_lv * pDelayPoints[i].feedback * trem ; //フィードバック if (is_stereo) { output_l += output * pDelayPoints[i].pan; output_r += output * (1.0 - pDelayPoints[i].pan); } else { output_l += output; } //回転 pDelayPoints[i].tremolo_phase += tremolo_speed; //付け替え if (pDelayPoints[i].tremolo_phase >= GN_PI2) { pDelayPoints[i].tremolo_phase -= GN_PI2; GetPoint(pDelayPoints[i].offset, pDelayPoints[i].pan); pDelayPoints[i].feedback = calc_feedback(pDelayPoints[i].offset); } } //インデックス進める buf_index++; if (buf_index == buf_size) buf_index = 0; //バッファに値を設定 pDelayBuf[buf_index] = output_l; if (is_stereo) { pDelayBuf[buf_index + buf_size] = output_r; } output_l -= input; if (is_stereo) output_r -= input; return output_l; } private: //残響時間と時間オフセットから、フィードバック量を算出 double calc_feedback(int offset) { double feedback = pow( 0.001, (offset / (double)SAMPLE_RATE) / (reverb_time * 0.001) ) / sqrt(DELAY_POINTS); //フィードバックの位相をプラスとマイナスで交互に切り替え feed_phase = !feed_phase; if (feed_phase) feedback = - feedback; return feedback; } }; int main(int argc, char* argv[]) { FILE *fp_in, *fp_out; float in = 0 ,f_out; double out_l = 0, out_r= 0; //実際にファイル入出力される値 int i; Daisharin daisyarin; daisyarin.Alloc(DELAY_BUF_SMPL,DELAY_POINTS,REV_TIME_MS,TREMOLO_SPEED); //入力ファイルのオープン if (argc > 1) { //コマンドライン引数がある場合は第1引数を入力ファイル名とみなす fp_in = fopen(argv[1],"rb"); } else { //デフォルトの入力ファイル名で開く fp_in = fopen(GN_INPUT_FILENAME,"rb"); } if (fp_in == NULL) return 0; //出力ファイルのオープン fp_out = fopen(GN_OUTPUT_FILENAME,"wb"); if (fp_out == NULL) {fclose(fp_in); return 0;} // 入力ファイルの終端に到達するまでループ while( !feof( fp_in ) ) { //float値を1サンプル読み込み fread( &in , sizeof(float), 1, fp_in); if( ferror( fp_in ) ) { perror( "読み込みエラー" ); break; } daisyarin.Proccess(in, out_l,out_r); //float値を1サンプル書き出し if (IS_STEREO) { f_out = out_l; fwrite(&f_out, sizeof(float), 1, fp_out); f_out = out_r; fwrite(&f_out, sizeof(float), 1, fp_out); } else { f_out = out_l; fwrite(&f_out, sizeof(float), 1, fp_out); } if( ferror( fp_out ) ) { perror( "書き出しエラー" ); break; } } fclose(fp_in); fclose(fp_out); return 0; } unsigned int seed = 928529388; double Noise() { #define DIV_D_32 (256.0 * 256.0 * 256.0 * 256.0) int i,lc; //変なループにしてみる lc = seed & 3; for (i=0;i <= lc ; i++) { //普通の乱数生成方法 seed = seed * 65529 - 116813153; } return (seed / DIV_D_32); }