NNGSAとのRMS比較をやる。NNGSAが低いのなら後段でSAかませたい。 残差の振幅分布を見ると、やっぱNGSAの方が良い。0付近へよくデータが集まる。 RMSEもNGSAのほうが良かった。。。
うーん、変だ。確かにSLAのときも全く同様の現象を見たんだけど。 実験ではNGSAとても悪かったはずなんだがなあ。。。
グダグダしてのんびりとしている。根本的な改善策がないか、過去のブログを見直してみる。
気になったのはTTAのエンコード部分。再帰的ライスを使っているのは知っていたが、さらに、指数移動平均によって平均を更新していることが分かった。
// aiki: 1つの残差出力
__forceinline void tta_encoder_put_value(TTA_adapt *rice, TTAint32 value) {
TTAuint32 k, unary, outval;
// aiki: 符号付き整数を符号なし整数に変換(非負数は奇, 負数は偶)
outval = ENC(value);
// encode Rice unsigned
k = rice->k0;
// aiki: 指数移動平均式で平均を更新: sum0 <- (15/16) * sum0 + outval
rice->sum0 += outval - (rice->sum0 >> 4);
// aiki: shift_16[x] は 2^(4 + x) = 16 * 2^(x) を計算するテーブル.
if (rice->k0 > 0 && rice->sum0 < shift_16[rice->k0])
// aiki: k0 > 0 && sum0 < 16 * 2^(k0) ならば k0 が大きいので減らす
rice->k0--;
else if (rice->sum0 > shift_16[rice->k0 + 1])
// aiki: sum0 > 32 * 2^(k0) ならば k0 が小さいので増やす
rice->k0++;
if (outval >= bit_shift[k]) {
// aiki: 出力値が 2^(k0) 以上ならば
// aiki: 出力値を引く: outval -= 2^(k0)
outval -= bit_shift[k];
k = rice->k1;
// aiki: 指数移動平均式で平均を更新: sum1 <- (15/16) * sum1 + outval
rice->sum1 += outval - (rice->sum1 >> 4);
if (rice->k1 > 0 && rice->sum1 < shift_16[rice->k1])
// aiki: k1 > 0 && sum1 < 16 * 2^(k1) ならば k1 が大きいので減らす
rice->k1--;
else if (rice->sum1 > shift_16[rice->k1 + 1])
// aiki: sum0 > 32 * 2^(k1) ならば k1 が小さいので増やす
rice->k1++;
// aiki: unaryの計算
unary = 1 + (outval >> k);
} else { // aiki: elseをブロックで囲った
// aiki: 出力値が 2^(k0) より小さければ、unaryは0
unary = 0;
}
// aiki: static変数の補足
// aiki: enc_fifo_bcount: static変数。キャッシュ内のビットカウント。
// aiki: enc_fifo_bcache: static変数。キャッシュ内のビット。
// put unary
// aiki: unaryの連続した1を出力する
do {
// aiki: キャッシュ内に1バイト以上データがあるなら1バイト未満になるまで出力
while (enc_fifo_bcount >= 8) {
write_byte(enc_fifo_bcache);
enc_fifo_bcache >>= 8;
enc_fifo_bcount -= 8;
}
if (unary > 23) {
// aiki: bit_mask[x]は下位x[bit]に1が立ったマスク
// aiki: unaryが24以上ならば, 23bit分をマスクで一気に書き出す
// aiki: 24が特別になっているのは, これに7bit以上のデータがあるとオーバーランするから
enc_fifo_bcache |= bit_mask[23] << enc_fifo_bcount;
enc_fifo_bcount += 23;
unary -= 23;
} else {
// aiki: マスクを使って一気に書き出す
enc_fifo_bcache |= bit_mask[unary] << enc_fifo_bcount;
enc_fifo_bcount += unary + 1;
unary = 0;
}
} while (unary);
// put binary
// aiki: キャッシュ内に1バイト以上データがあるなら1バイト未満になるまで出力
while (enc_fifo_bcount >= 8) {
write_byte(enc_fifo_bcache);
enc_fifo_bcache >>= 8;
enc_fifo_bcount -= 8;
}
if (k) {
// aiki: Rice符号の剰余部出力: k(k0 or k1)が1以上であれば、outvalの下位k[bit]を出力
enc_fifo_bcache |= (outval & bit_mask[k]) << enc_fifo_bcount;
enc_fifo_bcount += k;
}
} // tta_encoder_put_value
// aiki: 1つの残差を復号
__forceinline TTAint32 tta_decoder_get_value(TTA_adapt *rice) {
TTAuint32 k, level, tmp;
TTAint32 value = 0;
// decode Rice unsigned
// aiki: unary部を復号し連続した1の数をvalueに
// aiki: バイト単位の読み出し
if (!(dec_fifo_bcache ^ bit_mask[dec_fifo_bcount])) {
value += dec_fifo_bcount;
dec_fifo_bcache = read_byte();
dec_fifo_bcount = 8;
while (dec_fifo_bcache == 0xff) {
value += 8;
dec_fifo_bcache = read_byte();
}
}
// aiki: 残ったビット分のカウント
while (dec_fifo_bcache & 1) {
value++;
dec_fifo_bcache >>= 1;
dec_fifo_bcount--;
}
dec_fifo_bcache >>= 1;
dec_fifo_bcount--;
// aiki: valueが0ならlevelは0でk0, それ以上ならlevelは1でk1をつかう
if (value) {
level = 1;
k = rice->k1;
value--;
} else {
level = 0;
k = rice->k0;
}
// aiki: Rice符号の剰余部取得: k(k0 or k1)が1以上であれば、outvalの下位k[bit]を出力
if (k) {
// aiki: キャッシュがk[bit]以上になるまでバイト単位で読み込み
while (dec_fifo_bcount < k) {
tmp = read_byte();
dec_fifo_bcache |= tmp << dec_fifo_bcount;
dec_fifo_bcount += 8;
}
// aiki: Rice符号の取得値の確定。右辺のvalueはunaryと同等。
value = (value << k) + (dec_fifo_bcache & bit_mask[k]);
// aiki: k[bit]読み込み後のキャッシュ更新
dec_fifo_bcache >>= k;
dec_fifo_bcount -= k;
dec_fifo_bcache &= bit_mask[dec_fifo_bcount];
}
if (level) {
// aiki: 指数移動平均式で平均を更新: sum1 <- (15/16) * sum1 + outval
rice->sum1 += value - (rice->sum1 >> 4);
if (rice->k1 > 0 && rice->sum1 < shift_16[rice->k1])
// aiki: k1 > 0 && sum1 < 16 * 2^(k1) ならば k1 が大きいので減らす
rice->k1--;
else if (rice->sum1 > shift_16[rice->k1 + 1])
// aiki: k1 > 0 && sum1 < 16 * 2^(k1) ならば k1 が大きいので減らす
rice->k1++;
// aiki: 出力値を足す: outval += 2^(k0)
value += bit_shift[rice->k0];
}
// aiki: 指数移動平均式で平均を更新: sum0 <- (15/16) * sum0 + outval
rice->sum0 += value - (rice->sum0 >> 4);
if (rice->k0 > 0 && rice->sum0 < shift_16[rice->k0])
// aiki: k0 > 0 && sum0 < 16 * 2^(k0) ならば k0 が大きいので減らす
rice->k0--;
else if (rice->sum0 > shift_16[rice->k0 + 1])
// aiki: sum0 > 32 * 2^(k0) ならば k0 が小さいので増やす
rice->k0++;
// aiki: 符号なし整数を符号付き整数に変換
value = DEC(value);
return value;
} // tta_decoder_get_value
ビット出力がよく洗練されている... static変数にしているのはむしろ扱いやすくしているのかもしれない。やっぱ俺はまだまだだ...
単独のフィルタでは伸び悩みを感じてきたので、奥の手たるカスケードを召喚した。組み合わせとしてはNGSA->SA, SA->NGSA, NGSA->NGSA, SA->SAがあったが、NGSA->SAが最もよく、フィルタ次数とブロックサイズ設定よってはwavpack(-hh)を超える結果を出したので、採用する。
良くなる理由は、カスケード接続が一般によい手段であることが指摘されている他、NGSAで相関除去が強く動くからではないかと想像している。
コード整理。NNGSAを廃止。1サンプル単位で予測できるように実装する。 static 関数とするのでインライン展開が効くはず。整理していたらすべてin-placeでやりきれそうに見えた。やってしまっていいか、寝てからやってしまおう。
AR係数次数0にするとSAになるわけだが、性能が良かったりする。。。。。もう少し検証必須。 また、SAは次数小さくしたほうが良い結果が出る。4とか、NGSAの半分とか。
係数の観察もしたい。丸めの影響も見たいところ。
TODO:
- 全部in-placeにしてバッファ1つでやりきるように整理
- SAのフィルタ次数策定