avcodec/ac3enc: preserve strongly out-of-phase coupling bands

the encoder always formed the stereo coupling channel as L+R and disabled phsflginu, which could cancel strongly out-of-phase bands before quantization. use an L-R carrier and signal the phase flag only when difference energy dominates in every coupling block, while quiet and ambiguous bands retain the existing L+R path. this reduces the multitone fate stddev from about 1158 to 588.

Signed-off-by: Ayoub Nabil Boubagrat <237098474+ayoubnabil@users.noreply.github.com>
This commit is contained in:
Ayoub Nabil Boubagrat authored and James Almer committed 2026-08-26 15:53:44 +00:00
1 parent 56c3d963ec
commit fea2db1be1
4 files changed
+120 -4

No files matched your search

+15 -1
View File
@@ -1183,16 +1183,22 @@ static void count_frame_bits(AC3EncodeContext *s)
/* coupling coordinates */
if (block->cpl_in_use) {
int cpl_coords_exist = 0;
for (ch = 1; ch <= s->fbw_channels; ch++) {
if (block->channel_in_cpl[ch]) {
if (!s->eac3 || block->new_cpl_coords[ch] != 2)
frame_bits++;
if (block->new_cpl_coords[ch]) {
cpl_coords_exist = 1;
frame_bits += 2;
frame_bits += (4 + 4) * s->num_cpl_bands;
}
}
}
if (s->channel_mode == AC3_CHMODE_STEREO &&
s->phase_flags_in_use && cpl_coords_exist)
frame_bits += s->num_cpl_bands;
}
/* stereo rematrixing */
@@ -1725,7 +1731,7 @@ static void output_audio_block(AC3EncodeContext *s, PutBitContext *pb, int blk)
put_bits(pb, 1, block->channel_in_cpl[ch]);
}
if (s->channel_mode == AC3_CHMODE_STEREO)
put_bits(pb, 1, 0); /* phase flags in use */
put_bits(pb, 1, s->phase_flags_in_use);
start_sub = (s->start_freq[CPL_CH] - 37) / 12;
end_sub = (s->cpl_end_freq - 37) / 12;
put_bits(pb, 4, start_sub);
@@ -1742,11 +1748,14 @@ static void output_audio_block(AC3EncodeContext *s, PutBitContext *pb, int blk)
/* coupling coordinates */
if (block->cpl_in_use) {
int cpl_coords_exist = 0;
for (ch = 1; ch <= s->fbw_channels; ch++) {
if (block->channel_in_cpl[ch]) {
if (!s->eac3 || block->new_cpl_coords[ch] != 2)
put_bits(pb, 1, block->new_cpl_coords[ch]);
if (block->new_cpl_coords[ch]) {
cpl_coords_exist = 1;
put_bits(pb, 2, block->cpl_master_exp[ch]);
for (bnd = 0; bnd < s->num_cpl_bands; bnd++) {
put_bits(pb, 4, block->cpl_coord_exp [ch][bnd]);
@@ -1755,6 +1764,11 @@ static void output_audio_block(AC3EncodeContext *s, PutBitContext *pb, int blk)
}
}
}
if (s->channel_mode == AC3_CHMODE_STEREO &&
s->phase_flags_in_use && cpl_coords_exist) {
for (bnd = 0; bnd < s->num_cpl_bands; bnd++)
put_bits(pb, 1, s->phase_flags[bnd]);
}
}
/* stereo rematrixing */
+2
View File
@@ -213,6 +213,8 @@ typedef struct AC3EncodeContext {
int cpl_on; ///< coupling turned on for this frame
int cpl_enabled; ///< coupling enabled for all frames
int phase_flags_in_use; ///< coupling phase flags are in use
uint8_t phase_flags[AC3_MAX_CPL_BANDS]; ///< coupling phase flags (phsflg)
int num_cpl_subbands; ///< number of coupling subbands (ncplsubnd)
int num_cpl_bands; ///< number of coupling bands (ncplbnd)
uint8_t cpl_band_sizes[AC3_MAX_CPL_BANDS]; ///< number of coeffs in each coupling band
+76 -1
View File
@@ -44,6 +44,10 @@
#define RENAME(element) element ## _fixed
#endif
/* power ratios for the -42 dB band floor and the 12 dB difference margin. */
#define PHASE_BAND_ENERGY_DENOMINATOR (1 << 14)
#define PHASE_DIFF_ENERGY_FACTOR (1 << 4)
/*
* Apply the MDCT to input samples to generate frequency coefficients.
* This applies the KBD window and normalizes the input to reduce precision
@@ -97,6 +101,7 @@ static void apply_channel_coupling(AC3EncodeContext *s)
CoefSumType energy[AC3_MAX_BLOCKS][AC3_MAX_CHANNELS][16] = {{{0}}};
int cpl_start, num_cpl_coefs;
s->phase_flags_in_use = 0;
memset(cpl_coords, 0, AC3_MAX_BLOCKS * sizeof(*cpl_coords));
#if AC3ENC_FLOAT
memset(fixed_cpl_coords, 0, AC3_MAX_BLOCKS * sizeof(*cpl_coords));
@@ -108,6 +113,59 @@ static void apply_channel_coupling(AC3EncodeContext *s)
num_cpl_coefs = FFALIGN(s->num_cpl_subbands * 12 + 1, 32);
cpl_start = FFMIN(256, cpl_start + num_cpl_coefs) - num_cpl_coefs;
if (s->channel_mode == AC3_CHMODE_STEREO) {
uint8_t phase_flags[AC3_MAX_CPL_BANDS];
int cpl_blocks = 0;
/* use a single phase strategy for the frame. a difference carrier is
* selected only when it has at least 12 dB more energy and the band
* is within 42 dB of the coded channel energy in every coupling
* block. */
memset(phase_flags, 1, s->num_cpl_bands);
for (blk = 0; blk < s->num_blocks; blk++) {
AC3Block *block = &s->blocks[blk];
CoefSumType sum[AC3_MAX_CPL_BANDS][4];
/* the DSP also returns sum and difference energy in slots 2/3. */
CoefSumType block_energy[4];
CoefSumType max_energy;
if (!block->cpl_in_use)
continue;
cpl_blocks++;
sum_square_butterfly(s, block_energy,
block->mdct_coef[1], block->mdct_coef[2],
s->start_freq[CPL_CH]);
i = s->start_freq[CPL_CH];
for (bnd = 0; bnd < s->num_cpl_bands; bnd++) {
sum_square_butterfly(s, sum[bnd],
block->mdct_coef[1] + i,
block->mdct_coef[2] + i,
s->cpl_band_sizes[bnd]);
/* reused by the coupling coordinate calculation below. */
energy[blk][1][bnd] = sum[bnd][0];
energy[blk][2][bnd] = sum[bnd][1];
block_energy[0] += sum[bnd][0];
block_energy[1] += sum[bnd][1];
i += s->cpl_band_sizes[bnd];
}
max_energy = FFMAX(block_energy[0], block_energy[1]);
for (bnd = 0; bnd < s->num_cpl_bands; bnd++) {
CoefSumType band_energy = FFMAX(sum[bnd][0], sum[bnd][1]);
int significant = band_energy >
max_energy / PHASE_BAND_ENERGY_DENOMINATOR;
phase_flags[bnd] &= significant &&
sum[bnd][3] / PHASE_DIFF_ENERGY_FACTOR > sum[bnd][2];
}
}
for (bnd = 0; bnd < s->num_cpl_bands; bnd++) {
int phase = phase_flags[bnd] && cpl_blocks;
s->phase_flags[bnd] = phase;
s->phase_flags_in_use |= phase;
}
}
/* calculate coupling channel from fbw channels */
for (blk = 0; blk < s->num_blocks; blk++) {
AC3Block *block = &s->blocks[blk];
@@ -123,6 +181,20 @@ static void apply_channel_coupling(AC3EncodeContext *s)
cpl_coef[i] += ch_coef[i];
}
if (s->channel_mode == AC3_CHMODE_STEREO) {
CoefType *left = block->mdct_coef[1];
CoefType *right = block->mdct_coef[2];
i = s->start_freq[CPL_CH];
for (bnd = 0; bnd < s->num_cpl_bands; bnd++) {
if (s->phase_flags[bnd]) {
for (j = 0; j < s->cpl_band_sizes[bnd]; j++)
block->mdct_coef[CPL_CH][i + j] = left[i + j] - right[i + j];
}
i += s->cpl_band_sizes[bnd];
}
}
/* coefficients must be clipped in order to be encoded */
clip_coefficients(&s->adsp, cpl_coef, num_cpl_coefs);
}
@@ -133,7 +205,10 @@ static void apply_channel_coupling(AC3EncodeContext *s)
i = s->start_freq[CPL_CH];
while (i < s->cpl_end_freq) {
int band_size = s->cpl_band_sizes[bnd];
for (ch = CPL_CH; ch <= s->fbw_channels; ch++) {
/* stereo channel energies were filled during phase analysis above. */
int last_ch = s->channel_mode == AC3_CHMODE_STEREO ?
CPL_CH : s->fbw_channels;
for (ch = CPL_CH; ch <= last_ch; ch++) {
for (blk = 0; blk < s->num_blocks; blk++) {
AC3Block *block = &s->blocks[blk];
if (!block->cpl_in_use || (ch > CPL_CH && !block->channel_in_cpl[ch]))
+27 -2
View File
@@ -103,6 +103,31 @@ FATE_AC3-$(call ENCDEC, AC3, MP4 MOV, WAV_MUXER WAV_DEMUXER ARESAMPLE_FILTER PCM
fate-ac3-encode: CMD = enc_dec_pcm mp4 wav s16le $(subst $(SAMPLES),$(TARGET_SAMPLES),$(REF)) -c:a ac3 -b:a 128k
fate-ac3-encode: CMP_TARGET = 404.53
# coupling must not cancel persistent out-of-phase stereo content.
AC3_PHASE_EXPR = 0.025*(sin(16000*PI*t)+sin(20000*PI*t)+sin(24000*PI*t)+sin(28000*PI*t))
tests/data/fate/ac3-phase.wav: TAG = GEN
tests/data/fate/ac3-phase.wav: ffmpeg$(PROGSSUF)$(EXESUF) | tests/data/fate
$(M)$(TARGET_EXEC) $(TARGET_PATH)/$< -nostdin -f lavfi \
-i "aevalsrc=$(AC3_PHASE_EXPR)|-$(AC3_PHASE_EXPR):s=48000:d=1" \
-c:a pcm_s16le -y $(TARGET_PATH)/$@ 2>/dev/null
AC3_PHASE_DEPS = FFMPEG LAVFI_INDEV AEVALSRC_FILTER ARESAMPLE_FILTER \
MP4_MUXER MOV_DEMUXER WAV_MUXER WAV_DEMUXER \
PCM_S16LE_ENCODER FILE_PROTOCOL PIPE_PROTOCOL
FATE_AC3_PHASE-$(call ALLYES, $(AC3_PHASE_DEPS) AC3_ENCODER AC3_DECODER) += fate-ac3-phase
FATE_AC3_PHASE-$(call ALLYES, $(AC3_PHASE_DEPS) AC3_FIXED_ENCODER AC3_DECODER) += fate-ac3-fixed-phase
FATE_AC3_PHASE-$(call ALLYES, $(AC3_PHASE_DEPS) EAC3_ENCODER EAC3_DECODER) += fate-eac3-phase
# without phase restoration the measured stddev is about 1158.
fate-ac3-phase: CMD = enc_dec_pcm mp4 wav s16le $(TARGET_PATH)/tests/data/fate/ac3-phase.wav -c:a ac3 -b:a 128k
fate-ac3-phase: CMP_TARGET = 588.4
fate-ac3-fixed-phase: CMD = enc_dec_pcm mp4 wav s16le $(TARGET_PATH)/tests/data/fate/ac3-phase.wav -c:a ac3_fixed -b:a 128k
fate-ac3-fixed-phase: CMP_TARGET = 588.4
fate-eac3-phase: CMD = enc_dec_pcm mp4 wav s16le $(TARGET_PATH)/tests/data/fate/ac3-phase.wav -c:a eac3 -b:a 128k
fate-eac3-phase: CMP_TARGET = 588.4
fate-ac3-phase fate-ac3-fixed-phase fate-eac3-phase: tests/data/fate/ac3-phase.wav
fate-ac3-phase fate-ac3-fixed-phase fate-eac3-phase: CMP = stddev
fate-ac3-phase fate-ac3-fixed-phase fate-eac3-phase: FUZZ = 2
fate-ac3-phase fate-ac3-fixed-phase fate-eac3-phase: REF = tests/data/fate/ac3-phase.wav
FATE_EAC3-$(call ENCDEC, EAC3, MP4 MOV, WAV_MUXER WAV_DEMUXER ARESAMPLE_FILTER PCM_S16LE_ENCODER PIPE_PROTOCOL) += fate-eac3-encode
fate-eac3-encode: CMD = enc_dec_pcm mp4 wav s16le $(subst $(SAMPLES),$(TARGET_SAMPLES),$(REF)) -c:a eac3 -b:a 128k
@@ -189,6 +214,6 @@ fate-eac3-core-bsf: CMP = oneline
fate-eac3-core-bsf: REF = b704bf851e99b7442e9bed368b60e6ca
FATE_SAMPLES_AVCONV += $(FATE_AC3-yes) $(FATE_EAC3-yes)
FATE_FFMPEG += $(FATE_AC3_DITHER-yes) $(FATE_AC3_FIXED_DEXP24-yes)
FATE_FFMPEG += $(FATE_AC3_DITHER-yes) $(FATE_AC3_FIXED_DEXP24-yes) $(FATE_AC3_PHASE-yes)
fate-ac3: $(FATE_AC3-yes) $(FATE_EAC3-yes) $(FATE_AC3_DITHER-yes) $(FATE_AC3_FIXED_DEXP24-yes)
fate-ac3: $(FATE_AC3-yes) $(FATE_EAC3-yes) $(FATE_AC3_DITHER-yes) $(FATE_AC3_FIXED_DEXP24-yes) $(FATE_AC3_PHASE-yes)