前回、Wave(PCM)音源の録音機能を使って音声の波形を取り込んでみたので、今回からはいよいよその波形に対する信号処理を行っていくことにしましょう。今回は、信号処理プログラムの骨格を作る意味で単純な取り込んだ音声データにエコーをかけてその波形の一部を表示してみることにします。
エコー処理
エコーの原理は簡単で「少し前の音を小さくして重ねる」だけです。今回は、前回同様8KHz/16Bitの波形データを使用し、その各サンプリングデータに400サンプル(0.05秒)前の値を0.6倍して加算してみることにしました。プログラムでは、波形データ用バッファlpWaveと処理用バッファlpWorkを用意し、以下のような処理を行います。
/* 先頭から400サンプルをワークにコピー */ memcpy(lpWork,lpWave,800); /* 400サンプル前の値を加えエコーの波形を生成 */ for (i=400;i<24000;i++) { nValue=(int)(lpWave[i-400]*0.6)+lpWave[i]; if (nValue>32767) nValue=32767; if (nValue<-32768) nValue=-32768; lpWork[i]=(short int)nValue; } // 処理結果を波形バッファにコピー memcpy(lpWave,lpWork,48000);
これで、lpWaveの波形にエコーがかかります。今回は16ビットなので、波形データのバイト単位のコピーなどを行うときは「サンプル数×2バイト」になる点に注意が必要です。結構この辺りは忘れがちなんで(^^;。
今回は、描画の都合もあってエコーの遅延を400サンプル(0.05秒)と短くしてありますが、実際にはもう少し遅らせた方が効果的でしょう。
波形データの描画
次に、波形データlpWaveをグラフに描画してみます。波形データは、符号付16ビットの数値が並んでいるだけなので、単純にその数値を折れ線グラフにしていけば良いでしょう。グラフのサイズは600*256ピクセルとし、このグラフをBITMAPのデバイスコンテキストhdcBMPに描いてみます。
void drawWave() { int y; /* ビットマップをクリア */ FillRect(hdcBMP,&recGraph,GetStockObject(GRAY_BRUSH)); SelectObject(hdcBMP,GetStockObject(BLACK_PEN)); /* ビットマップ中央にy=0の直線描画 */ MoveToEx(hdcBMP,0,128,NULL); LineTo(hdcBMP,600,128); SelectObject(hdcBMP,hpenLine); MoveToEx(hdcBMP,0,128,NULL); /* 各サンプリングデータを線でつないで描画 */ for (int i=0;i<600;i++) { y=(int)(lpWave[i]/256)+128; LineTo(hdcBMP,i,y); } }
データは符号付16ビット(-32768〜32767)なので、それを256段階(-128-127)に対応させています。
今回は波形データlpWaveの最初の600サンプルを描画しましたが、全体の傾向を見るためにはスクロールバーなどでスクロールできるようにすると良いですね。ただ、波形データは膨大なサンプル数になる(8KHzでも1秒間に8000)ので、見やすい形にするのは大変かもしれませんが....。
プログラム
実行したら、ライン入力などから適当な音声を「録音」してみてください。録音した波形が表示されるので、「再生」してみましょう。続いて、「エコー」をかけて波形を確認しながら再生してみてください。波形グラフには最初の600サンプルが表示されるため、エコーがかかるのは表示領域の後方200サンプルです。