From 18a380a7c209b482f63adeb19f5d398dcb1c10bd Mon Sep 17 00:00:00 2001 From: Jean-Marc Valin <jmvalin@jmvalin.ca> Date: Wed, 20 Apr 2016 13:27:06 -0400 Subject: [PATCH] Make it possible to ignore inverted phase stereo for downmix purposes --- celt/bands.c | 9 +++++++-- celt/bands.h | 2 +- celt/celt_decoder.c | 29 ++++++++++++++++++++++++++++- celt/celt_encoder.c | 24 ++++++++++++++++++++++-- include/opus_defines.h | 26 +++++++++++++++++++++++++- src/opus_decoder.c | 20 ++++++++++++++++++++ src/opus_encoder.c | 20 ++++++++++++++++++++ tests/run_vectors.sh | 25 +++++++++++++++++-------- 8 files changed, 140 insertions(+), 15 deletions(-) diff --git a/celt/bands.c b/celt/bands.c index 09e743dad..bbe8a4c31 100644 --- a/celt/bands.c +++ b/celt/bands.c @@ -683,6 +683,7 @@ struct band_ctx { opus_uint32 seed; int arch; int theta_round; + int disable_inv; }; struct split_ctx { @@ -832,7 +833,7 @@ static void compute_theta(struct band_ctx *ctx, struct split_ctx *sctx, } else if (stereo) { if (encode) { - inv = itheta > 8192; + inv = itheta > 8192 && !ctx->disable_inv; if (inv) { int j; @@ -849,6 +850,9 @@ static void compute_theta(struct band_ctx *ctx, struct split_ctx *sctx, inv = ec_dec_bit_logp(ec, 2); } else inv = 0; + /* inv flag override to avoid problems with downmixing. */ + if (ctx->disable_inv) + inv = 0; itheta = 0; } qalloc = ec_tell_frac(ec) - tell; @@ -1379,7 +1383,7 @@ void quant_all_bands(int encode, const CELTMode *m, int start, int end, const celt_ener *bandE, int *pulses, int shortBlocks, int spread, int dual_stereo, int intensity, int *tf_res, opus_int32 total_bits, opus_int32 balance, ec_ctx *ec, int LM, int codedBands, - opus_uint32 *seed, int complexity, int arch) + opus_uint32 *seed, int complexity, int arch, int disable_inv) { int i; opus_int32 remaining_bits; @@ -1445,6 +1449,7 @@ void quant_all_bands(int encode, const CELTMode *m, int start, int end, ctx.seed = *seed; ctx.spread = spread; ctx.arch = arch; + ctx.disable_inv = disable_inv; ctx.resynth = resynth; ctx.theta_round = 0; for (i=start;i<end;i++) diff --git a/celt/bands.h b/celt/bands.h index 41d080f67..c040c7f74 100644 --- a/celt/bands.h +++ b/celt/bands.h @@ -105,7 +105,7 @@ void quant_all_bands(int encode, const CELTMode *m, int start, int end, const celt_ener *bandE, int *pulses, int shortBlocks, int spread, int dual_stereo, int intensity, int *tf_res, opus_int32 total_bits, opus_int32 balance, ec_ctx *ec, int M, int codedBands, opus_uint32 *seed, - int complexity, int arch); + int complexity, int arch, int disable_inv); void anti_collapse(const CELTMode *m, celt_norm *X_, unsigned char *collapse_masks, int LM, int C, int size, int start, diff --git a/celt/celt_decoder.c b/celt/celt_decoder.c index e441d0457..58e7b75a9 100644 --- a/celt/celt_decoder.c +++ b/celt/celt_decoder.c @@ -73,6 +73,7 @@ struct OpusCustomDecoder { int downsample; int start, end; int signalling; + int disable_inv; int arch; /* Everything beyond this point gets cleared on a reset */ @@ -163,6 +164,11 @@ OPUS_CUSTOM_NOSTATIC int opus_custom_decoder_init(CELTDecoder *st, const CELTMod st->start = 0; st->end = st->mode->effEBands; st->signalling = 1; +#ifdef ENABLE_UPDATE_DRAFT + st->disable_inv = channels == 1; +#else + st->disable_inv = 0; +#endif st->arch = opus_select_arch(); opus_custom_decoder_ctl(st, OPUS_RESET_STATE); @@ -1043,7 +1049,8 @@ int celt_decode_with_ec(CELTDecoder * OPUS_RESTRICT st, const unsigned char *dat quant_all_bands(0, mode, start, end, X, C==2 ? X+N : NULL, collapse_masks, NULL, pulses, shortBlocks, spread_decision, dual_stereo, intensity, tf_res, - len*(8<<BITRES)-anti_collapse_rsv, balance, dec, LM, codedBands, &st->rng, 0, st->arch); + len*(8<<BITRES)-anti_collapse_rsv, balance, dec, LM, codedBands, &st->rng, 0, + st->arch, st->disable_inv); if (anti_collapse_rsv > 0) { @@ -1298,6 +1305,26 @@ int opus_custom_decoder_ctl(CELTDecoder * OPUS_RESTRICT st, int request, ...) *value=st->rng; } break; + case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 value = va_arg(ap, opus_int32); + if(value<0 || value>1) + { + goto bad_arg; + } + st->disable_inv = value; + } + break; + case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 *value = va_arg(ap, opus_int32*); + if (!value) + { + goto bad_arg; + } + *value = st->disable_inv; + } + break; default: goto bad_request; } diff --git a/celt/celt_encoder.c b/celt/celt_encoder.c index 1a031d9e1..5be76103c 100644 --- a/celt/celt_encoder.c +++ b/celt/celt_encoder.c @@ -75,6 +75,7 @@ struct OpusCustomEncoder { int lsb_depth; int variable_duration; int lfe; + int disable_inv; int arch; /* Everything beyond this point gets cleared on a reset */ @@ -181,7 +182,6 @@ static int opus_custom_encoder_init_arch(CELTEncoder *st, const CELTMode *mode, st->start = 0; st->end = st->mode->effEBands; st->signalling = 1; - st->arch = arch; st->constrained_vbr = 1; @@ -2079,7 +2079,7 @@ int celt_encode_with_ec(CELTEncoder * OPUS_RESTRICT st, const opus_val16 * pcm, quant_all_bands(1, mode, start, end, X, C==2 ? X+N : NULL, collapse_masks, bandE, pulses, shortBlocks, st->spread_decision, dual_stereo, st->intensity, tf_res, nbCompressedBytes*(8<<BITRES)-anti_collapse_rsv, - balance, enc, LM, codedBands, &st->rng, st->complexity, st->arch); + balance, enc, LM, codedBands, &st->rng, st->complexity, st->arch, st->disable_inv); if (anti_collapse_rsv > 0) { @@ -2376,6 +2376,26 @@ int opus_custom_encoder_ctl(CELTEncoder * OPUS_RESTRICT st, int request, ...) st->variable_duration = value; } break; + case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 value = va_arg(ap, opus_int32); + if(value<0 || value>1) + { + goto bad_arg; + } + st->disable_inv = value; + } + break; + case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 *value = va_arg(ap, opus_int32*); + if (!value) + { + goto bad_arg; + } + *value = st->disable_inv; + } + break; case OPUS_RESET_STATE: { int i; diff --git a/include/opus_defines.h b/include/opus_defines.h index 315412dd1..0d9aab087 100644 --- a/include/opus_defines.h +++ b/include/opus_defines.h @@ -165,8 +165,9 @@ extern "C" { #define OPUS_GET_EXPERT_FRAME_DURATION_REQUEST 4041 #define OPUS_SET_PREDICTION_DISABLED_REQUEST 4042 #define OPUS_GET_PREDICTION_DISABLED_REQUEST 4043 - /* Don't use 4045, it's already taken by OPUS_GET_GAIN_REQUEST */ +#define OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST 4046 +#define OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST 4047 /* Macros to trigger compilation errors when the wrong types are provided to a CTL */ #define __opus_check_int(x) (((void)((x) == (opus_int32)0)), (opus_int32)(x)) @@ -681,6 +682,29 @@ extern "C" { */ #define OPUS_GET_SAMPLE_RATE(x) OPUS_GET_SAMPLE_RATE_REQUEST, __opus_check_int_ptr(x) +/** If set to 1, disables the use of phase inversion for intensity stereo, improving the + * quality of mono downmixes, but slightly reducing normal stereo quality. Disabling phase + * inversion does not comply with RFC6716, even though it does not cause any + * interoperability issue. It will become part of the Opus standard once RFC6716 gets + * updated with draft-ietf-codec-opus-update. + * @see OPUS_GET_PHASE_INVERSION_DISABLED + * @param[in] x <tt>opus_int32</tt>: Allowed values: + * <dl> + * <dt>0</dt><dd>Enable phase inversion (default).</dd> + * <dt>1</dt><dd>Disable phase inversion.</dd> + * </dl> + * @hideinitializer */ +#define OPUS_SET_PHASE_INVERSION_DISABLED(x) OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST, __opus_check_int(x) +/** Gets the encoder's configured phase inversion status. + * @see OPUS_SET_PHASE_INVERSION_DISABLED + * @param[out] x <tt>opus_int32 *</tt>: Returns one of the following values: + * <dl> + * <dt>0</dt><dd>Stereo phase inversion enabled (default).</dd> + * <dt>1</dt><dd>Stereo phase inversion disabled.</dd> + * </dl> + * @hideinitializer */ +#define OPUS_GET_PHASE_INVERSION_DISABLED(x) OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST, __opus_check_int_ptr(x) + /**@}*/ /** @defgroup opus_decoderctls Decoder related CTLs diff --git a/src/opus_decoder.c b/src/opus_decoder.c index 080bec507..af46d82f0 100644 --- a/src/opus_decoder.c +++ b/src/opus_decoder.c @@ -899,6 +899,26 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...) *value = st->last_packet_duration; } break; + case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 value = va_arg(ap, opus_int32); + if(value<0 || value>1) + { + goto bad_arg; + } + celt_decoder_ctl(celt_dec, OPUS_SET_PHASE_INVERSION_DISABLED(value)); + } + break; + case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 *value = va_arg(ap, opus_int32*); + if (!value) + { + goto bad_arg; + } + celt_decoder_ctl(celt_dec, OPUS_GET_PHASE_INVERSION_DISABLED(value)); + } + break; default: /*fprintf(stderr, "unknown opus_decoder_ctl() request: %d", request);*/ ret = OPUS_UNIMPLEMENTED; diff --git a/src/opus_encoder.c b/src/opus_encoder.c index f484b0a42..ef464fc55 100644 --- a/src/opus_encoder.c +++ b/src/opus_encoder.c @@ -2781,6 +2781,26 @@ int opus_encoder_ctl(OpusEncoder *st, int request, ...) *value = st->silk_mode.reducedDependency; } break; + case OPUS_SET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 value = va_arg(ap, opus_int32); + if(value<0 || value>1) + { + goto bad_arg; + } + celt_encoder_ctl(celt_enc, OPUS_SET_PHASE_INVERSION_DISABLED(value)); + } + break; + case OPUS_GET_PHASE_INVERSION_DISABLED_REQUEST: + { + opus_int32 *value = va_arg(ap, opus_int32*); + if (!value) + { + goto bad_arg; + } + celt_encoder_ctl(celt_enc, OPUS_GET_PHASE_INVERSION_DISABLED(value)); + } + break; case OPUS_RESET_STATE: { void *silk_enc; diff --git a/tests/run_vectors.sh b/tests/run_vectors.sh index 1d447c43f..41aaff848 100755 --- a/tests/run_vectors.sh +++ b/tests/run_vectors.sh @@ -33,8 +33,8 @@ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -rm logs_mono.txt -rm logs_stereo.txt +rm -f logs_mono.txt logs_mono2.txt +rm -f logs_stereo.txt logs_stereo2.txt if [ "$#" -ne "3" ]; then echo "usage: run_vectors.sh <exec path> <vector path> <rate>" @@ -87,9 +87,11 @@ do echo ERROR: decoding failed exit 1 fi - $OPUS_COMPARE -r $RATE $VECTOR_PATH/testvector$file.dec tmp.out >> logs_mono.txt 2>&1 + $OPUS_COMPARE -r $RATE $VECTOR_PATH/testvector${file}.dec tmp.out >> logs_mono.txt 2>&1 float_ret=$? - if [ "$float_ret" -eq "0" ]; then + $OPUS_COMPARE -r $RATE $VECTOR_PATH/testvector${file}m.dec tmp.out >> logs_mono2.txt 2>&1 + float_ret2=$? + if [ "$float_ret" -eq "0" ] || [ "$float_ret2" -eq "0" ]; then echo output matches reference else echo ERROR: output does not match reference @@ -116,9 +118,11 @@ do echo ERROR: decoding failed exit 1 fi - $OPUS_COMPARE -s -r $RATE $VECTOR_PATH/testvector$file.dec tmp.out >> logs_stereo.txt 2>&1 + $OPUS_COMPARE -s -r $RATE $VECTOR_PATH/testvector${file}.dec tmp.out >> logs_stereo.txt 2>&1 float_ret=$? - if [ "$float_ret" -eq "0" ]; then + $OPUS_COMPARE -s -r $RATE $VECTOR_PATH/testvector${file}m.dec tmp.out >> logs_stereo2.txt 2>&1 + float_ret2=$? + if [ "$float_ret" -eq "0" ] || [ "$float_ret2" -eq "0" ]; then echo output matches reference else echo ERROR: output does not match reference @@ -130,5 +134,10 @@ done echo All tests have passed successfully -grep quality logs_mono.txt | awk '{sum+=$4}END{print "Average mono quality is", sum/NR, "%"}' -grep quality logs_stereo.txt | awk '{sum+=$4}END{print "Average stereo quality is", sum/NR, "%"}' +mono1=`grep quality logs_mono.txt | awk '{sum+=$4}END{if (NR == 12) sum /= 12; else sum = 0; print sum}'` +mono2=`grep quality logs_mono2.txt | awk '{sum+=$4}END{if (NR == 12) sum /= 12; else sum = 0; print sum}'` +echo $mono1 $mono2 | awk '{if ($2 > $1) $1 = $2; print "Average mono quality is", $1, "%"}' + +stereo1=`grep quality logs_stereo.txt | awk '{sum+=$4}END{if (NR == 12) sum /= 12; else sum = 0; print sum}'` +stereo2=`grep quality logs_stereo2.txt | awk '{sum+=$4}END{if (NR == 12) sum /= 12; else sum = 0; print sum}'` +echo $stereo1 $stereo2 | awk '{if ($2 > $1) $1 = $2; print "Average stereo quality is", $1, "%"}' -- GitLab