Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<details>
<summary><b>Documentation</b>: Clip API</summary>
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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))

37 changes: 37 additions & 0 deletions abletonosc/clip.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand Down