More crossfade tweaks
authorMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 7 Nov 2025 14:47:18 +0000 (15:47 +0100)
committerMarcel van der Veldt <m.vanderveldt@outlook.com>
Fri, 7 Nov 2025 14:47:18 +0000 (15:47 +0100)
music_assistant/controllers/streams.py

index 9f71353ed777d713269c82cbe278cb7f24c61776..f83514bcf6724c59f87ad82b448e38a641b099a6 100644 (file)
@@ -124,7 +124,8 @@ class CrossfadeData:
 
     data: bytes
     fade_in_size: int
-    pcm_format: AudioFormat
+    pcm_format: AudioFormat  # Format of the 'data' bytes (current/previous track's format)
+    fade_in_pcm_format: AudioFormat  # Format for 'fade_in_size' (next track's format)
     queue_item_id: str
 
 
@@ -1453,8 +1454,9 @@ class StreamsController(CoreController):
 
         if crossfade_data:
             # Calculate discard amount in seconds (format-independent)
+            # Use fade_in_pcm_format because fade_in_size is in the next track's original format
             fade_in_duration_seconds = (
-                crossfade_data.fade_in_size / crossfade_data.pcm_format.pcm_sample_size
+                crossfade_data.fade_in_size / crossfade_data.fade_in_pcm_format.pcm_sample_size
             )
             discard_seconds = int(fade_in_duration_seconds) - 1
             # Calculate discard amounts in CURRENT track's format
@@ -1620,7 +1622,7 @@ class StreamsController(CoreController):
                     # edge case: pcm format mismatch, we need to resample the next track's
                     # beginning part before crossfading
                     self.logger.debug(
-                        "Resampling next track from %s to %s for queue %s",
+                        "Resampling next track's crossfade from %s to %s for queue %s",
                         next_queue_item_pcm_format.sample_rate,
                         pcm_format.sample_rate,
                         queue.display_name,
@@ -1662,15 +1664,17 @@ class StreamsController(CoreController):
                     for _chunk in divide_chunks(crossfade_first, pcm_format.pcm_sample_size):
                         yield _chunk
                     # store the other half for the next track
-                    # IMPORTANT: Use original buffer size (in next track's format) for fade_in_size
-                    # because the next track will stream in its native format and needs to know
-                    # how many bytes to discard in that format.
-                    # However, crossfade_second data is in current track's format (pcm_format)
+                    # IMPORTANT: crossfade_second data is in CURRENT track's format (pcm_format)
                     # because it was created from the resampled buffer used for mixing.
+                    # BUT fade_in_size represents bytes in NEXT track's original format
+                    # (next_queue_item_pcm_format) because that's how much of the next track
+                    # was consumed during the crossfade. We need both formats to correctly
+                    # handle the crossfade data when the next track starts.
                     self._crossfade_data[queue_item.queue_id] = CrossfadeData(
                         data=crossfade_second,
                         fade_in_size=original_buffer_size,
-                        pcm_format=pcm_format,
+                        pcm_format=pcm_format,  # Format of the data (current track)
+                        fade_in_pcm_format=next_queue_item_pcm_format,  # Format for fade_in_size
                         queue_item_id=next_queue_item.queue_item_id,
                     )
                 except Exception as err: