"""Resolve the orthogonal Visual × Audio controls into render outputs (audio spec §4/§6). The single source of truth for: whether the projector shows video, and which audio url plays for a given (audio source, current altitude). Replaces player/content.py's 7-way bundled table. White-noise is a global, altitude-independent bed; soundtrack follows the single Altitude dial; off is silence. `music` is a reserved, deferred source — not in v1.""" from __future__ import annotations from dataclasses import dataclass VISUAL_POSITIONS = frozenset({"on", "off"}) # v1 audio sources. "music" is reserved/deferred (no assets) and intentionally absent. AUDIO_SOURCES = frozenset({"off", "soundtrack", "white_noise"}) @dataclass(frozen=True) class AudioResolution: source: str url: str | None altitude_coupled: bool def resolve_visual(position: str) -> bool: """Visual on/off → whether the renderer shows video (vs fade-to-black).""" if position not in VISUAL_POSITIONS: raise ValueError( f"unknown visual position {position!r}; expected one of {sorted(VISUAL_POSITIONS)}" ) return position == "on" def resolve_audio_url(source: str, *, scale_audio: str, noise_url: str) -> str | None: """The audio url for (source, current altitude): soundtrack → the current scale's asset (or None if none authored); white_noise → the global bed; off → None. `scale_audio` is the ring scale's `audio` path (relative to /media/audio/).""" if source not in AUDIO_SOURCES: raise ValueError( f"unknown audio source {source!r}; expected one of {sorted(AUDIO_SOURCES)}" ) if source == "off": return None if source == "white_noise": return noise_url # soundtrack — couple to the current altitude's asset return f"/media/audio/{scale_audio}" if scale_audio else None def resolve_audio(source: str, *, scale_audio: str, noise_url: str) -> AudioResolution: """The full render.audio view: source, resolved url, and whether it follows the Altitude dial (true only for soundtrack).""" url = resolve_audio_url(source, scale_audio=scale_audio, noise_url=noise_url) return AudioResolution(source=source, url=url, altitude_coupled=(source == "soundtrack"))