if queue_track.streamdetails.duration
else crossfade_buffer_duration,
)
+ # Ensure crossfade buffer size is aligned to frame boundaries
+ # Frame size = bytes_per_sample * channels
+ bytes_per_sample = pcm_format.bit_depth // 8
+ frame_size = bytes_per_sample * pcm_format.channels
crossfade_buffer_size = int(pcm_format.pcm_sample_size * crossfade_buffer_duration)
+ # Round down to nearest frame boundary
+ crossfade_buffer_size = (crossfade_buffer_size // frame_size) * frame_size
bytes_written = 0
buffer = b""
if streamdetails.duration
else crossfade_buffer_duration,
)
+ # Ensure crossfade buffer size is aligned to frame boundaries
+ # Frame size = bytes_per_sample * channels
+ bytes_per_sample = pcm_format.bit_depth // 8
+ frame_size = bytes_per_sample * pcm_format.channels
crossfade_buffer_size = int(pcm_format.pcm_sample_size * crossfade_buffer_duration)
+ # Round down to nearest frame boundary
+ crossfade_buffer_size = (crossfade_buffer_size // frame_size) * frame_size
fade_out_data: bytes | None = None
if crossfade_data:
# Convert PCM bytes to numpy array and then to mono for analysis
audio_array = np.frombuffer(audio_data, dtype=np.float32)
if pcm_format.channels > 1:
+ # Ensure array size is divisible by channel count
+ samples_per_channel = len(audio_array) // pcm_format.channels
+ valid_samples = samples_per_channel * pcm_format.channels
+ if valid_samples != len(audio_array):
+ self.logger.warning(
+ "Audio buffer size (%d) not divisible by channels (%d), "
+ "truncating %d samples",
+ len(audio_array),
+ pcm_format.channels,
+ len(audio_array) - valid_samples,
+ )
+ audio_array = audio_array[:valid_samples]
+
# Reshape to separate channels and take average for mono conversion
audio_array = audio_array.reshape(-1, pcm_format.channels)
mono_audio = np.asarray(np.mean(audio_array, axis=1, dtype=np.float32))
# Single channel - ensure consistent array type
mono_audio = np.asarray(audio_array, dtype=np.float32)
+ # Validate that the audio is finite (no NaN or Inf values)
+ if not np.all(np.isfinite(mono_audio)):
+ self.logger.error(
+ "Audio buffer contains non-finite values (NaN/Inf) for %s, cannot analyze",
+ stream_details_name,
+ )
+ return None
+
analysis = await self._analyze_track_beats(mono_audio, fragment, pcm_format.sample_rate)
total_time = time.perf_counter() - start_time