4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TD-PSOLAの実装

Posted at

「TD-PSOLA」とは時間領域でピッチの調節を行う方法です。
実装に関しての詳しい内容はソースコードのコメントに書いています。

# include <vector>

std::vector<float> PSOLA(const std::vector<float>& data, const float& rate)
{
    //相関関数範囲
    const unsigned int size = (info.sample * 0.01) * info.channel;
    //ずれ最小値
    const unsigned int min  = size / 2;
    //ずれ最大値
    const unsigned int max  = size * 2;

    //変換データ格納用
    std::vector<float>convert(data.size() * 2);

    //元データの配列インデックス
    unsigned int offset0 = 0;
    //変換データの配列インデックス
    unsigned int offset1 = 0;

    while (offset0 + max < data.size())
    {
        //自己相関関数
        float peak = 0.0;
        int index = min;
        for (unsigned int i = min; i <= max; ++i)
        {
            float tmp = 0.0f;
            for (unsigned int n = 0; n < size; ++n)
            {
                tmp += data[n] * data[i + n];
            }

            //ピーク値更新
            if (peak < tmp)
            {
                peak  = tmp;
                index = i;
            }
        }

        //伸縮率を適応したブロックサイズ
        int tmp = index * rate;
        //ピーク位置を中心にしたブロックサイズ分の配列を作成
        std::vector<float>cut(&data[std::fmax(int(offset0 + index - tmp), 0)], &data[offset0 + index + tmp]);

        //クロスフェード処理
        for (size_t i = 0; i < cut.size(); ++i)
        {
            //ハニング窓関数はwikiに載っている公式をそのまま使用
            cut[i] *= okmonn::Hanning<float>(i, cut.size());
        }
        for (size_t i = 0; i < cut.size(); ++i)
        {
            convert[offset1 + i] += cut[i];
        }

        //元データの配列インデックス更新
        offset0 += index;
        //変換データの配列インデックス更新
        //ピークから次のピークまでの区間をクロスフェードするので更新は半分だけで大丈夫
        offset1 += cut.size() / 2;
    }

    return convert;
}

//エントリーポイント
int main(void)
{
    //テスト用の波形「ラ」の音
    std::vector<float>data(44100 * 2);
    for(size_t i = 0; i < data.size(); i += 2)
    {
        data[i] = std::sin(2.0f * std::acos(-1.0f) * 440.0f * i / 44100.0f);
        data[i + 1] = data[i];
    }
    
    //ピッチを2倍に設定
    auto psola = PSOLA(data, 2.0f);
    /*
    波形のサンプリング周波数を2倍に設定する
    前回の記事に書いたリサンプリングの処理を行えばOK
    */
    return 0;
}

ピッチだけをn倍にするには、再生時間をn倍にして再生速度を1/n倍にすれば良いみたいです。
自己相関を行う部分で結構な処理時間を要してしまうので注意してください。

参考サイト,本
タイムストレッチ、ピッチシフトのアルゴリズム
恋声の技術に迫る
C言語ではじめる音のプログラミング―サウンドエフェクトの信号処理

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?