ピッチシフターの実装例
ピッチシフターは入力信号に音程を高くしたり、低くしたりするエフェクターです。
パラメーターとして下記がよく利用されます。
パラメーター | 意味 | だいたいの範囲 |
---|---|---|
ミックスレベル | ピッチシフターのかかり具合 | 0~1の間 |
ピッチ | どれだけ音を高く(低く)するか | -12~12半音(-1~1オクターブ)程度 |
実装は読み込み速度を速めたディレイ信号を入力信号に加えて出力します。
ディレイ信号の読み込み速度を速めることで音程が変わります。
ディレイ信号はリングバッファを使用して保存します。リングバッファは「リングバッファについて」で作成したものを使用しています。
また、読み込み速度を変更した際、整数値にならない場合があるためサンプルとサンプルの間の位置の値を線形補間などを用いて割り出す必要があります。
線形補間の詳細は省略いたします。
あくまで実装例ですのでいい音質のものがほしい場合は、ご自身で試行錯誤いただくようお願いします。
【実装イメージ】
■線形補間の実装
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 |
void pitchshifter(float inL[], float inR[], float outL[], float outR[], int wavelength) { // エフェクターのパラメーター static float mix = 1.0f; // ピッチシフターの効き具合。0.0~1.0の間 static float pitch = 12.0f; // どれだけ音を高く(低く)するか。-12~12程度 // 内部変数 static CRingBuffur ringbufL, ringbufR; // リングバッファ(https://www.utsbox.com/?p=1505 より) // リングバッファのinteravalを設定する // とりあえず1000サンプル程度とする // (intervalはリングバッファ(https://www.utsbox.com/?p=1505 より) int delaysample = 1000; ringbufL.SetInterval(delaysample); ringbufR.SetInterval(delaysample); // 読み込み速度を速めた後の読み込み位置 static float pos = 0.0f; // ピッチから読み込み速度を計算 // -12半音(1オクターブ下)で-0.5、12半音(1オクターブ上)で1となるようにする static float speed = pow(2.0f, pitch / 12.0f) - 1.0f ; // 入力信号をピッチシフトする for (int i = 0; i < wavelength; i++) { // 読み込み位置を移動した際の前後の整数値を取得(あとで線形補間するため) int p1 = (int)pos; int p2 = pos + 1; // 前後の整数値から読み込み位置の値を線形補間で割り出す // 線形補間で割り出した結果はピッチシフト後の信号となる float lerpL1 = lerp(ringbufL.Read(p1), ringbufL.Read(p2), pos - (float)p1); float lerpR1 = lerp(ringbufR.Read(p1), ringbufR.Read(p2), pos - (float)p1); // 読み込み位置を更新する // posが大きく(小さく)なりすぎないように定期的に 0 に戻す // 大きくなった時にリングバッファのintervalを超えてはいけないので、 // とりあえずdelaysample* 0.9とする。 pos += speed; if (abs(pos) >= (float)delaysample * 0.9f) { pos = 0.0f; } // ディレイ信号として入力信号をリングバッファに書き込み ringbufL.Write(inL[i]); ringbufR.Write(inR[i]); // リングバッファの状態を更新する ringbufL.Update(); ringbufR.Update(); // 入力信号にピッチシフト信号を混ぜて出力する outL[i] = (1.0f - mix)*inL[i] + mix * lerpL1; outR[i] = (1.0f - mix)*inR[i] + mix * lerpR1; } |
上記はあくまで実装例です。
読み込み位置(上記の変数pos)が0になる時(42行目)に音声信号が不連続となりノイズが入ります。
クロスフェード等を利用して音声信号を滑らかにつなぐことでノイズは改善することができます。
質問はコメント欄や掲示板、Twitterでいただけばとおもいます。
他のエフェクター実装例はこちらにもあります。 → エフェクターの簡単な実装例
■掲示板
■Twitterアカウント:@vstcpp URL:https://twitter.com/vstcpp