今考えているウェーブレットによるチャンネル間相関除去って既知の技術だと思ったんだがなあ。資料がないように見える。

音声のLRチャンネルの相互相関を観察してみよう。そして、LRをレイジーウェーブレット(スプリット)の結果と考えてリフティングしてみる。 作って動かしてみてるけど、

  • 無音部(とリリース)でしか相関が高くならない
    • 残響成分はなんやかんやで位相が揃っているのかもしれない。
  • MS処理してみると全体的に相関が小さくなる。(1次以上のラグ相関も消えている感じ)
  • CDF22をやっつけで適用してみたけどMS処理とほぼ変わらず。まじで?実装ミスを疑っている。良くなる予想なんだが。

CDF22をNARUに突っ込んでみた(MSで使ってみた)が、案の定というか性能悪化の傾向。 LRは周波数特性が違うから、スプリット(レイジーウェーブレット)した状態とは違う状態になっているかかも。スプリットしたら正確にサンプリングの半周期分遅れた自分に対して処理できるけど、LR信号はスピーカー配置とかで変わってくる。 そしてもっと重大なことに可逆じゃない。端点処理が怪しい。デバッグしてたらくたばってしまい夕方…。

void CDF22Forward(int32_t **data, uint32_t num_samples)
{
    uint32_t smpl;

    for (smpl = 1; smpl < num_samples - 1; smpl++) {
        data[1][smpl] -= (data[0][smpl] + data[0][smpl + 1]) >> 1;
        data[0][smpl] += (data[1][smpl - 1] + data[1][smpl]) >> 2;
    }
}

void CDF22Inverse(int32_t **data, uint32_t num_samples)
{
    uint32_t smpl;

    for (smpl = 1; smpl < num_samples - 1; smpl++) {
        data[0][smpl] -= (data[1][smpl - 1] + data[1][smpl]) >> 2;
        data[1][smpl] += (data[0][smpl] + data[0][smpl + 1]) >> 1;
    }
}

どう見ても順変換で data[0][smpl + 1] が書き換え前に参照されてて、逆変換で戻す前の値を参照していてうまくいくはずがない。 悶絶してたら (9) ウェーブレット変換 -2- に実装があって、以下のように書き換えたら可逆になった。性能はHaarより少し悪い。

void CDF22Forward(int32_t **data, uint32_t num_samples)
{
    uint32_t smpl;

    for (smpl = 1; smpl < num_samples - 1; smpl++) {
        data[1][smpl] -= (data[0][smpl] + data[0][smpl + 1]) >> 1;
    }
    for (smpl = 1; smpl < num_samples - 1; smpl++) {
        data[0][smpl] += (data[1][smpl - 1] + data[1][smpl]) >> 2;
    }
}

void CDF22Inverse(int32_t **data, uint32_t num_samples)
{
    uint32_t smpl;

    for (smpl = 1; smpl < num_samples - 1; smpl++) {
        data[0][smpl] -= (data[1][smpl - 1] + data[1][smpl]) >> 2;
    }
    for (smpl = 1; smpl < num_samples - 1; smpl++) {
        data[1][smpl] += (data[0][smpl] + data[0][smpl + 1]) >> 1;
    }
}

確かに可逆になるんだけどループ2回回すのがかっこ悪い。1回でやり切れるはずなんだな。しかしこれ以上は追わない。MS変換を置き換えるには至らない感じか。でも、CDF(2,2)は低域と高域で2次までのモーメントを消せるのが強い。チャンネル毎の処理としては使ってみたい。

他に少し収穫。MS処理をHaarリフティングと考えると以下のように簡潔かつ高速になる。 (ただし現行のMS処理と圧縮結果が微妙に変わることに注意。)

/* LR -> MS(int32_t) */
void LRtoMSInt32(int32_t **data, uint32_t num_samples)
{
    uint32_t smpl;

    for (smpl = 0; smpl < num_samples; smpl++) {
        data[1][smpl] -= data[0][smpl];
        data[0][smpl] += (data[1][smpl] >> 1);
    }
}

/* MS -> LR(int32_t) */
void MStoLRInt32(int32_t **data, uint32_t num_samples)
{
    uint32_t smpl;

    for (smpl = 0; smpl < num_samples; smpl++) {
        data[0][smpl] -= (data[1][smpl] >> 1);
        data[1][smpl] += data[0][smpl];
    }
}