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
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ Represents the view (user interface) of live

## Track API

Represents an audio, MIDI, return or master track. Can be used to set track audio parameters (volume, panning, send, mute, solo), listen for the playing clip slot, query devices, etc. Can also be used to query clips in arrangement view.
Represents an audio, MIDI, return or master track. Can be used to set track audio parameters (volume, panning, sends, mute, solo), listen for the playing clip slot, query devices, etc. Can also be used to query clips in arrangement view.

To query the properties of multiple tracks, see [Song: Properties of cue points, scenes and tracks](https://github.com/ideoforms/AbletonOSC#song-properties-of-cue-points-scenes-and-tracks).

Expand All @@ -234,6 +234,7 @@ To query the properties of multiple tracks, see [Song: Properties of cue points,

- Changes for any Track property can be listened for by calling `/live/track/start_listen/<property> <track_index>`
- Responses will be sent to `/live/track/get/<property>`, with parameters `<track_index> <property_value>`
- Sends will take the same format, but with an added send index after the track index: `/live/track/start_listen/sends <track_index> <send_index>`

#### Getters

Expand Down Expand Up @@ -268,7 +269,7 @@ To query the properties of multiple tracks, see [Song: Properties of cue points,
| /live/track/get/name | track_id | track_id, name | Query track name |
| /live/track/get/panning | track_id | track_id, panning | Query track panning |
| /live/track/get/playing_slot_index | track_id | track_id, index | Query currently-playing slot |
| /live/track/get/send | track_id, send_id | track_id, send_id, value | Query track send |
| /live/track/get/sends | track_id, send_id | track_id, send_id, value | Query track send |
| /live/track/get/solo | track_id | track_id, solo | Query track solo on/off |
| /live/track/get/volume | track_id | track_id, volume | Query track volume |

Expand All @@ -288,7 +289,7 @@ To query the properties of multiple tracks, see [Song: Properties of cue points,
| /live/track/set/output_routing_channel | track_id, channel | | Set output routing channel |
| /live/track/set/output_routing_type | track_id, type | | Set output routing type |
| /live/track/set/panning | track_id, panning | | Set track panning |
| /live/track/set/send | track_id, send_id, value | | Set track send |
| /live/track/set/sends | track_id, send_id, value | | Set track send |
| /live/track/set/solo | track_id, solo | | Set track solo (1=on, 0=off) |
| /live/track/set/volume | track_id, volume | | Set track volume |

Expand Down
64 changes: 36 additions & 28 deletions abletonosc/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def track_callback(params: Tuple[Any]):
# Volume, panning and send are properties of the track's mixer_device so
# can't be formulated as normal callbacks that reference properties of track.
#--------------------------------------------------------------------------------
mixer_properties_rw = ["volume", "panning"]
mixer_properties_rw = ["volume", "panning", "sends"]
for prop in mixer_properties_rw:
self.osc_server.add_handler("/live/track/get/%s" % prop,
create_track_callback(self._get_mixer_property, prop))
Expand All @@ -89,19 +89,6 @@ def track_callback(params: Tuple[Any]):
self.osc_server.add_handler("/live/track/stop_listen/%s" % prop,
create_track_callback(self._stop_mixer_listen, prop, include_track_id=True))

# Still need to fix these
# Might want to find a better approach that unifies volume and sends
def track_get_send(track, params: Tuple[Any] = ()):
send_id, = params
return send_id, track.mixer_device.sends[send_id].value

def track_set_send(track, params: Tuple[Any] = ()):
send_id, value = params
track.mixer_device.sends[send_id].value = value

self.osc_server.add_handler("/live/track/get/send", create_track_callback(track_get_send))
self.osc_server.add_handler("/live/track/set/send", create_track_callback(track_set_send))

def track_delete_clip(track, params: Tuple[Any]):
clip_index, = params
track.clip_slots[clip_index].delete_clip()
Expand Down Expand Up @@ -232,17 +219,34 @@ def track_set_input_routing_channel(track, params):
self.osc_server.add_handler("/live/track/set/input_routing_channel", create_track_callback(track_set_input_routing_channel))

def _set_mixer_property(self, target, prop, params: Tuple) -> None:
parameter_object = getattr(target.mixer_device, prop)
self.logger.info("Setting property for %s: %s (new value %s)" % (self.class_identifier, prop, params[0]))
parameter_object.value = params[0]
if prop == 'sends':
send_id, value = params
parameter_object = getattr(target.mixer_device, prop)[send_id]
self.logger.info("Setting property for %s: %s (new value %s)" % (self.class_identifier, prop, params[0]))
parameter_object.value = value
else:
parameter_object = getattr(target.mixer_device, prop)
self.logger.info("Setting property for %s: %s (new value %s)" % (self.class_identifier, prop, params[0]))
parameter_object.value = params[0]

def _get_mixer_property(self, target, prop, params: Optional[Tuple] = ()) -> Tuple[Any]:
parameter_object = getattr(target.mixer_device, prop)
self.logger.info("Getting property for %s: %s = %s" % (self.class_identifier, prop, parameter_object.value))
return parameter_object.value,
if prop == 'sends':
send_id, = params
parameter_object = getattr(target.mixer_device, prop)[send_id]
self.logger.info("Getting property for %s: %s = %s" % (self.class_identifier, prop, parameter_object.value))
return send_id, parameter_object.value,
else:
parameter_object = getattr(target.mixer_device, prop)
self.logger.info("Getting property for %s: %s = %s" % (self.class_identifier, prop, parameter_object.value))
return parameter_object.value,

def _start_mixer_listen(self, target, prop, params: Optional[Tuple] = ()) -> None:
parameter_object = getattr(target.mixer_device, prop)
if prop == 'sends':
track_id, send_id, = params
parameter_object = getattr(target.mixer_device, prop)[send_id]
else:
parameter_object = getattr(target.mixer_device, prop)

def property_changed_callback():
value = parameter_object.value
self.logger.info("Property %s changed of %s %s: %s" % (prop, self.class_identifier, str(params), value))
Expand All @@ -251,19 +255,23 @@ def property_changed_callback():

listener_key = (prop, tuple(params))
if listener_key in self.listener_functions:
self._stop_mixer_listen(target, prop, params)

self.logger.info("Adding listener for %s %s, property: %s" % (self.class_identifier, str(params), prop))

parameter_object.add_value_listener(property_changed_callback)
self.listener_functions[listener_key] = property_changed_callback
self.logger.info("Already assigned listener for %s %s, property: %s" % (self.class_identifier, str(params), prop))
else:
self.logger.info("Adding listener for %s %s, property: %s" % (self.class_identifier, str(params), prop))
parameter_object.add_value_listener(property_changed_callback)
self.listener_functions[listener_key] = property_changed_callback
#--------------------------------------------------------------------------------
# Immediately send the current value
#--------------------------------------------------------------------------------
property_changed_callback()

def _stop_mixer_listen(self, target, prop, params: Optional[Tuple[Any]] = ()) -> None:
parameter_object = getattr(target.mixer_device, prop)
if prop == 'sends':
track_id, send_id, = params
parameter_object = getattr(target.mixer_device, prop)[send_id]
else:
parameter_object = getattr(target.mixer_device, prop)

listener_key = (prop, tuple(params))
if listener_key in self.listener_functions:
self.logger.info("Removing listener for %s %s, property %s" % (self.class_identifier, str(params), prop))
Expand Down
4 changes: 2 additions & 2 deletions tests/test_track.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def test_track_get_send(client):
send_id = 1

for value in [0.5, 0.0]:
client.send_message("/live/track/set/send", [track_id, send_id, value])
client.send_message("/live/track/set/sends", [track_id, send_id, value])
wait_one_tick()
assert client.query("/live/track/get/send", (track_id, send_id)) == (track_id, send_id, value,)
assert client.query("/live/track/get/sends", (track_id, send_id)) == (track_id, send_id, value,)

#--------------------------------------------------------------------------------
# Test track properties - clips
Expand Down