From f4898811fb3367d595988ecd8ca5ca3fdf1c6604 Mon Sep 17 00:00:00 2001 From: Nils Schimmelmann Date: Tue, 25 Aug 2026 18:48:40 +0000 Subject: [PATCH] sbr: fix parameter resolution in VBR mode and transient normalization When running in VBR quality mode (-q), hEncoder->config.bitRate is 0, causing SbrUpdate() to default to low amplitude and coarse frequency resolutions (bs_amp_res = 0, dk = 2). Derive an effective nominal stream bitrate from quantqual when bitRate is 0 so HE-AAC uses fine resolution parameters. Also update SbrAnalyze() to normalize transientStrength using the total slot count (num_slots) rather than the decimated count (sampled), preventing transient strength scaling artifacts during decimation. --- libfaac/frame.c | 3 ++- libfaac/sbr_analysis.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/libfaac/frame.c b/libfaac/frame.c index 54b12609e..e1d377bd1 100644 --- a/libfaac/frame.c +++ b/libfaac/frame.c @@ -308,7 +308,8 @@ int faacEncApplyConfig(faacEncStruct* hEncoder, if (hEncoder->config.aacObjectType == HE_V1) { SBRContext *sCtx = hEncoder->sbrContext; - SbrContextUpdateConfig(sCtx, hEncoder->numChannels, hEncoder->config.bitRate * hEncoder->numChannels, &hEncoder->fft_tables); + unsigned long sbr_bitrate = hEncoder->config.bitRate ? (hEncoder->config.bitRate * hEncoder->numChannels) : ((unsigned long)hEncoder->config.quantqual * 1280); + SbrContextUpdateConfig(sCtx, hEncoder->numChannels, sbr_bitrate, &hEncoder->fft_tables); /* kx * Fs / (2*64): each QMF band is Fs/(2*SBR_QMF_BANDS_64) Hz wide. * Matching core bandwidth to the SBR crossover avoids a gap or overlap. */ hEncoder->config.bandWidth = SbrContextGetXOverBandwidth(sCtx); diff --git a/libfaac/sbr_analysis.c b/libfaac/sbr_analysis.c index dc4f8531e..65d621888 100644 --- a/libfaac/sbr_analysis.c +++ b/libfaac/sbr_analysis.c @@ -70,7 +70,7 @@ void SbrAnalyze(SignalAnalysis *sa, float *fullPtrs[], int nch, int numSamples, } sa->ch[ch].lastVal = val_in; - sa->ch[ch].transientStrength = smax * (float)sampled / (ssum + SBR_ENERGY_FLOOR); + sa->ch[ch].transientStrength = smax * (float)num_slots / (ssum + SBR_ENERGY_FLOOR); sa->ch[ch].transientSlot = smax_idx; /* Evaluate relative energy jumps to inform block switching. */