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
47 changes: 37 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,15 +329,43 @@ A Clip Slot represents a container for a clip. It is used to create and delete c
<details>
<summary><b>Documentation</b>: Clip Slot API</summary>

| Address | Query params | Response params | Description |
|:------------------------------------|:---------------------------------------------------------------|:-----------------------------------------|:------------------------------------------------|
| /live/clip_slot/fire | track_index, clip_index | | Fire play/pause of the specified clip slot |
| /live/clip_slot/create_clip | track_index, clip_index, length | | Create a clip in the slot |
| /live/clip_slot/delete_clip | track_index, clip_index | | Delete the clip in the slot |
| /live/clip_slot/get/has_clip | track_index, clip_index | track_index, clip_index, has_clip | Query whether the slot has a clip |
| /live/clip_slot/get/has_stop_button | track_index, clip_index | track_index, clip_index, has_stop_button | Query whether the slot has a stop button |
| /live/clip_slot/set/has_stop_button | track_index, clip_index, has_stop_button | | Add or remove stop button (1=on, 0=off) |
| /live/clip_slot/duplicate_clip_to | track_index, clip_index, target_track_index, target_clip_index | | Duplicate the clip to an empty target clip slot |
| Address | Query params | Response params | Description |
| -------------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| /live/clip_slot/fire | track_index, clip_index | | Fire play/pause of the specified clip slot |
| /live/clip_slot/stop | track_index, clip_index | | Stops playling/recording the specified clip slot |
| /live/clip_slot/create/midi_clip | track_index, clip_index, length | | Create a MIDI clip in the slot |
| /live/clip_slot/create/audio_clip | track_index, clip_index, file_path | | Create an audio clip from file in the slot |
| /live/clip_slot/delete/clip | track_index, clip_index | | Delete the clip in the slot |
| /live/clip_slot/duplicate_to | track_index, clip_index, target_track_index, target_clip_index | | Duplicate the clip to a target clip slot |
| /live/clip_slot/set/fire_button | track_index, clip_index, fire_button_state | | Set fire button state directly (1=on, 0=off) |
| /live/clip_slot/get/color | track_index, clip_index | track_index, clip_index, color | Get clip slot color; Group Track slots only |
| /live/clip_slot/start_listen/color | track_index, clip_index | track_index, clip_index, color | Listen for slot color changes; Group Track slots only |
| /live/clip_slot/stop_listen/color | track_index, clip_index | | Stop listening for slot color changes; Group Track slots only |
| /live/clip_slot/get/color_index | track_index, clip_index | track_index, clip_index, color_index | Get clip slot color index (0-69); Group Track slots only |
| /live/clip_slot/start_listen/color_index | track_index, clip_index | track_index, clip_index, color_index | Listen for slot color index changes; Group Track slots only |
| /live/clip_slot/stop_listen/color_index | track_index, clip_index | | Stop listening for slot color index changes; Group Track slots only |
| /live/clip_slot/get/controls_other_clips | track_index, clip_index | track_index, clip_index, controls_other_clips | Get whether slot controls other clips; Group Track slots only |
| /live/clip_slot/start_listen/controls_other_clips | track_index, clip_index | track_index, clip_index, controls_other_clips | Listen for controls_other_clips changes; Group Track slots only |
| /live/clip_slot/stop_listen/controls_other_clips | track_index, clip_index | | Stop listening for controls_other_clips changes; Group Track slots only |
| /live/clip_slot/get/has_clip | track_index, clip_index | track_index, clip_index, has_clip | Query whether the slot has a clip |
| /live/clip_slot/start_listen/has_clip | track_index, clip_index | track_index, clip_index, has_clip | Listen for has_clip changes |
| /live/clip_slot/stop_listen/has_clip | track_index, clip_index | | Stop listening for has_clip changes |
| /live/clip_slot/get/has_stop_button | track_index, clip_index | track_index, clip_index, has_stop_button | Query whether the slot has a stop button |
| /live/clip_slot/set/has_stop_button | track_index, clip_index, has_stop_button | | Add or remove stop button (1=on, 0=off) |
| /live/clip_slot/start_listen/has_stop_button | track_index, clip_index | track_index, clip_index, has_stop_button | Listen for has_stop_button changes |
| /live/clip_slot/stop_listen/has_stop_button | track_index, clip_index | | Stop listening for has_stop_button changes |
| /live/clip_slot/get/is_group_slot | track_index, clip_index | track_index, clip_index, is_group_slot | Query whether the slot is a group slot |
| /live/clip_slot/get/is_playing | track_index, clip_index | track_index, clip_index, is_playing | Query whether the slot is playing (playing_status != 0) |
| /live/clip_slot/get/is_recording | track_index, clip_index | track_index, clip_index, is_recording | Query whether the slot is recording (playing_status == 2) |
| /live/clip_slot/get/is_triggered | track_index, clip_index | track_index, clip_index, is_triggered | Query whether the slot is triggered |
| /live/clip_slot/start_listen/is_triggered | track_index, clip_index | track_index, clip_index, is_triggered | Listen for is_triggered changes |
| /live/clip_slot/stop_listen/is_triggered | track_index, clip_index | | Stop listening for is_triggered changes |
| /live/clip_slot/get/playing_status | track_index, clip_index | track_index, clip_index, playing_status | Query slot playing_status: At least one clip in Group Track slot is (1=playing, 2=recording) |
| /live/clip_slot/start_listen/playing_status | track_index, clip_index | track_index, clip_index, playing_status | Listen for playing_status changes |
| /live/clip_slot/stop_listen/playing_status | track_index, clip_index | | Stop listening for playing_status changes |
| /live/clip_slot/get/will_record_on_start | track_index, clip_index | track_index, clip_index, will_record_on_start | Query whether slot will record on start |
| /live/clip_slot/create_clip | track_index, clip_index, length | | Create a MIDI clip in the slot (kept for backwards-compatibility) |
| /live/clip_slot/delete_clip | track_index, clip_index | | Delete the clip in the slot (kept for backwards-compatibility) |

</details>

Expand Down Expand Up @@ -557,4 +585,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))

122 changes: 85 additions & 37 deletions abletonosc/clip_slot.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,61 +7,109 @@ def __init__(self, manager):
self.class_identifier = "clip_slot"

def init_api(self):
def create_clip_slot_callback(func, *args, pass_clip_index=False):
def create_clip_slot_callback(func, *args, pass_clip_index=False, **kwargs):
def clip_slot_callback(params: Tuple[Any]):
track_index, clip_index = int(params[0]), int(params[1])
track = self.song.tracks[track_index]
clip_slot = track.clip_slots[clip_index]

if pass_clip_index:
rv = func(clip_slot, *args, tuple(params[0:]))
rv = func(clip_slot, *args, tuple(params[0:]), **kwargs)
else:
rv = func(clip_slot, *args, tuple(params[2:]))
rv = func(clip_slot, *args, tuple(params[2:]), **kwargs)

self.logger.info(track_index, clip_index, rv)
self.logger.info("clip_slot %s,%s -> %s", track_index, clip_index, rv)
if rv is not None:
return (track_index, clip_index, *rv)

return clip_slot_callback

methods = [
"fire",
"stop",
"create_clip",
"delete_clip"
]
properties_r = [
"has_clip",
"controls_other_clips",
"is_group_slot",
"is_playing",
"is_triggered",
"playing_status",
"will_record_on_start",
]
properties_rw = [
"has_stop_button"
]
methods = {
"fire": {"alias": 0, "caller": 1},
"stop": {"alias": 0, "caller": 1},
"create_clip": {"alias": 0, "caller": 1}, # Back-compat
"create/midi_clip": {"alias": 1, "caller": "create_clip"},
"create/audio_clip": {"alias": 1, "caller": "create_audio_clip"},
"delete_clip": {"alias": 0, "caller": 1}, # Back-compat
"delete/clip": {"alias": 1, "caller": "delete_clip"},
"duplicate_to": {"alias": 0, "caller": "clip_slot_duplicate_to"},
"set/fire_button": {"alias": 1, "caller": "set_fire_button_state"},
}

for method in methods:
self.osc_server.add_handler("/live/clip_slot/%s" % method,
create_clip_slot_callback(self._call_method, method))
properties = {
# TODO: returns objects, needs serialization
# "canonical_parent": {"get": 0, "set": 0, "listen": 0},
# "clip": {"get": 0, "set": 0, "listen": 0},

for prop in properties_r + properties_rw:
self.osc_server.add_handler("/live/clip_slot/get/%s" % prop,
create_clip_slot_callback(self._get_property, prop))
self.osc_server.add_handler("/live/clip_slot/start_listen/%s" % prop,
create_clip_slot_callback(self._start_listen, prop, pass_clip_index=True))
self.osc_server.add_handler("/live/clip_slot/stop_listen/%s" % prop,
create_clip_slot_callback(self._stop_listen, prop, pass_clip_index=True))
for prop in properties_rw:
self.osc_server.add_handler("/live/clip_slot/set/%s" % prop,
create_clip_slot_callback(self._set_property, prop))
# All clip slots
"has_clip": {"get": 1, "set": 0, "listen": 1},
"has_stop_button": {"get": 1, "set": 1, "listen": 1},
"is_group_slot": {"get": 1, "set": 0, "listen": 0},
"is_playing": {"get": 1, "set": 0, "listen": 0},
"is_recording": {"get": 1, "set": 0, "listen": 0},
"is_triggered": {"get": 1, "set": 0, "listen": 1},
"will_record_on_start": {"get": 1, "set": 0, "listen": 0},

# Group Track slots only
"color": {"get": 1, "set": 0, "listen": 1},
"color_index": {"get": 1, "set": 0, "listen": 1},
"controls_other_clips": {"get": 1, "set": 0, "listen": 1},
"playing_status": {"get": 1, "set": 0, "listen": 1},

def duplicate_clip_slot(clip_slot, args):

}

def clip_slot_duplicate_to(clip_slot, args):
target_track_index, target_clip_index = tuple(args)
track = self.song.tracks[target_track_index]
target_clip_slot = track.clip_slots[target_clip_index]
clip_slot.duplicate_clip_to(target_clip_slot)

self.osc_server.add_handler("/live/clip_slot/duplicate_clip_to", create_clip_slot_callback(duplicate_clip_slot))
# Add Handlers
local_funcs = locals()
for method, spec in methods.items():
alias = spec.get("alias")
caller = spec.get("caller")
if not caller:
continue
if not alias and isinstance(caller, str):
caller = local_funcs[caller]
self.osc_server.add_handler("/live/clip_slot/%s" % method,
create_clip_slot_callback(caller))
else:
if not alias:
caller = method
self.osc_server.add_handler("/live/clip_slot/%s" % method,
create_clip_slot_callback(self._call_method, caller))

for prop, spec in properties.items():
getter_func = spec.get("get")
if isinstance(getter_func, str):
getter = local_funcs[getter_func]
self.osc_server.add_handler("/live/clip_slot/get/%s" % prop,
create_clip_slot_callback(getter))
elif getter_func:
self.osc_server.add_handler("/live/clip_slot/get/%s" % prop,
create_clip_slot_callback(self._get_property, prop))

setter_func = spec.get("set")
if isinstance(setter_func, str):
setter = local_funcs[setter_func]
self.osc_server.add_handler("/live/clip_slot/set/%s" % prop,
create_clip_slot_callback(setter))
elif setter_func:
self.osc_server.add_handler("/live/clip_slot/set/%s" % prop,
create_clip_slot_callback(self._set_property, prop))

observable = spec.get("listen")
if isinstance(observable, str):
getter = "bang" if observable == "bang" else local_funcs[observable]
self.osc_server.add_handler("/live/clip_slot/start_listen/%s" % prop,
create_clip_slot_callback(self._start_listen, prop, pass_clip_index=True, getter=getter))
self.osc_server.add_handler("/live/clip_slot/stop_listen/%s" % prop,
create_clip_slot_callback(self._stop_listen, prop, pass_clip_index=True))
elif observable:
self.osc_server.add_handler("/live/clip_slot/start_listen/%s" % prop,
create_clip_slot_callback(self._start_listen, prop, pass_clip_index=True))
self.osc_server.add_handler("/live/clip_slot/stop_listen/%s" % prop,
create_clip_slot_callback(self._stop_listen, prop, pass_clip_index=True))
Loading