Improved AC3 decoder level support (heavy drc, dialnorm)

Added support for AC3 heavy dynamic range compression used
to restrict the output range and added a setting to specify
the output target level and use the dialog normalization
field to apply it in the digital domain.

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
pull/88/head
Jean-Francois Thibert 10 years ago committed by Michael Niedermayer
parent 7a67ab5cba
commit 12df9b9a15
  1. 4
      libavcodec/ac3.h
  2. 45
      libavcodec/ac3dec.c
  3. 7
      libavcodec/ac3dec.h
  4. 99
      libavcodec/ac3dec_fixed.c
  5. 2
      libavcodec/ac3dec_float.c

@ -67,7 +67,8 @@
#define AC3_RENAME(x) x ## _fixed #define AC3_RENAME(x) x ## _fixed
#define AC3_NORM(norm) (1<<24)/(norm) #define AC3_NORM(norm) (1<<24)/(norm)
#define AC3_MUL(a,b) ((((int64_t) (a)) * (b))>>12) #define AC3_MUL(a,b) ((((int64_t) (a)) * (b))>>12)
#define AC3_RANGE(x) (x) #define AC3_RANGE(x) (x|((x&128)<<1))
#define AC3_HEAVY_RANGE(x) (x<<1)
#define AC3_DYNAMIC_RANGE(x) (x) #define AC3_DYNAMIC_RANGE(x) (x)
#define AC3_SPX_BLEND(x) (x) #define AC3_SPX_BLEND(x) (x)
#define AC3_DYNAMIC_RANGE1 0 #define AC3_DYNAMIC_RANGE1 0
@ -86,6 +87,7 @@
#define AC3_NORM(norm) (1.0f/(norm)) #define AC3_NORM(norm) (1.0f/(norm))
#define AC3_MUL(a,b) ((a) * (b)) #define AC3_MUL(a,b) ((a) * (b))
#define AC3_RANGE(x) (dynamic_range_tab[(x)]) #define AC3_RANGE(x) (dynamic_range_tab[(x)])
#define AC3_HEAVY_RANGE(x) (heavy_dynamic_range_tab[(x)])
#define AC3_DYNAMIC_RANGE(x) (powf(x, s->drc_scale)) #define AC3_DYNAMIC_RANGE(x) (powf(x, s->drc_scale))
#define AC3_SPX_BLEND(x) (x)* (1.0f/32) #define AC3_SPX_BLEND(x) (x)* (1.0f/32)
#define AC3_DYNAMIC_RANGE1 1.0f #define AC3_DYNAMIC_RANGE1 1.0f

@ -65,6 +65,7 @@ static const uint8_t quantization_tab[16] = {
/** dynamic range table. converts codes to scale factors. */ /** dynamic range table. converts codes to scale factors. */
static float dynamic_range_tab[256]; static float dynamic_range_tab[256];
static float heavy_dynamic_range_tab[256];
/** Adjustments in dB gain */ /** Adjustments in dB gain */
static const float gain_levels[9] = { static const float gain_levels[9] = {
@ -164,6 +165,14 @@ static av_cold void ac3_tables_init(void)
int v = (i >> 5) - ((i >> 7) << 3) - 5; int v = (i >> 5) - ((i >> 7) << 3) - 5;
dynamic_range_tab[i] = powf(2.0f, v) * ((i & 0x1F) | 0x20); dynamic_range_tab[i] = powf(2.0f, v) * ((i & 0x1F) | 0x20);
} }
/* generate compr dynamic range table
reference: Section 7.7.2 Heavy Compression */
for (i = 0; i < 256; i++) {
int v = (i >> 4) - ((i >> 7) << 4) - 4;
heavy_dynamic_range_tab[i] = powf(2.0f, v) * ((i & 0xF) | 0x10);
}
} }
/** /**
@ -236,9 +245,19 @@ static int ac3_parse_header(AC3DecodeContext *s)
/* read the rest of the bsi. read twice for dual mono mode. */ /* read the rest of the bsi. read twice for dual mono mode. */
i = !s->channel_mode; i = !s->channel_mode;
do { do {
skip_bits(gbc, 5); // skip dialog normalization s->dialog_normalization[(!s->channel_mode)-i] = -get_bits(gbc, 5);
if (get_bits1(gbc)) if (s->dialog_normalization[(!s->channel_mode)-i] == 0) {
skip_bits(gbc, 8); //skip compression s->dialog_normalization[(!s->channel_mode)-i] = -31;
}
if (s->target_level != 0) {
s->level_gain[(!s->channel_mode)-i] = powf(2.0f,
(float)(s->target_level -
s->dialog_normalization[(!s->channel_mode)-i])/6.0f);
}
if (s->compression_exists[(!s->channel_mode)-i] = get_bits1(gbc)) {
s->heavy_dynamic_range[(!s->channel_mode)-i] =
AC3_HEAVY_RANGE(get_bits(gbc, 8));
}
if (get_bits1(gbc)) if (get_bits1(gbc))
skip_bits(gbc, 8); //skip language code skip_bits(gbc, 8); //skip language code
if (get_bits1(gbc)) if (get_bits1(gbc))
@ -819,8 +838,9 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
if (get_bits1(gbc)) { if (get_bits1(gbc)) {
/* Allow asymmetric application of DRC when drc_scale > 1. /* Allow asymmetric application of DRC when drc_scale > 1.
Amplification of quiet sounds is enhanced */ Amplification of quiet sounds is enhanced */
INTFLOAT range = AC3_RANGE(get_bits(gbc, 8)); int range_bits = get_bits(gbc, 8);
if (range > 1.0 || s->drc_scale <= 1.0) INTFLOAT range = AC3_RANGE(range_bits);
if (range_bits <= 127 || s->drc_scale <= 1.0)
s->dynamic_range[i] = AC3_DYNAMIC_RANGE(range); s->dynamic_range[i] = AC3_DYNAMIC_RANGE(range);
else else
s->dynamic_range[i] = range; s->dynamic_range[i] = range;
@ -1314,15 +1334,20 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
/* apply scaling to coefficients (headroom, dynrng) */ /* apply scaling to coefficients (headroom, dynrng) */
for (ch = 1; ch <= s->channels; ch++) { for (ch = 1; ch <= s->channels; ch++) {
int audio_channel = 0;
INTFLOAT gain; INTFLOAT gain;
if(s->channel_mode == AC3_CHMODE_DUALMONO) { if (s->channel_mode == AC3_CHMODE_DUALMONO)
gain = s->dynamic_range[2-ch]; audio_channel = 2-ch;
} else { if (s->heavy_compression && s->compression_exists[audio_channel])
gain = s->dynamic_range[0]; gain = s->heavy_dynamic_range[audio_channel];
} else
gain = s->dynamic_range[audio_channel];
#if USE_FIXED #if USE_FIXED
scale_coefs(s->transform_coeffs[ch], s->fixed_coeffs[ch], gain, 256); scale_coefs(s->transform_coeffs[ch], s->fixed_coeffs[ch], gain, 256);
#else #else
if (s->target_level != 0)
gain = gain * s->level_gain[audio_channel];
gain *= 1.0 / 4194304.0f; gain *= 1.0 / 4194304.0f;
s->fmt_conv.int32_to_float_fmul_scalar(s->transform_coeffs[ch], s->fmt_conv.int32_to_float_fmul_scalar(s->transform_coeffs[ch],
s->fixed_coeffs[ch], gain, 256); s->fixed_coeffs[ch], gain, 256);

@ -84,6 +84,9 @@ typedef struct AC3DecodeContext {
int bitstream_mode; ///< bitstream mode (bsmod) int bitstream_mode; ///< bitstream mode (bsmod)
int channel_mode; ///< channel mode (acmod) int channel_mode; ///< channel mode (acmod)
int lfe_on; ///< lfe channel in use int lfe_on; ///< lfe channel in use
int dialog_normalization[2]; ///< dialog level in dBFS (dialnorm)
int compression_exists[2]; ///< compression field is valid for frame (compre)
int compression_gain[2]; ///< gain to apply for heavy compression (compr)
int channel_map; ///< custom channel map int channel_map; ///< custom channel map
int preferred_downmix; ///< Preferred 2-channel downmix mode (dmixmod) int preferred_downmix; ///< Preferred 2-channel downmix mode (dmixmod)
int center_mix_level; ///< Center mix level index int center_mix_level; ///< Center mix level index
@ -103,6 +106,8 @@ typedef struct AC3DecodeContext {
float ltrt_surround_mix_level; float ltrt_surround_mix_level;
float loro_center_mix_level; float loro_center_mix_level;
float loro_surround_mix_level; float loro_surround_mix_level;
int target_level; ///< target level in dBFS
float level_gain[2];
///@name Frame syntax parameters ///@name Frame syntax parameters
int snr_offset_strategy; ///< SNR offset strategy (snroffststr) int snr_offset_strategy; ///< SNR offset strategy (snroffststr)
@ -161,6 +166,8 @@ typedef struct AC3DecodeContext {
///@name Dynamic range ///@name Dynamic range
INTFLOAT dynamic_range[2]; ///< dynamic range INTFLOAT dynamic_range[2]; ///< dynamic range
INTFLOAT drc_scale; ///< percentage of dynamic range compression to be applied INTFLOAT drc_scale; ///< percentage of dynamic range compression to be applied
int heavy_compression; ///< apply heavy compression
INTFLOAT heavy_dynamic_range[2]; ///< heavy dynamic range compression
///@} ///@}
///@name Bandwidth ///@name Bandwidth

@ -81,40 +81,69 @@ static void scale_coefs (
int temp, temp1, temp2, temp3, temp4, temp5, temp6, temp7; int temp, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
mul = (dynrng & 0x1f) + 0x20; mul = (dynrng & 0x1f) + 0x20;
shift = 4 - ((dynrng << 24) >> 29); shift = 4 - ((dynrng << 23) >> 28);
round = 1 << (shift-1); if (shift > 0 ) {
for (i=0; i<len; i+=8) { round = 1 << (shift-1);
for (i=0; i<len; i+=8) {
temp = src[i] * mul;
temp1 = src[i+1] * mul; temp = src[i] * mul;
temp = temp + round; temp1 = src[i+1] * mul;
temp2 = src[i+2] * mul; temp = temp + round;
temp2 = src[i+2] * mul;
temp1 = temp1 + round;
dst[i] = temp >> shift; temp1 = temp1 + round;
temp3 = src[i+3] * mul; dst[i] = temp >> shift;
temp2 = temp2 + round; temp3 = src[i+3] * mul;
temp2 = temp2 + round;
dst[i+1] = temp1 >> shift;
temp4 = src[i + 4] * mul; dst[i+1] = temp1 >> shift;
temp3 = temp3 + round; temp4 = src[i + 4] * mul;
dst[i+2] = temp2 >> shift; temp3 = temp3 + round;
dst[i+2] = temp2 >> shift;
temp5 = src[i+5] * mul;
temp4 = temp4 + round; temp5 = src[i+5] * mul;
dst[i+3] = temp3 >> shift; temp4 = temp4 + round;
temp6 = src[i+6] * mul; dst[i+3] = temp3 >> shift;
temp6 = src[i+6] * mul;
dst[i+4] = temp4 >> shift;
temp5 = temp5 + round; dst[i+4] = temp4 >> shift;
temp7 = src[i+7] * mul; temp5 = temp5 + round;
temp6 = temp6 + round; temp7 = src[i+7] * mul;
temp6 = temp6 + round;
dst[i+5] = temp5 >> shift;
temp7 = temp7 + round; dst[i+5] = temp5 >> shift;
dst[i+6] = temp6 >> shift; temp7 = temp7 + round;
dst[i+7] = temp7 >> shift; dst[i+6] = temp6 >> shift;
dst[i+7] = temp7 >> shift;
}
} else {
shift = -shift;
for (i=0; i<len; i+=8) {
temp = src[i] * mul;
temp1 = src[i+1] * mul;
temp2 = src[i+2] * mul;
dst[i] = temp << shift;
temp3 = src[i+3] * mul;
dst[i+1] = temp1 << shift;
temp4 = src[i + 4] * mul;
dst[i+2] = temp2 << shift;
temp5 = src[i+5] * mul;
dst[i+3] = temp3 << shift;
temp6 = src[i+6] * mul;
dst[i+4] = temp4 << shift;
temp7 = src[i+7] * mul;
dst[i+5] = temp5 << shift;
dst[i+6] = temp6 << shift;
dst[i+7] = temp7 << shift;
}
} }
} }
@ -150,6 +179,8 @@ static void ac3_downmix_c_fixed16(int16_t **samples, int16_t (*matrix)[2],
#include "ac3dec.c" #include "ac3dec.c"
static const AVOption options[] = { static const AVOption options[] = {
{ "drc_scale", "percentage of dynamic range compression to apply", OFFSET(drc_scale), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 6.0, PAR },
{ "heavy_compr", "heavy dynamic range compression enabled", OFFSET(heavy_compression), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, PAR },
{ NULL}, { NULL},
}; };

@ -32,6 +32,8 @@
static const AVOption options[] = { static const AVOption options[] = {
{ "drc_scale", "percentage of dynamic range compression to apply", OFFSET(drc_scale), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 6.0, PAR }, { "drc_scale", "percentage of dynamic range compression to apply", OFFSET(drc_scale), AV_OPT_TYPE_FLOAT, {.dbl = 1.0}, 0.0, 6.0, PAR },
{ "heavy_compr", "heavy dynamic range compression enabled", OFFSET(heavy_compression), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, PAR },
{ "target_level", "target level in -dBFS (0 not applied)", OFFSET(target_level), AV_OPT_TYPE_INT, {.i64 = 0 }, -31, 0, PAR },
{"dmix_mode", "Preferred Stereo Downmix Mode", OFFSET(preferred_stereo_downmix), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 2, 0, "dmix_mode"}, {"dmix_mode", "Preferred Stereo Downmix Mode", OFFSET(preferred_stereo_downmix), AV_OPT_TYPE_INT, {.i64 = -1 }, -1, 2, 0, "dmix_mode"},
{"ltrt_cmixlev", "Lt/Rt Center Mix Level", OFFSET(ltrt_center_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, 0}, {"ltrt_cmixlev", "Lt/Rt Center Mix Level", OFFSET(ltrt_center_mix_level), AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, 0},

Loading…
Cancel
Save