Audio DSP Plug-in 実装例サンプル ~オーディオ コンプレッサー DSP で音に迫力を付けてみよう!~
こんにちははらだんです。今回は以前紹介した Windows Media Player (WMP) の DSP プラグイン (DMO) の続きで、ウィザードで作成した Audio DSP Plug-in の雛形にプログラムを追加実装して”オーディオ コンプレッサー DSP ”を作成してみます。最後に DSP プラグインの処理の効果を 波形を見ながら比較してみたいと思います。今回の内容もVisual Studio と Windows SDK でどなたでも検証可能ですのでぜひ試してみてください。
目次
1. コンプレッサーとは
2. 3 分クッキング的実装
3. 出力テスト
1. コンプレッサーとは
----------
振幅(音量)を圧縮する処理です。ある閾値を超えた振幅について、一定の緩やかな比率で縮小させたあと、最大値が分解能の最大となるようノーマライズを行う演算です。この処理を行うと音に迫力が出るためよくこの処理が使われます。ただし振幅を歪める処理であることに変わりはないので、やりすぎると音色が変わってしまいます。
この図は点線が DSP なしの場合の入出力、実線が DSP ありの入出力です。一番小さい音と一番大きい音とその付近はコンプレッサーではほぼ変化がないのがわかると思います。また、任意の2つの入力について、入力時の振幅の大小関係と出力時の大小関係は変わらないこともわかると思います。その上で全体的に音が大きくなる方向に増幅されているという面白い増幅アルゴリズムです。
さらに詳しくは前回も紹介した書籍ですが、こちらが参考になります。
・C言語ではじめる音のプログラミング サウンドエフェクトの信号処理 青木直史
ISBN 978-4-274-20650-4
2. 3 分クッキング的実装
----------
1. の図の通りに実装します。今回は 16bit の分解能の部分のみ実装します。それ以外の 8bit / 24bit / 32bit については元のままですので、ウィザードの作成する減衰フィルタとして動きます。
以下は CWmpplugin1::DoProcessOutput() の実装の一部、16bit の部分です。クラス名は作成した DSP Plug-in のソリューション名により変わりますが、関数は一緒です。下記の 3 つの変数は関数の最初で宣言しています。fCompressData は計算途中のデータを保持しておく変数、fThreshold は 1. で説明した閾値です。fZoom は最後に出力データの最大値が 32767 (負の方は -32768)となるようにノーマライズ処理を行うための増幅率です。
-
double fCompressData;
double fThreshold = 0.2; //閾値は後でプロパティとして設定できるように
double fZoom;
-
case 16:
{
// return no. bytes actually copied to output buffer
*cbBytesProcessed = dwSamplesToProcess * sizeof(short);
//
fZoom = 1.0 / ( (m_fScaleFactor * (1.0 - fThreshold)) + fThreshold );
// 16-bit sound is -32768..32767 with 0 == silence
short *pwInputData = (short *) pbInputData;
short *pwOutputData = (short *) pbOutputData;
while (dwSamplesToProcess--)
{
// Get the input sample
int i = *pwInputData++;
if (i >0)
{
fCompressData = ((double) i) / 32767.0;
if ( fCompressData > fThreshold)
{
fCompressData = ( fCompressData - fThreshold) * m_fScaleFactor + fThreshold; // Do Compress
}
i = int( fZoom * fCompressData * 32767.0);
}else{
fCompressData = ( -(double) i) / 32768.0;
if ( fCompressData > fThreshold )
{
fCompressData = ( fCompressData - fThreshold ) * m_fScaleFactor + fThreshold;// Do Compress
}
i = int( -fZoom * fCompressData * 32768.0);
}
// Truncate if exceeded full scale.
if (i > 32767)
i = 32767;
if (i < -32768)
i = -32768;
// Write to output buffer.
*pwOutputData++ = (short)i;
}
}
break;
コードの変更は以上です。実行中、もともとあるプロパティで閾値を超えたデータの増幅率を設定します。小さいほど圧縮されます。かなり簡単に済ませていますが、ゼロ除算についてとプロパティ設定の実装がないこと、各種分解能に対応していないことがありますので、完成にはまだ道のりがあります。
3. 出力テスト
----------
今回はわびーに頼んでオリジナルのデータを作成してもらい、コンプレッサーをかけてみました。出力をファイルに書き出し、WAVE 波形エディタで表示た結果が以下になります。
この図が入力の波形です。
コンプレッサーの出力はこのようになります。青い部分の面積が増えていることがわかります。その分人間の耳には音が大きく聞こえます。そして入力波形の時によく見えていたひげのように細くて振幅の大きな波形について、目立たなくなっているのがわかります。
3 つ目の図は入力全体を 4 倍増幅したものです。コンプレッサーの時とは異なり、ひげのように細い元々振幅の大きな波形はさらに大きな波形になり 1 つは最大値に達しています。これ以上音を大きくすることができないのに、コンプレッサーの出力ほど青い部分の割合が少ないのがわかります。音を大きくしても迫力が増さない音となります。
この図は左が入力、右がコンプレッサーの出力です。振幅のピークの大小関係は保たれていることがわかると思います。
というわけでウィザードを使うと少ない実装で DSP が作れてしまうのがわかりました。周波数フィルタやエコーのような過去のデータをある程度持ち続ける必要がある DSP 処理を開発する場合にはクラス内にバッファが必要となりますのでもう少しコードが増えますが、いずれにしても意外と簡単だと感じたのではないでしょうか。
作成したプログラムはこちら。(後ほどアップロードいたします)
閑話休題:
会社のマラソン部にいますが、4 月末に多摩川で開催されるチャリティマラソンにボランティアスタッフで参加することになりました。走ることはあってもスタッフをするのは初めてです。ちなみにこの前の東京マラソンは残念ながら抽選漏れでした。残念:)