Files
valknar b7cec9d24b Rename package directory AbletonOSC/ -> ableton_osc/
Python package naming convention uses snake_case. Update the import in
the root __init__.py and the setuptools include pattern in pyproject.toml.
Internal relative imports within the package are unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 13:46:34 +02:00

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