コーラス・フランジャーの実装例
コーラス・フランジャーは入力信号に音程をずらした音を混ぜて音に厚みを持たせるエフェクターです。
コーラス・フランジャーの違いは明確になく、音程のずれの大きさで呼び方が変わります。
コーラスは音程のずれが少なく入力信号に厚みを持たせる程度のもで、フランジャーは音程のずれが大きく入力信号にジェットサウンドを加えたようなものになります。
パラメーターとして下記がよく利用されます。
パラメーター | 意味 | だいたいの範囲 |
---|---|---|
ミックスレベル | コーラス・フランジャーのかかり具合 | 0~1の間 |
レート | 揺らぎの間隔(ディレイ信号の読み込み位置を揺らす速度) | 0Hz~16Hz程度 |
深さ | 揺らぎの深さ(ディレイ信号の読み込み位置を揺らす幅) | 5.0~200.0サンプル程度 |
フィードバック | フィードバックの効き具合 | 0~1の範囲 |
実装は入力信号に読み込み位置を周期的にずらした(揺らした)ディレイ信号を加えて出力します。
ディレイ信号の読み込み位置を周期的に揺らすことで、音程が数Hz~数十Hz程度変わります。この揺らしたディレイ信号を入力信号に加えることで音に厚みを持たせることができます。
ディレイ信号はリングバッファを使用して保存します。リングバッファは「リングバッファについて」で作成したものを使用しています。
また、読み込み位置を揺らした際、整数値にならない場合があるためサンプルとサンプルの間の位置の値を線形補間などを用いて割り出す必要があります。
線形補間の詳細は省略いたします。
あくまで実装例ですのでいい音質のものがほしい場合は、ご自身で試行錯誤いただくようお願いします。
【実装イメージ】
■線形補間の実装
1 2 3 4 5 6 7 |
// 線形補間関数 // v1とv2を割合tで線形補間する。tは0.0~1.0の範囲とする // tが0.0の時v1の値となり、tが1.0の時v2の値となる float lerp(float v1, float v2, float t) { return (1.0f - t) * v1 + t * v2; } |
■エフェクターの実装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
void delay(float inL[], float inR[], float outL[], float outR[], int wavelength) { // inL[]、inR[]、outL[]、outR[]はそれぞれ入力信号と出力信号のバッファ(左右) // wavelenghtはバッファのサイズ、サンプリング周波数は44100Hzとする // エフェクターのパラメーター float mix = 0.3f; // コーラスのかかり具合。0.0~1.0の間 float rate = 3.0f; // コーラスの揺らぎの間隔。0Hz~16Hz程度 float depth = 10.0f; // コーラスの揺らぎの深さ。5.0~200.0サンプル程度 float feedback = 0.3f; // コーラスのフィードバック量。0.0~1.0の間 // 内部変数 CRingBuffur ringbufL, ringbufR; // リングバッファ(https://www.utsbox.com/?p=1505 より) // ディレイタイムをサンプル数に変換して設定 // depth分だけ読み込むサンプル位置が動くので、動いた際にintervalが0以下にならないようにする // とりあえず1000サンプル程度とする // (intervalはリングバッファ https://www.utsbox.com/?p=1505 参照) int delaysample = 1000; ringbufL.SetInterval(delaysample); ringbufR.SetInterval(delaysample); float theta = 0; // ディレイ読み込み位置を揺らすためのsin関数の角度 θ。初期値は0 float speed = (2.0f * 3.14159265f * rate) / 44100.0f; // 揺らぎのスピード。角速度ωと同じ。 // 入力信号にコーラスかける for (int i = 0; i < wavelength; i++) { // 角度θに角速度を加える theta += speed; // 読み込み位置を揺らす量を計算 // sin()関数の結果にdepthを掛ける float a = sin(theta) * depth; // 読み込み位置を揺らした際の前後の整数値を取得(あとで線形補間するため) int p1 = (int)a; int p2 = a + 1; // 前後の整数値から読み込み位置の値を線形補間で割り出す float lerpL1 = lerp(ringbufL.Read(p1), ringbufL.Read(p2), a - (float)p1); float lerpR1 = lerp(ringbufR.Read(p1), ringbufR.Read(p2), a - (float)p1); // 入力信号にディレイ信号を混ぜる float tmpL = (1.0f - mix)*inL[i] + mix * lerpL1; float tmpR = (1.0f - mix)*inR[i] + mix * lerpR1; // ディレイ信号として入力信号とフィードバック信号をリングバッファに書き込み ringbufL.Write((1.0f - feedback) * inL[i] + feedback * tmpL); ringbufR.Write((1.0f - feedback) * inR[i] + feedback * tmpR); // リングバッファの状態を更新する ringbufL.Update(); ringbufR.Update(); // 出力信号に書き込む outL[i] = tmpL; outR[i] = tmpR; } } |
上記はあくまで実装例です。
読み込み位置を揺らす関数をSin関数以外にしたり、リングバッファを複数用意することで音の揺れ方や厚みが変わります。
リングバッファに保存する信号やフィードバック信号を左右逆にしたり、フィルタをディレイ信号にかけてみるのも面白いかもしれません。
質問はコメント欄や掲示板、Twitterでいただけばとおもいます。
他のエフェクター実装例はこちらにもあります。 → エフェクターの簡単な実装例
■掲示板
■Twitterアカウント:@vstcpp URL:https://twitter.com/vstcpp