From 21af73eb217f64fef2f549bc752b860fc95e0293 Mon Sep 17 00:00:00 2001 From: "Timothy B. Terriberry" Date: Wed, 19 Jan 2011 16:30:03 -0800 Subject: [PATCH] Make collapse-detection bitexact. Jean-Marc's original anti-collapse patch used a threshold on the content of a decoded band to determine whether or not it should be filled with random noise. Since this is highly sensitive to the accuracy of the implementation, it could lead to significant decoder output differences even if decoding error up to that point was relatively small. This patch detects collapsed bands from the output of the vector quantizer, using exact integer arithmetic. It makes two simplifying assumptions: a) If either input to haar1() is non-zero during TF resolution adjustments, then the output will be non-zero. b) If the content of a block is non-zero in any of the bands that are used for folding, then the folded output will be non-zero. b) in particular is likely to be false when SPREAD_NONE is used. It also ignores the case where mid and side are orthogonal in stereo_merge, but this is relatively unlikely. This misses just over 3% of the cases that Jean-Marc's anti-collapse detection strategy would catch, but does not mis-classify any (all detected collapses are true collapses). This patch overloads the "fill" parameter to mark which blocks have non-zero content for folding. As a consequence, if a set of blocks on one side of a split has collapsed, _no_ folding is done: the result would be zero anyway, except for short blocks with SPREAD_AGGRESSIVE that are split down to a single block, but a) that means a lot of bits were available so a collapse is unlikely and b) anti-collapse can fill the block anyway, if it's used. This also means that if itheta==0 or itheta==16384, we no longer fold at all on that side (even with long blocks), since we'd be multiplying the result by zero anyway. --- libcelt/bands.c | 98 ++++++++++++++++++++++++++++++++++++------------- libcelt/bands.h | 4 +- libcelt/celt.c | 28 +++++++++----- libcelt/vq.c | 32 +++++++++++++++- libcelt/vq.h | 6 ++- 5 files changed, 127 insertions(+), 41 deletions(-) diff --git a/libcelt/bands.c b/libcelt/bands.c index 0951c549..04b1c90f 100644 --- a/libcelt/bands.c +++ b/libcelt/bands.c @@ -212,7 +212,7 @@ void denormalise_bands(const CELTMode *m, const celt_norm * restrict X, celt_sig } /* This prevents energy collapse for transients with multiple short MDCTs */ -void anti_collapse(const CELTMode *m, celt_norm *_X, int LM, int C, int size, +void anti_collapse(const CELTMode *m, celt_norm *_X, unsigned char *collapse_masks, int LM, int C, int size, int start, int end, celt_word16 *logE, celt_word16 *prev1logE, celt_word16 *prev2logE, int *pulses, celt_uint32 seed) { @@ -257,11 +257,8 @@ void anti_collapse(const CELTMode *m, celt_norm *_X, int LM, int C, int size, X = _X+c*size+(m->eBands[i]<>k, 1<>k, 1<>=recombine; N_B<<=recombine; @@ -691,6 +690,7 @@ static void quant_band(int encode, const CELTMode *m, int i, celt_norm *X, celt_ haar1(X, N_B, B); if (lowband) haar1(lowband, N_B, B); + fill |= fill<>= 1; time_divide++; @@ -718,6 +718,8 @@ static void quant_band(int encode, const CELTMode *m, int i, celt_norm *X, celt_ Y = X+N; split = 1; LM -= 1; + if (B==1) + fill |= fill<<1; B = (B+1)>>1; } } @@ -853,11 +855,13 @@ static void quant_band(int encode, const CELTMode *m, int i, celt_norm *X, celt_ { imid = 32767; iside = 0; + fill &= (1<>B)<>20; } + cm = (1<>B; stereo_merge(X, Y, mid, N); + } if (inv) { int j; @@ -1044,11 +1058,15 @@ static void quant_band(int encode, const CELTMode *m, int i, celt_norm *X, celt_ { B >>= 1; N_B <<= 1; + cm |= cm>>B; haar1(X, N_B, B); } for (k=0;k>k, 1<>=recombine; @@ -1063,10 +1081,11 @@ static void quant_band(int encode, const CELTMode *m, int i, celt_norm *X, celt_ } } } + return cm; } void quant_all_bands(int encode, const CELTMode *m, int start, int end, - celt_norm *_X, celt_norm *_Y, const celt_ener *bandE, int *pulses, + celt_norm *_X, celt_norm *_Y, unsigned char *collapse_masks, const celt_ener *bandE, int *pulses, int shortBlocks, int spread, int dual_stereo, int intensity, int *tf_res, int resynth, int total_bits, void *ec, int LM, int codedBands) { @@ -1097,7 +1116,7 @@ void quant_all_bands(int encode, const CELTMode *m, int start, int end, else seed = ((ec_dec*)ec)->rng; balance = 0; - lowband_offset = -1; + lowband_offset = 0; for (i=start;i= M*eBands[start] && (update_lowband || lowband_offset==-1)) - lowband_offset = M*eBands[i]; + if (M*eBands[i]-N >= M*eBands[start] && (update_lowband || lowband_offset==0)) + lowband_offset = i; tf_change = tf_res[i]; if (i>=m->effEBands) @@ -1143,8 +1164,30 @@ void quant_all_bands(int encode, const CELTMode *m, int start, int end, } /* This ensures we never repeat spectral content within one band */ - if (lowband_offset != -1) - effective_lowband = IMAX(M*eBands[start], lowband_offset-N); + if (lowband_offset != 0) + effective_lowband = IMAX(M*eBands[start], M*eBands[lowband_offset]-N); + + /* Get a conservative estimate of the collapse_mask's for the bands we're + going to be folding from. */ + if (lowband_offset != 0 && (spread!=SPREAD_AGGRESSIVE || B>1)) + { + int fold_start; + int fold_end; + int fold_i; + fold_start = lowband_offset; + while(M*eBands[--fold_start] > effective_lowband); + fold_end = lowband_offset-1; + while(M*eBands[++fold_end] < effective_lowband+N); + x_cm = y_cm = 0; + fold_i = fold_start; do { + x_cm |= collapse_masks[fold_i*C+0]; + y_cm |= collapse_masks[fold_i*C+1]; + } while (++fold_imode->nbEBands, int); ALLOC(fine_priority, st->mode->nbEBands, int); - /* bits = packet size - where we are - safety - anti-collapse*/ - bits = (nbCompressedBytes*8<=2 ? (1<=2&&bits>=(LM+2<mode, st->start, st->end, offsets, alloc_trim, &intensity, &dual_stereo, bits, pulses, fine_quant, fine_priority, C, LM, enc, 1, st->lastCodedBands); @@ -1286,11 +1290,12 @@ int celt_encode_with_ec_float(CELTEncoder * restrict st, const celt_sig * pcm, i #endif /* Residual quantisation */ - quant_all_bands(1, st->mode, st->start, st->end, X, C==2 ? X+N : NULL, + ALLOC(collapse_masks, st->mode->nbEBands, unsigned char); + quant_all_bands(1, st->mode, st->start, st->end, X, C==2 ? X+N : NULL, collapse_masks, bandE, pulses, shortBlocks, st->spread_decision, dual_stereo, intensity, tf_res, resynth, nbCompressedBytes*8, enc, LM, codedBands); - if (isTransient && LM>=2) + if (anti_collapse_rsv > 0) { anti_collapse_on = st->consec_transient<2; ec_enc_bits(enc, anti_collapse_on, 1); @@ -1311,7 +1316,7 @@ int celt_encode_with_ec_float(CELTEncoder * restrict st, const celt_sig * pcm, i #endif if (anti_collapse_on) { - anti_collapse(st->mode, X, LM, C, N, + anti_collapse(st->mode, X, collapse_masks, LM, C, N, st->start, st->end, oldBandE, oldLogE, oldLogE2, pulses, enc->rng); } @@ -1882,6 +1887,7 @@ int celt_decode_with_ec_float(CELTDecoder * restrict st, const unsigned char *da VARDECL(int, offsets); VARDECL(int, fine_priority); VARDECL(int, tf_res); + VARDECL(unsigned char, collapse_masks); celt_sig *out_mem[2]; celt_sig *decode_mem[2]; celt_sig *overlap_mem[2]; @@ -1905,6 +1911,7 @@ int celt_decode_with_ec_float(CELTDecoder * restrict st, const unsigned char *da celt_int32 tell; int dynalloc_logp; int postfilter_tapset; + int anti_collapse_rsv; int anti_collapse_on=0; SAVE_STACK; @@ -2060,7 +2067,9 @@ int celt_decode_with_ec_float(CELTDecoder * restrict st, const unsigned char *da alloc_trim = tell+(6<=2 ? (1<=2&&bits>=(LM+2<mode, st->start, st->end, offsets, alloc_trim, &intensity, &dual_stereo, bits, pulses, fine_quant, fine_priority, C, LM, dec, 0, 0); @@ -2068,11 +2077,12 @@ int celt_decode_with_ec_float(CELTDecoder * restrict st, const unsigned char *da unquant_fine_energy(st->mode, st->start, st->end, bandE, oldBandE, fine_quant, dec, C); /* Decode fixed codebook */ - quant_all_bands(0, st->mode, st->start, st->end, X, C==2 ? X+N : NULL, + ALLOC(collapse_masks, st->mode->nbEBands, unsigned char); + quant_all_bands(0, st->mode, st->start, st->end, X, C==2 ? X+N : NULL, collapse_masks, NULL, pulses, shortBlocks, spread_decision, dual_stereo, intensity, tf_res, 1, len*8, dec, LM, codedBands); - if (isTransient && LM>=2) + if (anti_collapse_rsv > 0) { anti_collapse_on = ec_dec_bits(dec, 1); } @@ -2081,7 +2091,7 @@ int celt_decode_with_ec_float(CELTDecoder * restrict st, const unsigned char *da fine_quant, fine_priority, len*8-ec_dec_tell(dec, 0), dec, C); if (anti_collapse_on) - anti_collapse(st->mode, X, LM, C, N, + anti_collapse(st->mode, X, collapse_masks, LM, C, N, st->start, st->end, oldBandE, oldLogE, oldLogE2, pulses, dec->rng); log2Amp(st->mode, st->start, st->end, bandE, oldBandE, C); diff --git a/libcelt/vq.c b/libcelt/vq.c index ed2c46a9..e5f2b98f 100644 --- a/libcelt/vq.c +++ b/libcelt/vq.c @@ -106,6 +106,8 @@ static void exp_rotation(celt_norm *X, int len, int dir, int stride, int K, int while ((stride2*stride2+stride2)*stride + (stride>>2) < len) stride2++; } + /*TODO: We should be passing around log2(B), not B, for both this and for + extract_collapse_mask().*/ len /= stride; for (i=0;i