aad042650e
Covers all major Live API objects with get/set/listen/method handlers: song, track, return_track, clip, clip_slot, device (incl. rack chains and drum pads), scene, view, application, browser, groove. Zero external runtime dependencies — OSC encoded/decoded in osc_server.py. Wildcard * support for track/scene indices. Listener callbacks fire to matching /get/ addresses for bidirectional state sync. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
166 lines
6.4 KiB
Python
166 lines
6.4 KiB
Python
"""Handles /live/return_track/* OSC addresses (mirrors TrackHandler for return tracks)."""
|
|
import logging
|
|
from typing import Any, List, Optional, Tuple
|
|
from .handler import AbletonOSCHandler
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
PROPS_RW = ["name", "mute", "solo", "color", "color_index"]
|
|
PROPS_RO = ["output_meter_level", "output_meter_left", "output_meter_right"]
|
|
|
|
|
|
class ReturnTrackHandler(AbletonOSCHandler):
|
|
def init_api(self) -> None:
|
|
self.clear_listeners()
|
|
|
|
for prop in PROPS_RW:
|
|
self._add(f"/live/return_track/get/{prop}", self._make_getter(prop))
|
|
self._add(f"/live/return_track/set/{prop}", self._make_setter(prop))
|
|
self._add(f"/live/return_track/start_listen/{prop}",
|
|
self._make_start_listen(prop))
|
|
self._add(f"/live/return_track/stop_listen/{prop}",
|
|
self._make_stop_listen(prop))
|
|
|
|
for prop in PROPS_RO:
|
|
self._add(f"/live/return_track/get/{prop}", self._make_getter(prop))
|
|
self._add(f"/live/return_track/start_listen/{prop}",
|
|
self._make_start_listen(prop))
|
|
self._add(f"/live/return_track/stop_listen/{prop}",
|
|
self._make_stop_listen(prop))
|
|
|
|
self._add("/live/return_track/get/volume", self._get_volume)
|
|
self._add("/live/return_track/set/volume", self._set_volume)
|
|
self._add("/live/return_track/get/panning", self._get_panning)
|
|
self._add("/live/return_track/set/panning", self._set_panning)
|
|
self._add("/live/return_track/get/send", self._get_send)
|
|
self._add("/live/return_track/set/send", self._set_send)
|
|
self._add("/live/return_track/get/num_devices", self._get_num_devices)
|
|
self._add("/live/return_track/get/devices/name", self._get_devices_name)
|
|
|
|
# ------------------------------------------------------------------
|
|
|
|
def _get_return_tracks(self, params) -> List[Tuple[int, Any]]:
|
|
tracks = list(self.song.return_tracks)
|
|
if not params or params[0] == "*":
|
|
return list(enumerate(tracks))
|
|
idx = int(params[0])
|
|
return [(idx, tracks[idx])] if 0 <= idx < len(tracks) else []
|
|
|
|
def _get_return_track(self, params) -> Optional[Any]:
|
|
tracks = list(self.song.return_tracks)
|
|
if not params:
|
|
return None
|
|
idx = int(params[0])
|
|
return tracks[idx] if 0 <= idx < len(tracks) else None
|
|
|
|
def _make_getter(self, prop: str):
|
|
def handler(params: tuple) -> Optional[tuple]:
|
|
result = []
|
|
for idx, track in self._get_return_tracks(params):
|
|
val = self._get_prop(track, prop)
|
|
if val is not None:
|
|
result += [idx, val]
|
|
return tuple(result) if result else None
|
|
return handler
|
|
|
|
def _make_setter(self, prop: str):
|
|
def handler(params: tuple) -> None:
|
|
if len(params) < 2:
|
|
return None
|
|
track = self._get_return_track(params)
|
|
if track:
|
|
self._set_prop(track, prop, params[1])
|
|
return None
|
|
return handler
|
|
|
|
def _make_start_listen(self, prop: str):
|
|
def handler(params: tuple) -> None:
|
|
for idx, track in self._get_return_tracks(params):
|
|
self._register_listener(
|
|
f"return_track.{idx}.{prop}", track, prop,
|
|
f"/live/return_track/get/{prop}", (idx,)
|
|
)
|
|
return handler
|
|
|
|
def _make_stop_listen(self, prop: str):
|
|
def handler(params: tuple) -> None:
|
|
for idx, _ in self._get_return_tracks(params):
|
|
self._remove_listener(f"return_track.{idx}.{prop}")
|
|
return handler
|
|
|
|
def _get_volume(self, params: tuple) -> Optional[tuple]:
|
|
result = []
|
|
for idx, track in self._get_return_tracks(params):
|
|
try:
|
|
result += [idx, track.mixer_device.volume.value]
|
|
except Exception:
|
|
pass
|
|
return tuple(result) if result else None
|
|
|
|
def _set_volume(self, params: tuple) -> None:
|
|
if len(params) < 2:
|
|
return None
|
|
track = self._get_return_track(params)
|
|
if track:
|
|
try:
|
|
track.mixer_device.volume.value = float(params[1])
|
|
except Exception as e:
|
|
logger.warning("return_track set volume: %s", e)
|
|
return None
|
|
|
|
def _get_panning(self, params: tuple) -> Optional[tuple]:
|
|
result = []
|
|
for idx, track in self._get_return_tracks(params):
|
|
try:
|
|
result += [idx, track.mixer_device.panning.value]
|
|
except Exception:
|
|
pass
|
|
return tuple(result) if result else None
|
|
|
|
def _set_panning(self, params: tuple) -> None:
|
|
if len(params) < 2:
|
|
return None
|
|
track = self._get_return_track(params)
|
|
if track:
|
|
try:
|
|
track.mixer_device.panning.value = float(params[1])
|
|
except Exception as e:
|
|
logger.warning("return_track set panning: %s", e)
|
|
return None
|
|
|
|
def _get_send(self, params: tuple) -> Optional[tuple]:
|
|
if len(params) < 2:
|
|
return None
|
|
track = self._get_return_track(params)
|
|
send_idx = int(params[1])
|
|
if track:
|
|
try:
|
|
sends = list(track.mixer_device.sends)
|
|
if 0 <= send_idx < len(sends):
|
|
return (int(params[0]), send_idx, sends[send_idx].value)
|
|
except Exception as e:
|
|
logger.warning("return_track get send: %s", e)
|
|
return None
|
|
|
|
def _set_send(self, params: tuple) -> None:
|
|
if len(params) < 3:
|
|
return None
|
|
track = self._get_return_track(params)
|
|
send_idx = int(params[1])
|
|
if track:
|
|
try:
|
|
sends = list(track.mixer_device.sends)
|
|
if 0 <= send_idx < len(sends):
|
|
sends[send_idx].value = float(params[2])
|
|
except Exception as e:
|
|
logger.warning("return_track set send: %s", e)
|
|
return None
|
|
|
|
def _get_num_devices(self, params: tuple) -> Optional[tuple]:
|
|
track = self._get_return_track(params)
|
|
return (len(track.devices),) if track else None
|
|
|
|
def _get_devices_name(self, params: tuple) -> Optional[tuple]:
|
|
track = self._get_return_track(params)
|
|
return tuple(d.name for d in track.devices) if track else None
|