"""Cross-platform asynchronous sound playback for notification alerts. Uses just_playback (backed by miniaudio) for MP3/WAV/OGG/FLAC playback with native volume control on all platforms. A new notification sound cancels any currently playing sound to prevent overlap. """ import threading from pathlib import Path from wled_controller.utils import get_logger logger = get_logger(__name__) _lock = threading.Lock() _playback = None # lazy-init on first use def _get_playback(): """Lazy-init the Playback singleton (import is heavy, defer until needed).""" global _playback if _playback is None: from just_playback import Playback _playback = Playback() return _playback def play_sound_async(file_path: Path, volume: float = 1.0) -> None: """Play a sound file (non-blocking). Supports WAV, MP3, OGG, FLAC. A new call stops any currently playing sound. just_playback.play() is inherently non-blocking (miniaudio backend thread). Args: file_path: Path to the sound file. volume: Volume level 0.0-1.0. """ if not file_path.exists(): logger.warning(f"Sound file not found: {file_path}") return volume = max(0.0, min(1.0, volume)) with _lock: try: pb = _get_playback() if pb.active: pb.stop() pb.load_file(str(file_path)) pb.set_volume(volume) pb.play() except Exception as e: logger.error(f"Sound playback failed: {e}") def stop_current_sound() -> None: """Stop any currently playing notification sound.""" with _lock: if _playback is not None and _playback.active: try: _playback.stop() except Exception as e: logger.debug("Failed to stop playback: %s", e)