From d376b00c6fd32c92b37500580e7e87fe01833c78 Mon Sep 17 00:00:00 2001 From: PhotonicVelocity Date: Wed, 28 Jan 2026 19:19:05 -0800 Subject: [PATCH] add support for arrangement_clip --- README.md | 10 ++++++++-- abletonosc/clip.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 083bb8d..f855ab2 100644 --- a/README.md +++ b/README.md @@ -345,7 +345,7 @@ A Clip Slot represents a container for a clip. It is used to create and delete c ## Clip API -Represents an audio or MIDI clip. Can be used to start/stop clips, and query/modify their notes, name, gain, pitch, color, playing state/position, etc. +Represents an audio or MIDI clip in session view. Can be used to start/stop clips, and query/modify their notes, name, gain, pitch, color, playing state/position, etc.
Documentation: Clip API @@ -415,6 +415,13 @@ Represents an audio or MIDI clip. Can be used to start/stop clips, and query/mod --- +## Arrangement Clip API + +Represents an audio or MIDI clip in Arrangement view. Endpoints mirror `/live/clip/*` but use the +`/live/arrangement_clip/*` namespace and index into `track.arrangement_clips`. + +--- + ## Scene API Represents a scene, used to trigger a row of clips simultaneously. A scene's name, color, tempo and time signature can all be set and queried. @@ -557,4 +564,3 @@ For code contributions and feedback, many thanks to: - Mark Marijnissen ([markmarijnissen](https://github.com/markmarijnissen)) - [capturcus](https://github.com/capturcus) - Esa Ruoho a.k.a. Lackluster ([esaruoho](https://github.com/esaruoho)) - diff --git a/abletonosc/clip.py b/abletonosc/clip.py index ce29fa0..fff52f8 100644 --- a/abletonosc/clip.py +++ b/abletonosc/clip.py @@ -63,6 +63,25 @@ def clip_callback(params: Tuple[Any]) -> Tuple: return clip_callback + def create_arrangement_clip_callback(func, *args, pass_clip_index=False): + """ + Creates a callback that expects: (track_index, arrangement_clip_index, *args) + and targets track.arrangement_clips[clip_index]. + """ + def clip_callback(params: Tuple[Any]) -> Tuple: + track_index, clip_index = int(params[0]), int(params[1]) + track = self.song.tracks[track_index] + clip = track.arrangement_clips[clip_index] + if pass_clip_index: + rv = func(clip, *args, tuple(params[0:])) + else: + rv = func(clip, *args, tuple(params[2:])) + + if rv is not None: + return (track_index, clip_index, *rv) + + return clip_callback + methods = [ "fire", "stop", @@ -117,6 +136,8 @@ def clip_callback(params: Tuple[Any]) -> Tuple: for method in methods: self.osc_server.add_handler("/live/clip/%s" % method, create_clip_callback(self._call_method, method)) + self.osc_server.add_handler("/live/arrangement_clip/%s" % method, + create_clip_callback(self._call_method, method)) for prop in properties_r + properties_rw: self.osc_server.add_handler("/live/clip/get/%s" % prop, @@ -125,9 +146,18 @@ def clip_callback(params: Tuple[Any]) -> Tuple: create_clip_callback(self._start_listen, prop, pass_clip_index=True)) self.osc_server.add_handler("/live/clip/stop_listen/%s" % prop, create_clip_callback(self._stop_listen, prop, pass_clip_index=True)) + self.osc_server.add_handler("/live/arrangement_clip/get/%s" % prop, + create_arrangement_clip_callback(self._get_property, prop)) + self.osc_server.add_handler("/live/arrangement_clip/start_listen/%s" % prop, + create_arrangement_clip_callback(self._start_listen, prop, pass_clip_index=True)) + self.osc_server.add_handler("/live/arrangement_clip/stop_listen/%s" % prop, + create_arrangement_clip_callback(self._stop_listen, prop, pass_clip_index=True)) + for prop in properties_rw: self.osc_server.add_handler("/live/clip/set/%s" % prop, create_clip_callback(self._set_property, prop)) + self.osc_server.add_handler("/live/arrangement_clip/set/%s" % prop, + create_arrangement_clip_callback(self._set_property, prop)) def clip_get_notes(clip, params: Tuple[Any] = ()): if len(params) == 4: @@ -164,8 +194,15 @@ def clip_remove_notes(clip, params: Tuple[Any] = ()): clip.remove_notes_extended(pitch_start, pitch_span, time_start, time_span) self.osc_server.add_handler("/live/clip/get/notes", create_clip_callback(clip_get_notes)) + self.osc_server.add_handler("/live/arrangement_clip/get/notes", + create_arrangement_clip_callback(clip_get_notes)) self.osc_server.add_handler("/live/clip/add/notes", create_clip_callback(clip_add_notes)) + self.osc_server.add_handler("/live/arrangement_clip/add/notes", + create_arrangement_clip_callback(clip_add_notes)) self.osc_server.add_handler("/live/clip/remove/notes", create_clip_callback(clip_remove_notes)) + self.osc_server.add_handler("/live/arrangement_clip/remove/notes", + create_arrangement_clip_callback(clip_remove_notes)) + def clips_filter_handler(params: Tuple): # TODO: Pre-cache clip notes