Improved AC3 decoder level support (heavy drc, dialnorm)
authorJean-Francois Thibert <jfthibert@google.com>
Tue, 26 Aug 2014 23:16:06 +0000 (19:16 -0400)
committerMichael Niedermayer <michaelni@gmx.at>
Wed, 27 Aug 2014 12:31:39 +0000 (14:31 +0200)
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>
libavcodec/ac3.h
libavcodec/ac3dec.c
libavcodec/ac3dec.h
libavcodec/ac3dec_fixed.c
libavcodec/ac3dec_float.c

index 542f79d..871640b 100644 (file)
@@ -67,7 +67,8 @@
 #define AC3_RENAME(x)           x ## _fixed
 #define AC3_NORM(norm)          (1<<24)/(norm)
 #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_SPX_BLEND(x)        (x)
 #define AC3_DYNAMIC_RANGE1      0
@@ -86,6 +87,7 @@
 #define AC3_NORM(norm)          (1.0f/(norm))
 #define AC3_MUL(a,b)            ((a) * (b))
 #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_SPX_BLEND(x)        (x)* (1.0f/32)
 #define AC3_DYNAMIC_RANGE1      1.0f
index 72c4185..969e37f 100644 (file)
@@ -65,6 +65,7 @@ static const uint8_t quantization_tab[16] = {
 
 /** dynamic range table. converts codes to scale factors. */
 static float dynamic_range_tab[256];
+static float heavy_dynamic_range_tab[256];
 
 /** Adjustments in dB gain */
 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;
         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. */
     i = !s->channel_mode;
     do {
-        skip_bits(gbc, 5); // skip dialog normalization
-        if (get_bits1(gbc))
-            skip_bits(gbc, 8); //skip compression
+        s->dialog_normalization[(!s->channel_mode)-i] = -get_bits(gbc, 5);
+        if (s->dialog_normalization[(!s->channel_mode)-i] == 0) {
+            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))
             skip_bits(gbc, 8); //skip language code
         if (get_bits1(gbc))
@@ -819,8 +838,9 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
         if (get_bits1(gbc)) {
             /* Allow asymmetric application of DRC when drc_scale > 1.
                Amplification of quiet sounds is enhanced */
-            INTFLOAT range = AC3_RANGE(get_bits(gbc, 8));
-            if (range > 1.0 || s->drc_scale <= 1.0)
+            int range_bits = get_bits(gbc, 8);
+            INTFLOAT range = AC3_RANGE(range_bits);
+            if (range_bits <= 127 || s->drc_scale <= 1.0)
                 s->dynamic_range[i] = AC3_DYNAMIC_RANGE(range);
             else
                 s->dynamic_range[i] = range;
@@ -1314,15 +1334,20 @@ static int decode_audio_block(AC3DecodeContext *s, int blk)
 
     /* apply scaling to coefficients (headroom, dynrng) */
     for (ch = 1; ch <= s->channels; ch++) {
+        int audio_channel = 0;
         INTFLOAT gain;
-        if(s->channel_mode == AC3_CHMODE_DUALMONO) {
-            gain = s->dynamic_range[2-ch];
-        } else {
-            gain = s->dynamic_range[0];
-        }
+        if (s->channel_mode == AC3_CHMODE_DUALMONO)
+            audio_channel = 2-ch;
+        if (s->heavy_compression && s->compression_exists[audio_channel])
+            gain = s->heavy_dynamic_range[audio_channel];
+        else
+            gain = s->dynamic_range[audio_channel];
+
 #if USE_FIXED
         scale_coefs(s->transform_coeffs[ch], s->fixed_coeffs[ch], gain, 256);
 #else
+        if (s->target_level != 0)
+          gain = gain * s->level_gain[audio_channel];
         gain *= 1.0 / 4194304.0f;
         s->fmt_conv.int32_to_float_fmul_scalar(s->transform_coeffs[ch],
                                                s->fixed_coeffs[ch], gain, 256);
index 3ac44ee..a213bc0 100644 (file)
@@ -84,6 +84,9 @@ typedef struct AC3DecodeContext {
     int bitstream_mode;                     ///< bitstream mode                         (bsmod)
     int channel_mode;                       ///< channel mode                           (acmod)
     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 preferred_downmix;                  ///< Preferred 2-channel downmix mode       (dmixmod)
     int center_mix_level;                   ///< Center mix level index
@@ -103,6 +106,8 @@ typedef struct AC3DecodeContext {
     float ltrt_surround_mix_level;
     float loro_center_mix_level;
     float loro_surround_mix_level;
+    int target_level;                       ///< target level in dBFS
+    float level_gain[2];
 
 ///@name Frame syntax parameters
     int snr_offset_strategy;                ///< SNR offset strategy                    (snroffststr)
@@ -161,6 +166,8 @@ typedef struct AC3DecodeContext {
 ///@name Dynamic range
     INTFLOAT dynamic_range[2];                 ///< dynamic range
     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
index c6cbeb9..f36e7b0 100644 (file)
@@ -81,40 +81,69 @@ static void scale_coefs (
     int temp, temp1, temp2, temp3, temp4, temp5, temp6, temp7;
 
     mul = (dynrng & 0x1f) + 0x20;
-    shift = 4 - ((dynrng << 24) >> 29);
-    round = 1 << (shift-1);
-    for (i=0; i<len; i+=8) {
-
-        temp = src[i] * mul;
-        temp1 = src[i+1] * mul;
-        temp = temp + round;
-        temp2 = src[i+2] * mul;
-
-        temp1 = temp1 + round;
-        dst[i] = temp >> shift;
-        temp3 = src[i+3] * mul;
-        temp2 = temp2 + round;
-
-        dst[i+1] = temp1 >> shift;
-        temp4 = src[i + 4] * mul;
-        temp3 = temp3 + round;
-        dst[i+2] = temp2 >> shift;
-
-        temp5 = src[i+5] * mul;
-        temp4 = temp4 + round;
-        dst[i+3] = temp3 >> shift;
-        temp6 = src[i+6] * mul;
-
-        dst[i+4] = temp4 >> shift;
-        temp5 = temp5 + round;
-        temp7 = src[i+7] * mul;
-        temp6 = temp6 + round;
-
-        dst[i+5] = temp5 >> shift;
-        temp7 = temp7 + round;
-        dst[i+6] = temp6 >> shift;
-        dst[i+7] = temp7 >> shift;
-
+    shift = 4 - ((dynrng << 23) >> 28);
+    if (shift > 0 ) {
+      round = 1 << (shift-1);
+      for (i=0; i<len; i+=8) {
+
+          temp = src[i] * mul;
+          temp1 = src[i+1] * mul;
+          temp = temp + round;
+          temp2 = src[i+2] * mul;
+
+          temp1 = temp1 + round;
+          dst[i] = temp >> shift;
+          temp3 = src[i+3] * mul;
+          temp2 = temp2 + round;
+
+          dst[i+1] = temp1 >> shift;
+          temp4 = src[i + 4] * mul;
+          temp3 = temp3 + round;
+          dst[i+2] = temp2 >> shift;
+
+          temp5 = src[i+5] * mul;
+          temp4 = temp4 + round;
+          dst[i+3] = temp3 >> shift;
+          temp6 = src[i+6] * mul;
+
+          dst[i+4] = temp4 >> shift;
+          temp5 = temp5 + round;
+          temp7 = src[i+7] * mul;
+          temp6 = temp6 + round;
+
+          dst[i+5] = temp5 >> shift;
+          temp7 = temp7 + round;
+          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"
 
 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},
 };
 
index 7108921..e7fc5cb 100644 (file)
@@ -32,6 +32,8 @@
 
 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 },
+    { "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"},
 {"ltrt_cmixlev",   "Lt/Rt Center Mix Level",   OFFSET(ltrt_center_mix_level),    AV_OPT_TYPE_FLOAT, {.dbl = -1.0 }, -1.0, 2.0, 0},