Files
ableton-osc/AbletonOSC/manager.py
T

145 lines
4.4 KiB
Python
Raw Normal View History

"""
Main ControlSurface entry point for Ableton Live.
Manages OSC server lifecycle and all handler registrations.
"""
import logging
import os
import sys
try:
from ableton.v2.control_surface import ControlSurface
except ImportError:
from contextlib import contextmanager
class ControlSurface: # type: ignore
def __init__(self, c_instance=None):
self._c_instance = c_instance
def song(self):
return None
def application(self):
return None
def disconnect(self):
pass
def update_display(self):
pass
def log_message(self, msg):
print(msg)
def show_message(self, msg):
print(msg)
def schedule_message(self, delay, callback):
pass
@contextmanager
def component_guard(self):
yield
from .osc_server import OSCServer
from .song import SongHandler
from .track import TrackHandler
from .return_track import ReturnTrackHandler
from .clip import ClipHandler
from .clip_slot import ClipSlotHandler
from .device import DeviceHandler
from .scene import SceneHandler
from .view import ViewHandler
from .application import ApplicationHandler
from .browser import BrowserHandler
from .groove import GrooveHandler
logger = logging.getLogger(__name__)
LISTEN_PORT = int(os.environ.get("ABLETON_OSC_LISTEN_PORT", 11000))
SEND_PORT = int(os.environ.get("ABLETON_OSC_SEND_PORT", 11001))
class Manager(ControlSurface):
def __init__(self, c_instance=None):
super().__init__(c_instance)
self._setup_logging()
self._handlers = []
try:
self.osc_server = OSCServer(listen_port=LISTEN_PORT, send_port=SEND_PORT)
self.osc_server.start()
self._setup_handlers()
self.show_message("AbletonOSC: Listening on port %d" % LISTEN_PORT)
logger.info("AbletonOSC started (listen=%d, send=%d)", LISTEN_PORT, SEND_PORT)
self.osc_server.send("/live/startup", ())
except OSError as e:
self.show_message("AbletonOSC: Couldn't bind to port %d (%s)" % (LISTEN_PORT, e))
logger.error("Couldn't bind to port %d: %s", LISTEN_PORT, e)
# ------------------------------------------------------------------
def _setup_logging(self) -> None:
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
)
def _setup_handlers(self) -> None:
handler_classes = [
ApplicationHandler,
SongHandler,
TrackHandler,
ReturnTrackHandler,
ClipHandler,
ClipSlotHandler,
DeviceHandler,
SceneHandler,
ViewHandler,
BrowserHandler,
GrooveHandler,
]
with self.component_guard():
for cls in handler_classes:
h = cls(self)
h.init_api()
self._handlers.append(h)
# Control endpoints
self.osc_server.add_handler("/live/test", self._handle_test)
self.osc_server.add_handler("/live/api/reload", self._handle_reload)
self.osc_server.add_handler("/live/api/show_message", self._handle_show_message)
# ------------------------------------------------------------------
# ControlSurface interface
# ------------------------------------------------------------------
def update_display(self) -> None:
"""Called by Live ~100ms; drives the OSC poll loop."""
self.osc_server.process()
def disconnect(self) -> None:
for h in self._handlers:
h.clear_listeners()
self.osc_server.send("/live/shutdown", ())
self.osc_server.stop()
super().disconnect()
logger.info("AbletonOSC disconnected")
# ------------------------------------------------------------------
# Built-in handlers
# ------------------------------------------------------------------
def _handle_test(self, params: tuple):
return ("AbletonOSC active",)
def _handle_reload(self, params: tuple):
# Re-init all handlers (clears listeners and re-registers)
for h in self._handlers:
h.clear_listeners()
h.init_api()
return ("reloaded",)
def _handle_show_message(self, params: tuple):
if params:
self.log_message(str(params[0]))
return None