From 8d0d156b807742b67b0b977f7ef417d95fd00e11 Mon Sep 17 00:00:00 2001 From: Marvin Schenkel Date: Tue, 30 Sep 2025 14:09:35 +0200 Subject: [PATCH] Smart fades tweaks (#2457) --- music_assistant/helpers/smart_fades.py | 33 +++++++++++++++++--------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/music_assistant/helpers/smart_fades.py b/music_assistant/helpers/smart_fades.py index b4e5e5b2..34f41c11 100644 --- a/music_assistant/helpers/smart_fades.py +++ b/music_assistant/helpers/smart_fades.py @@ -466,6 +466,7 @@ class SmartFadesMixer: fade_out_label="[fadeout_beatalign]", fade_in_label="[fadein_beatalign]", crossfade_duration=crossfade_duration, + crossfade_bars=crossfade_bars, ) filters.extend(frequency_filters) @@ -509,16 +510,11 @@ class SmartFadesMixer: # Calculate ideal bars based on BPM compatibility. We link this to time stretching # so we avoid extreme tempo changes over short fades. - if bpm_diff_percent <= TIME_STRETCH_BPM_PERCENTAGE_THRESHOLD: - ideal_bars = 8 - elif bpm_diff_percent < 25.0: - ideal_bars = 2 - else: - ideal_bars = 1 + ideal_bars = 10 if bpm_diff_percent <= TIME_STRETCH_BPM_PERCENTAGE_THRESHOLD else 6 # We could encounter songs that have a long athmospheric intro without any downbeats # In those cases, we need to reduce the bars until it fits in the fadein buffer. - for bars in [ideal_bars, 4, 2, 1]: + for bars in [ideal_bars, 8, 6, 4, 2, 1]: if bars > ideal_bars: continue # Skip bars longer than optimal @@ -827,6 +823,7 @@ class SmartFadesMixer: fade_out_label: str, fade_in_label: str, crossfade_duration: float, + crossfade_bars: int, ) -> list[str]: """Create LP / HP complementary filters using frequency sweeps for smooth transitions.""" # Calculate target frequency based on average BPM @@ -850,14 +847,28 @@ class SmartFadesMixer: # The crossfade always happens at the END of the buffer, regardless of beat alignment fadeout_eq_start = max(0, SMART_CROSSFADE_DURATION - fadeout_eq_duration) + # For shorter fades, use exp/exp curves to avoid abruptness + if crossfade_bars < 8: + fadeout_curve = "exponential" + fadein_curve = "exponential" + # For long fades, use log/linear curves + else: + # Use logarithmic curve to give the next track more space + fadeout_curve = "logarithmic" + # Use linear curve for transition, predictable and not too abrupt + fadein_curve = "linear" + self.logger.debug( - "EQ: crossover=%dHz, EQ fadeout duration=%.1fs" - " EQ fadein duration=%.1fs, BPM=%.1f BPM ratio=%.2f", + "EQ: crossover=%dHz, EQ fadeout duration=%.1fs," + " EQ fadein duration=%.1fs, BPM=%.1f, BPM ratio=%.2f," + " EQ curves: %s/%s", crossover_freq, fadeout_eq_duration, fadein_eq_duration, avg_bpm, bpm_ratio, + fadeout_curve, + fadein_curve, ) # fadeout (unfiltered → low-pass) @@ -870,7 +881,7 @@ class SmartFadesMixer: start_time=fadeout_eq_start, sweep_direction="fade_in", poles=1, - curve_type="logarithmic", # Use logarithmic curve to give the next track more space + curve_type=fadeout_curve, ) # fadein (high-pass → unfiltered) @@ -883,7 +894,7 @@ class SmartFadesMixer: start_time=0, sweep_direction="fade_out", poles=1, - curve_type="linear", # Use linear curve for transition, predictable and not too abrupt + curve_type=fadein_curve, ) return fadeout_filters + fadein_filters -- 2.34.1