"""Handles /live/scene/* OSC addresses.""" import logging from typing import Any, Optional from .handler import AbletonOSCHandler logger = logging.getLogger(__name__) SCENE_PROPS_RW = [ "name", "color", "color_index", "tempo", "tempo_enabled", "time_signature_numerator", "time_signature_denominator", "time_signature_enabled", ] SCENE_PROPS_RO = ["is_empty", "is_triggered"] class SceneHandler(AbletonOSCHandler): def init_api(self) -> None: self.clear_listeners() for prop in SCENE_PROPS_RW: self._add(f"/live/scene/get/{prop}", self._make_getter(prop)) self._add(f"/live/scene/set/{prop}", self._make_setter(prop)) self._add(f"/live/scene/start_listen/{prop}", self._make_start_listen(prop)) self._add(f"/live/scene/stop_listen/{prop}", self._make_stop_listen(prop)) for prop in SCENE_PROPS_RO: self._add(f"/live/scene/get/{prop}", self._make_getter(prop)) self._add(f"/live/scene/start_listen/{prop}", self._make_start_listen(prop)) self._add(f"/live/scene/stop_listen/{prop}", self._make_stop_listen(prop)) self._add("/live/scene/fire", self._fire) self._add("/live/scene/fire_as_selected", self._fire_as_selected) self._add("/live/scene/get/num_clips", self._get_num_clips) self._add("/live/scene/get/clip_slots/has_clip", self._get_has_clips) # ------------------------------------------------------------------ def _get_scene(self, params: tuple) -> Optional[Any]: if not params: return None idx = int(params[0]) scenes = list(self.song.scenes) return scenes[idx] if 0 <= idx < len(scenes) else None def _make_getter(self, prop: str): def handler(params: tuple) -> Optional[tuple]: scene = self._get_scene(params) if scene is None: return None val = self._get_prop(scene, prop) return (int(params[0]), val) if val is not None else None return handler def _make_setter(self, prop: str): def handler(params: tuple) -> None: if len(params) < 2: return None scene = self._get_scene(params) if scene: self._set_prop(scene, prop, params[1]) return None return handler def _make_start_listen(self, prop: str): def handler(params: tuple) -> None: scene = self._get_scene(params) if scene: self._register_listener( f"scene.{params[0]}.{prop}", scene, prop, f"/live/scene/get/{prop}", (int(params[0]),) ) return handler def _make_stop_listen(self, prop: str): def handler(params: tuple) -> None: if params: self._remove_listener(f"scene.{params[0]}.{prop}") return handler def _fire(self, params: tuple) -> None: scene = self._get_scene(params) if scene: try: scene.fire() except Exception as e: logger.warning("scene.fire: %s", e) return None def _fire_as_selected(self, params: tuple) -> None: scene = self._get_scene(params) if scene: try: scene.fire_as_selected() except Exception as e: logger.warning("scene.fire_as_selected: %s", e) return None def _get_num_clips(self, params: tuple) -> Optional[tuple]: scene = self._get_scene(params) if scene is None: return None count = sum(1 for slot in scene.clip_slots if slot.has_clip) return (int(params[0]), count) def _get_has_clips(self, params: tuple) -> Optional[tuple]: scene = self._get_scene(params) if scene is None: return None return (int(params[0]),) + tuple(bool(slot.has_clip) for slot in scene.clip_slots)