This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit 06e949e45de3dd8f8680e7a290a0e947808f8bd2
Author:     Lynne <[email protected]>
AuthorDate: Tue Jun 30 16:23:10 2026 +0900
Commit:     Lynne <[email protected]>
CommitDate: Wed Jul 1 21:05:27 2026 +0900

    aacpsy: pre-echo-aware short-block detection for isolated onsets
    
    The LAME attack detector high-pass filters at fs/4 before measuring attack
    intensity, so the gentler attacks of tonal material (piano, plucked strings)
    fall below the threshold and stay in long blocks, smearing pre-echo across 
the
    quiet gap before the note. Relax the attack threshold for an isolated onset,
    preceded by PSY_LAME_PE_GAP long frames with a near-silent pre-onset 
sub-block
    (< PSY_LAME_PE_QUIET of the frame peak), so it switches to short blocks.
---
 libavcodec/aacpsy.c | 31 ++++++++++++++++++++++++++-----
 1 file changed, 26 insertions(+), 5 deletions(-)

diff --git a/libavcodec/aacpsy.c b/libavcodec/aacpsy.c
index 78c217f8f4..e38c43a323 100644
--- a/libavcodec/aacpsy.c
+++ b/libavcodec/aacpsy.c
@@ -99,6 +99,14 @@ enum {
 #define AAC_NUM_BLOCKS_SHORT 8      ///< number of blocks in a short sequence
 #define PSY_LAME_NUM_SUBBLOCKS 2    ///< Number of sub-blocks in each short 
block
 
+/* Pre-echo-aware attack detection: the LAME ratio test misses gentler attacks 
after a quiet
+ * gap, which then stay long and pre-echo. For an isolated onset (long for 
PSY_LAME_PE_GAP
+ * frames) whose pre-onset is below PSY_LAME_PE_QUIET of the frame peak, scale 
the threshold by
+ * PSY_LAME_PE_RED so it switches short; dense-transient content never 
qualifies. */
+#define PSY_LAME_PE_GAP    12       ///< min consecutive long frames before 
the relaxation applies
+#define PSY_LAME_PE_QUIET  0.4f     ///< pre-onset must be below this fraction 
of the frame peak
+#define PSY_LAME_PE_RED    0.45f    ///< attack-threshold multiplier for a 
qualifying isolated onset
+
 /**
  * @}
  */
@@ -134,6 +142,7 @@ typedef struct AacPsyChannel{
     float prev_energy_subshort[AAC_NUM_BLOCKS_SHORT * PSY_LAME_NUM_SUBBLOCKS];
     int   prev_attack;                   ///< attack value for the last short 
block in the previous sequence
     int   next_attack0_zero;          ///< whether attack[0] of the next frame 
is zero
+    int   frames_since_short;            ///< consecutive long frames 
(pre-echo-aware isolated-onset gate)
 
     /* rate-loop re-analysis rewind state, see psy_3gpp_analyze() */
     int64_t    rc_frame_num;             ///< frame this channel last saved 
rewind state for
@@ -966,11 +975,21 @@ static FFPsyWindowInfo psy_lame_window(FFPsyContext *ctx, 
const float *audio,
             attack_intensity[i + PSY_LAME_NUM_SUBBLOCKS] = p;
         }
 
-        /* compare energy between sub-short blocks */
-        for (i = 0; i < (AAC_NUM_BLOCKS_SHORT + 1) * PSY_LAME_NUM_SUBBLOCKS; 
i++)
-            if (!attacks[i / PSY_LAME_NUM_SUBBLOCKS])
-                if (attack_intensity[i] > pch->attack_threshold)
-                    attacks[i / PSY_LAME_NUM_SUBBLOCKS] = (i % 
PSY_LAME_NUM_SUBBLOCKS) + 1;
+        {   /* pre-echo-aware threshold relaxation, see PSY_LAME_PE_* */
+            float frame_peak = 1.0f;
+            for (i = PSY_LAME_NUM_SUBBLOCKS; i < (AAC_NUM_BLOCKS_SHORT + 1) * 
PSY_LAME_NUM_SUBBLOCKS; i++)
+                frame_peak = FFMAX(frame_peak, energy_subshort[i]);
+            for (i = 0; i < (AAC_NUM_BLOCKS_SHORT + 1) * 
PSY_LAME_NUM_SUBBLOCKS; i++)
+                if (!attacks[i / PSY_LAME_NUM_SUBBLOCKS]) {
+                    float thr = pch->attack_threshold;
+                    if (i >= PSY_LAME_NUM_SUBBLOCKS &&
+                        pch->frames_since_short >= PSY_LAME_PE_GAP &&
+                        energy_subshort[i - PSY_LAME_NUM_SUBBLOCKS] < 
PSY_LAME_PE_QUIET * frame_peak)
+                        thr *= PSY_LAME_PE_RED;
+                    if (attack_intensity[i] > thr)
+                        attacks[i / PSY_LAME_NUM_SUBBLOCKS] = (i % 
PSY_LAME_NUM_SUBBLOCKS) + 1;
+                }
+        }
 
         /* should have energy change between short blocks, in order to avoid 
periodic signals */
         /* Good samples to show the effect are Trumpet test songs */
@@ -1008,6 +1027,8 @@ static FFPsyWindowInfo psy_lame_window(FFPsyContext *ctx, 
const float *audio,
                 if (attacks[i] && attacks[i-1])
                     attacks[i] = 0;
         }
+
+        pch->frames_since_short = uselongblock ? pch->frames_since_short + 1 : 
0;
     } else {
         /* We have no lookahead info, so just use same type as the previous 
sequence. */
         uselongblock = !(prev_type == EIGHT_SHORT_SEQUENCE);

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to