Skip to content

Commit 2acadec

Browse files
committed
yield to loop tweaks
1 parent 2b6f6a3 commit 2acadec

File tree

4 files changed

+29
-20
lines changed

4 files changed

+29
-20
lines changed

music_assistant/controllers/players/player_controller.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,6 +2135,8 @@ async def _poll_players(self) -> None:
21352135
str(err),
21362136
exc_info=err if self.logger.isEnabledFor(10) else None,
21372137
)
2138+
# Yield to event loop to prevent blocking
2139+
await asyncio.sleep(0)
21382140
await asyncio.sleep(1)
21392141

21402142
async def _handle_select_plugin_source(

music_assistant/helpers/audio.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,24 @@
8080
STREAMDETAILS_EXPIRATION: Final[int] = 60 * 15 # 15 minutes
8181

8282

83+
def align_audio_to_frame_boundary(audio_data: bytes, pcm_format: AudioFormat) -> bytes:
84+
"""Align audio data to frame boundaries by truncating incomplete frames.
85+
86+
:param audio_data: Raw PCM audio data to align.
87+
:param pcm_format: AudioFormat of the audio data.
88+
"""
89+
bytes_per_sample = pcm_format.bit_depth // 8
90+
frame_size = bytes_per_sample * pcm_format.channels
91+
valid_bytes = (len(audio_data) // frame_size) * frame_size
92+
if valid_bytes != len(audio_data):
93+
LOGGER.debug(
94+
"Truncating %d bytes from audio buffer to align to frame boundary",
95+
len(audio_data) - valid_bytes,
96+
)
97+
return audio_data[:valid_bytes]
98+
return audio_data
99+
100+
83101
async def crossfade_pcm_parts(
84102
fade_in_part: bytes,
85103
fade_out_part: bytes,
@@ -458,6 +476,8 @@ async def fill_buffer_task() -> None:
458476
):
459477
chunk_count += 1
460478
await audio_buffer.put(chunk)
479+
# Yield to event loop to prevent blocking warnings
480+
await asyncio.sleep(0)
461481
# Only set EOF if we completed successfully
462482
await audio_buffer.set_eof()
463483
except asyncio.CancelledError:
@@ -1245,11 +1265,6 @@ async def resample_pcm_audio(
12451265
)
12461266
return b""
12471267
# Ensure frame alignment after resampling
1248-
# Import inline to avoid circular dependency at module level
1249-
from music_assistant.helpers.smart_fades import ( # noqa: PLC0415
1250-
align_audio_to_frame_boundary,
1251-
)
1252-
12531268
return align_audio_to_frame_boundary(stdout, output_format)
12541269
except Exception as err:
12551270
LOGGER.exception(

music_assistant/helpers/smart_fades.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@
1919
import shortuuid
2020

2121
from music_assistant.constants import VERBOSE_LOG_LEVEL
22-
from music_assistant.helpers.audio import crossfade_pcm_parts, strip_silence
22+
from music_assistant.helpers.audio import (
23+
align_audio_to_frame_boundary,
24+
crossfade_pcm_parts,
25+
strip_silence,
26+
)
2327
from music_assistant.helpers.process import communicate
2428
from music_assistant.helpers.util import remove_file
2529
from music_assistant.models.smart_fades import (
@@ -40,20 +44,6 @@
4044
TIME_STRETCH_BPM_PERCENTAGE_THRESHOLD = 5.0
4145

4246

43-
def align_audio_to_frame_boundary(audio_data: bytes, pcm_format: AudioFormat) -> bytes:
44-
"""Align audio data to frame boundaries by truncating incomplete frames."""
45-
bytes_per_sample = pcm_format.bit_depth // 8
46-
frame_size = bytes_per_sample * pcm_format.channels
47-
valid_bytes = (len(audio_data) // frame_size) * frame_size
48-
if valid_bytes != len(audio_data):
49-
logging.getLogger(__name__).debug(
50-
"Truncating %d bytes from audio buffer to align to frame boundary",
51-
len(audio_data) - valid_bytes,
52-
)
53-
return audio_data[:valid_bytes]
54-
return audio_data
55-
56-
5747
class SmartFadesAnalyzer:
5848
"""Smart fades analyzer that performs audio analysis."""
5949

music_assistant/providers/airplay/protocols/raop.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from __future__ import annotations
44

5+
import asyncio
56
import logging
67
from typing import TYPE_CHECKING, cast
78

@@ -196,6 +197,7 @@ async def _stderr_reader(self) -> None:
196197
logger.debug("End of stream reached")
197198
break
198199
logger.log(VERBOSE_LOG_LEVEL, line)
200+
await asyncio.sleep(0) # Yield to event loop
199201

200202
# ensure we're cleaned up afterwards (this also logs the returncode)
201203
logger.debug("CLIRaop stderr reader ended")

0 commit comments

Comments
 (0)