タイムストレッチは音程を保ったまま再生速度を変える処理、 ピッチシフトは再生速度を保ったまま音程を変える処理です。 下図のように単純に波形を拡大、縮小すると再生速度と音程が 両方変わってしまいます。再生速度と音程を別々に変更するには 特殊な処理をする必要があります。
タイムストレッチ、ピッチシフトのアルゴリズムには、 FFTを用いる手法やクロスフェードを利用する手法などがあります。 以下ではWaveToneで採用しているクロスフェードによるタイムストレッチ、 ピッチシフトのアルゴリズムについて解説します。
再生速度を2倍にするには、下図のように波形を小さなブロックに分割し、 ブロックを1つおきに配置します。
再生速度を0.5倍にするには、コピー元の位置を0.5ブロックずつずらしながら ブロックを配置します。
ブロックのサイズをある程度大きくしないと、低周波が再現できません。 しかし、大きくしすぎるとスムーズに聞こえません。50msec程度がよいと思います。 コピー元の位置をずらす量を0.9倍、1.1倍などにすれば、 再生速度を0.9倍、1.1倍にすることもできます。
上のように波形を切り貼りする方法では、ブロックの境界で 波形が不連続であるため、再生するとプツプツという雑音が聞こえます。 この雑音を抑えるため、各ブロックにフェードイン、フェードアウトをかけ、 前のブロックのフェードアウトと、次のブロックのフェードインを重ねます。
クロスフェードをかけることで、プツプツ音を抑えることはできますが、 波形が重なる部分で位相差があると、打ち消しあって音量が小さくなって しまいます。 そこで、ブロックの区切り位置をゼロクロス点にすることで位相をそろえたり、 打ち消されることを見越してフェードイン、フェードアウトのカーブを Sin波にするなどして音量変化を抑制します。 また、多少CPU負荷がかかりますが、クロスフェード部分の2乗誤差や自己相関を求め、 波形が似通っている箇所を検索して位相をそろえる方法もあります。
(↑実際にはクロスフェードと併用します。)
上図の例では、ゼロクロス点が1周期内に1箇所しかありませんが、 倍音や和音を含んでいると1周期内にゼロクロス点が複数あることがあります。 このような場合は、「前回のゼロクロス点からの間隔が極大となる ゼロクロス点で区切る」などの方法で位相を揃えることができます。
WaveToneやVocalShifterでは、2乗誤差で区切り位置を探索しています。
タイムストレッチと単純な波形の拡大、縮小を組み合わせることで、 音程を変更することができます。再生速度を保ったまま、ピッチを2倍にする (音程を1オクターブ上げる)には、再生速度を0.5倍にしてから、 単純に0.5倍に縮小します。
一般的に、再生速度をv倍、ピッチをp倍したいときは、 タイムストレッチのアルゴリズムで再生速度をv/p倍してから、 波形を単純に1/p倍に拡大、縮小します。