# AbletonOSC A full-coverage Ableton Live 11 remote script that exposes the entire Live Object Model over UDP/OSC. Drop it into Live's MIDI Remote Scripts directory, select it in preferences, and control every aspect of your session from any language or tool that speaks OSC. **Zero external runtime dependencies** — OSC encoding and decoding are implemented from scratch in pure Python, so nothing needs to be installed inside Ableton's embedded Python environment. --- ## Table of contents - [Installation](#installation) - [Network configuration](#network-configuration) - [Protocol reference](#protocol-reference) - [Address schema](#address-schema) - [Operations](#operations) - [Type mapping](#type-mapping) - [Indexing](#indexing) - [Wildcard support](#wildcard-support) - [Response format](#response-format) - [Listener pattern](#listener-pattern) - [Control endpoints](#control-endpoints) - [Song](#song) - [Track](#track) - [Return track](#return-track) - [Clip slot](#clip-slot) - [Clip](#clip) - [Device](#device) - [Scene](#scene) - [View](#view) - [Application](#application) - [Browser](#browser) - [Groove](#groove) - [Development setup](#development-setup) - [Architecture](#architecture) --- ## Installation 1. Copy the `AbletonOSC/` folder into Ableton Live's MIDI Remote Scripts directory: | Platform | Path | |----------|------| | Windows | `C:\ProgramData\Ableton\Live \Resources\MIDI Remote Scripts\` | | macOS | `~/Library/Application Support/Ableton/Live /Resources/MIDI Remote Scripts/` | 2. Start (or restart) Ableton Live. 3. Open **Preferences → MIDI → Control Surfaces**, set one of the slots to `AbletonOSC`. 4. Send a test ping: ``` /live/test → reply: "AbletonOSC active" ``` --- ## Network configuration | Direction | Address | Port | |-----------|---------|------| | Incoming (commands) | `0.0.0.0` (all interfaces) | **11000** | | Outgoing (replies / listener push) | sender's IP | **11001** | Ports can be overridden with environment variables before Live starts: ``` ABLETON_OSC_LISTEN_PORT=11000 ABLETON_OSC_SEND_PORT=11001 ``` Replies are always sent to the IP of the most recent sender on port 11001. Running multiple clients simultaneously from the same IP works; running them from different IPs does not (last sender wins). --- ## Protocol reference ### Address schema ``` /live/// [index…] [value…] /live// [index…] [arg…] ``` ### Operations | Operation | Direction | Description | |-----------|-----------|-------------| | `get` | request → reply | Read the current value. The reply is sent back to the same address. | | `set` | request only | Write a value. No reply is sent unless documented otherwise. | | `start_listen` | request | Attach a Live listener; sends the current value immediately, then pushes updates to the `get` address whenever the value changes. | | `stop_listen` | request | Detach the previously registered listener. | Methods (e.g. `/live/song/start_playing`) have no operation verb. They are fire-and-forget unless explicitly noted as returning a value. ### Type mapping OSC types used in this implementation: | OSC tag | Python type | Notes | |---------|-------------|-------| | `i` | `int` | 32-bit signed integer | | `f` | `float` | 32-bit IEEE 754 | | `s` | `str` | UTF-8, null-padded to 4-byte boundary | | `T` / `F` | `bool` | OSC true/false atoms | | `N` | `None` | OSC nil | Booleans in replies are sent as OSC `T`/`F`. Booleans in incoming messages are accepted as `T`/`F`, `1`/`0` int, or `True`/`False` float. ### Indexing - All indices are **zero-based**. - `track_index` refers to the position in `song.tracks` (not including return tracks). - `clip_index` / `slot_index` refers to the clip slot position within the track (row in the session view, 0 = top). - `device_index` refers to the position in `track.devices`. - `param_index` refers to the position in `device.parameters`. - `scene_index` refers to the position in `song.scenes`. ### Wildcard support Track handlers accept `*` in place of `track_index`. The handler iterates all tracks and returns one response per track, prefixed with that track's index. ``` /live/track/get/volume * → 0 0.85 1 0.72 2 1.0 ... ``` ### Response format A reply is sent to the **same OSC address** as the request, with the value appended. For indexed objects (track, clip, device, scene), the index(es) are included in the reply so responses from wildcard queries or multiple listeners can be correlated: ``` Request: /live/track/get/mute 2 Reply: /live/track/get/mute 2 0 (track 2, mute=false) Request: /live/clip/get/name 0 3 Reply: /live/clip/get/name 0 3 "My Clip" Request: /live/device/get/parameter/value 1 0 5 Reply: /live/device/get/parameter/value 1 0 5 0.75 ``` When a handler returns multiple values (e.g. a list of names), they are flattened into OSC arguments: ``` /live/song/get/track_names → "Drums" "Bass" "Lead" ... /live/track/get/clips/name 0 → "Loop A" "Loop B" ... ``` ### Listener pattern ``` # Subscribe — receives current value immediately, then on every change: /live/song/start_listen/tempo → /live/song/get/tempo 120.0 # … every time tempo changes Live pushes: → /live/song/get/tempo 124.0 # Unsubscribe: /live/song/stop_listen/tempo ``` Listeners survive a `/live/api/reload` only if you re-send `start_listen` after the reload. --- ## Control endpoints | Address | Args | Description | |---------|------|-------------| | `/live/test` | — | Connectivity check. Replies `"AbletonOSC active"`. | | `/live/startup` | — | Sent automatically by the script on load. | | `/live/shutdown` | — | Sent automatically by the script on disconnect. | | `/live/error` | `string` | Sent by the script when a handler throws an unhandled exception. | | `/live/api/reload` | — | Clears and re-registers all handlers. Replies `"reloaded"`. | | `/live/api/show_message` | `string` | Displays a string in Live's status bar. | --- ## Song The song object is a singleton; no index is needed. ### Properties #### Read / write | Property | Type | Description | |----------|------|-------------| | `tempo` | float | BPM (60–200) | | `time_signature_numerator` | int | Beats per bar | | `time_signature_denominator` | int | Beat unit (2, 4, 8, 16…) | | `loop` | bool | Arrangement loop on/off | | `loop_start` | float | Loop start in beats | | `loop_length` | float | Loop length in beats | | `current_song_time` | float | Playback position in beats | | `metronome` | bool | Metronome on/off | | `record_mode` | bool | Arrangement record on/off | | `arrangement_overdub` | bool | Arrangement overdub on/off | | `session_record` | bool | Session record armed | | `overdub` | bool | Clip overdub on/off | | `groove_amount` | float | Global groove intensity (0–1) | | `swing_amount` | float | Global swing amount (0–1) | | `clip_trigger_quantization` | int | Clip launch quantization enum | | `midi_recording_quantization` | int | MIDI recording quantization enum | | `punch_in` | bool | Arrangement punch-in enabled | | `punch_out` | bool | Arrangement punch-out enabled | | `exclusive_arm` | bool | Solo arm mode on/off | | `exclusive_solo` | bool | Exclusive solo mode on/off | #### Read-only | Property | Type | Description | |----------|------|-------------| | `is_playing` | bool | True while transport is running | | `can_undo` | bool | Undo history available | | `can_redo` | bool | Redo history available | | `song_length` | float | Total arrangement length in beats | | `session_record_status` | int | Session record state enum | ### Aggregate queries | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/song/get/num_tracks` | — | `int` | Number of tracks | | `/live/song/get/num_scenes` | — | `int` | Number of scenes | | `/live/song/get/num_return_tracks` | — | `int` | Number of return tracks | | `/live/song/get/track_names` | `[start] [end]` | `string…` | Track names, optionally sliced | | `/live/song/get/scene_names` | `[start] [end]` | `string…` | Scene names, optionally sliced | | `/live/song/get/return_track_names` | — | `string…` | Return track names | | `/live/song/get/cue_points` | — | `string float …` | Interleaved name/time pairs | ### Methods | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/song/start_playing` | — | — | Start transport from current position | | `/live/song/stop_playing` | — | — | Stop transport | | `/live/song/continue_playing` | — | — | Resume from current position | | `/live/song/stop_all_clips` | — | — | Stop all playing clips | | `/live/song/undo` | — | — | Undo last action | | `/live/song/redo` | — | — | Redo last undone action | | `/live/song/tap_tempo` | — | — | Tap tempo | | `/live/song/trigger_session_record` | — | — | Toggle session record | | `/live/song/re_enable_automation` | — | — | Re-enable all disabled automation | | `/live/song/capture_midi` | — | — | Capture MIDI from input | | `/live/song/jump_by` | `float` | — | Jump playback position by N beats | | `/live/song/jump_to_next_cue` | — | — | Jump to next cue point | | `/live/song/jump_to_prev_cue` | — | — | Jump to previous cue point | | `/live/song/create_audio_track` | `[index]` | `int` | Create audio track at index (−1 = end), returns new track count | | `/live/song/create_midi_track` | `[index]` | `int` | Create MIDI track at index | | `/live/song/create_return_track` | — | — | Append a return track | | `/live/song/create_scene` | `[index]` | `int` | Create scene at index, returns new scene count | | `/live/song/delete_track` | `track_index` | — | Delete a track | | `/live/song/delete_scene` | `scene_index` | — | Delete a scene | | `/live/song/delete_return_track` | `return_index` | — | Delete a return track | | `/live/song/duplicate_track` | `track_index` | — | Duplicate a track | | `/live/song/duplicate_scene` | `scene_index` | — | Duplicate a scene | ### Beat listener (special) The beat listener fires on every beat change during playback, not just when a property is set. ``` /live/song/start_listen/beat → pushes /live/song/get/beat /live/song/stop_listen/beat ``` --- ## Track Addresses all regular session/audio/MIDI/group tracks. Return tracks use `/live/return_track/` (same API surface). ### Properties All track property requests take `track_index` as the first argument. Use `*` to query all tracks at once. #### Read / write | Property | Type | Description | |----------|------|-------------| | `arm` | bool | Record arm | | `mute` | bool | Mute | | `solo` | bool | Solo | | `name` | string | Track name | | `color` | int | Track color as packed RGB integer | | `color_index` | int | Track color index | | `fold_state` | bool | Group track folded | | `current_monitoring_state` | int | 0=In, 1=Auto, 2=Off | #### Read-only | Property | Type | Description | |----------|------|-------------| | `can_be_armed` | bool | Whether the track supports arming | | `has_audio_input` | bool | — | | `has_audio_output` | bool | — | | `has_midi_input` | bool | — | | `has_midi_output` | bool | — | | `is_foldable` | bool | Group track | | `is_grouped` | bool | Track is inside a group | | `is_visible` | bool | Track visible in session | | `fired_slot_index` | int | Index of slot queued to play (−1 if none) | | `playing_slot_index` | int | Index of currently playing slot (−1 if none) | | `output_meter_level` | float | Combined output level (0–1) | | `output_meter_left` | float | Left channel level | | `output_meter_right` | float | Right channel level | | `output_meter_peak_left` | float | Left peak | | `output_meter_peak_right` | float | Right peak | ### Mixer device | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/track/get/volume` | `track_index` | `track_index float` | Fader level (0–1 linear; 0.85 ≈ 0 dB) | | `/live/track/set/volume` | `track_index float` | — | Set fader level | | `/live/track/start_listen/volume` | `track_index` | — | Push volume changes | | `/live/track/stop_listen/volume` | `track_index` | — | | | `/live/track/get/panning` | `track_index` | `track_index float` | Pan (−1 left … 0 centre … 1 right) | | `/live/track/set/panning` | `track_index float` | — | | | `/live/track/start_listen/panning` | `track_index` | — | Push panning changes | | `/live/track/stop_listen/panning` | `track_index` | — | | | `/live/track/get/send` | `track_index send_index` | `track_index send_index float` | Send level (0–1) | | `/live/track/set/send` | `track_index send_index float` | — | Set send level | | `/live/track/start_listen/send` | `track_index send_index` | — | Push send level changes | | `/live/track/stop_listen/send` | `track_index send_index` | — | | ### Routing | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/track/get/available_input_routing_types` | `track_index` | `string…` | Available input type names | | `/live/track/get/available_input_routing_channels` | `track_index` | `string…` | Available input channel names | | `/live/track/get/input_routing_type` | `track_index` | `string` | Current input type name | | `/live/track/set/input_routing_type` | `track_index string` | — | Set input type by display name | | `/live/track/get/input_routing_channel` | `track_index` | `string` | Current input channel name | | `/live/track/set/input_routing_channel` | `track_index string` | — | Set input channel by display name | | `/live/track/get/available_output_routing_types` | `track_index` | `string…` | Available output type names | | `/live/track/get/available_output_routing_channels` | `track_index` | `string…` | Available output channel names | | `/live/track/get/output_routing_type` | `track_index` | `string` | Current output type name | | `/live/track/set/output_routing_type` | `track_index string` | — | Set output type by display name | | `/live/track/get/output_routing_channel` | `track_index` | `string` | Current output channel name | | `/live/track/set/output_routing_channel` | `track_index string` | — | Set output channel by display name | ### Aggregate clip info | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/track/get/clips/name` | `track_index` | `string…` | Names of all occupied session slots | | `/live/track/get/clips/length` | `track_index` | `float…` | Lengths in beats | | `/live/track/get/clips/color` | `track_index` | `int…` | Colors | | `/live/track/get/clips/is_playing` | `track_index` | `bool…` | Playing states | | `/live/track/get/arrangement_clips/name` | `track_index` | `string…` | Arrangement clip names | | `/live/track/get/arrangement_clips/length` | `track_index` | `float…` | Arrangement clip lengths | | `/live/track/get/arrangement_clips/start_time` | `track_index` | `float…` | Arrangement clip start times in beats | ### Device info | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/track/get/num_devices` | `track_index` | `int` | Number of devices on track | | `/live/track/get/devices/name` | `track_index` | `string…` | Device names | | `/live/track/get/devices/type` | `track_index` | `int…` | Device types (0=audio_effect, 1=instrument, 2=midi_effect) | | `/live/track/get/devices/class_name` | `track_index` | `string…` | Device class names (e.g. `"Reverb"`, `"Operator"`) | | `/live/track/get/devices/can_have_chains` | `track_index` | `bool…` | Whether each device is a rack | ### Methods | Address | Args | Description | |---------|------|-------------| | `/live/track/stop_all_clips` | `track_index` (or `*`) | Stop all clips on track(s) | | `/live/track/delete_device` | `track_index device_index` | Delete a device | | `/live/track/duplicate_device` | `track_index device_index` | Duplicate a device | --- ## Return track Identical API surface to `/live/track/`, accessed via `/live/return_track/`. Supported properties: `name`, `mute`, `solo`, `color`, `color_index`, `output_meter_level`, `output_meter_left`, `output_meter_right`, plus `volume`, `panning`, `send`, `num_devices`, `devices/name`. --- ## Clip slot Addresses a single cell in the session grid. Indexed by `(track_index, slot_index)`. ### Properties | Property | R/W | Type | Description | |----------|-----|------|-------------| | `has_clip` | R | bool | Whether a clip occupies the slot | | `is_playing` | R | bool | Clip currently playing | | `is_triggered` | R | bool | Clip queued to play | | `is_recording` | R | bool | Clip currently recording | | `is_group_slot` | R | bool | Slot belongs to a group track | | `controls_other_clips` | R | bool | Group slot that controls children | | `playing_status` | R | int | Playback status enum | | `will_record_on_start` | R | bool | Will record when triggered | | `has_stop_button` | R/W | bool | Slot has a stop button visible | ### Methods | Address | Args | Description | |---------|------|-------------| | `/live/clip_slot/fire` | `track_index slot_index` | Launch the clip (or empty slot) | | `/live/clip_slot/stop` | `track_index slot_index` | Stop playback | | `/live/clip_slot/create_clip` | `track_index slot_index [length]` | Create a new empty MIDI clip; `length` in beats (default 4) | | `/live/clip_slot/delete_clip` | `track_index slot_index` | Delete the clip in this slot | | `/live/clip_slot/duplicate_clip_to` | `src_track src_slot dst_track dst_slot` | Copy clip to another slot | --- ## Clip Addresses the clip object inside a slot. Indexed by `(track_index, clip_index)`. All replies include the two indices as a prefix. ### Properties #### Read / write | Property | Type | Description | |----------|------|-------------| | `name` | string | Clip name | | `color` | int | Color (packed RGB) | | `color_index` | int | Color index | | `muted` | bool | Clip muted | | `gain` | float | Clip gain (0–1) | | `pitch_coarse` | int | Transpose in semitones (−48…+48) | | `pitch_fine` | float | Detune in cents (−50…+50) | | `velocity_amount` | float | Velocity deviation | | `looping` | bool | Loop enabled | | `loop_start` | float | Loop start in beats | | `loop_end` | float | Loop end in beats | | `start_marker` | float | Start marker in beats | | `end_marker` | float | End marker in beats | | `warping` | bool | Warp enabled (audio clips) | | `warp_mode` | int | Warp mode enum (0=Beats, 1=Tones, …) | | `launch_mode` | int | 0=Trigger, 1=Gate, 2=Toggle, 3=Repeat | | `launch_quantization` | int | Clip launch quantization enum | | `ram_mode` | bool | RAM mode (audio clips) | #### Read-only | Property | Type | Description | |----------|------|-------------| | `is_playing` | bool | Currently playing | | `is_recording` | bool | Currently recording | | `is_midi_clip` | bool | — | | `is_audio_clip` | bool | — | | `is_overdubbing` | bool | — | | `is_triggered` | bool | Queued to play | | `will_record_on_start` | bool | — | | `has_groove` | bool | Groove assigned | | `length` | float | Clip length in beats | | `start_time` | float | Start position in arrangement (beats) | | `end_time` | float | End position in arrangement (beats) | | `playing_position` | float | Current playback position in clip (beats) | | `sample_length` | float | Sample length in seconds (audio clips) | | `gain_display_string` | string | Human-readable gain string | | `file_path` | string | Audio file path (audio clips) | ### Playback methods | Address | Args | Description | |---------|------|-------------| | `/live/clip/fire` | `track_index clip_index` | Launch the clip | | `/live/clip/stop` | `track_index clip_index` | Stop the clip | | `/live/clip/duplicate_loop` | `track_index clip_index` | Double the loop length and duplicate content | | `/live/clip/quantize` | `track_index clip_index [quantization] [strength]` | Quantize notes; `quantization` is a Live enum int (default 4 = 1/16), `strength` 0–1 (default 1.0) | ### MIDI notes Notes in the classic API are represented as flat 5-tuples per note: `pitch start_time duration velocity mute`. Times and durations are in beats (floats). Pitch and velocity are MIDI integers 0–127. Mute is 0 or 1. | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/clip/get/notes` | `track_index clip_index` | `track clip pitch start dur vel mute …` | All notes in clip | | `/live/clip/get/notes` | `track_index clip_index pitch pitch_span time time_span` | `track clip pitch start dur vel mute …` | Filtered by pitch range and time range | | `/live/clip/add/notes` | `track clip pitch start dur vel mute [pitch start dur vel mute …]` | — | Add one or more notes (multiples of 5 args after indices) | | `/live/clip/remove/notes` | `track clip` | — | Remove all notes | | `/live/clip/remove/notes` | `track clip time time_span pitch pitch_span` | — | Remove notes in range | #### Extended note API (Live 11+) Returns 8 values per note: `pitch start_time duration velocity mute note_id release_velocity probability` | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/clip/get/notes_extended` | `track clip` | (8 values per note) | All notes with full properties including IDs | | `/live/clip/get/notes_extended` | `track clip pitch pitch_span time time_span` | (8 values per note) | Filtered query | | `/live/clip/apply_note_modifications` | `track clip note_id pitch start dur vel mute rel_vel prob […]` | — | Modify notes by ID; multiples of 8 args after indices | ### Automation envelopes Envelopes are addressed by `(track_index, clip_index, device_index, param_index)`. | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/clip/get/automation_envelope` | `track clip device param` | `track clip device param time val time val …` | Sampled envelope points | | `/live/clip/create_automation_envelope` | `track clip device param` | — | Create an envelope for the parameter | | `/live/clip/clear_envelope` | `track clip device param` | — | Clear all events from one envelope | | `/live/clip/clear_all_envelopes` | `track clip` | — | Clear all envelopes in the clip | ### Warp markers (audio clips) | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/clip/get/warp_markers` | `track clip` | `track clip beat_time sample_time …` | Interleaved beat/sample time pairs | ### Groove | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/clip/get/groove` | `track clip` | `track clip groove_name` | Name of assigned groove, or `""` | | `/live/clip/set/groove` | `track clip groove_name` | — | Assign groove by name; `""` clears it | --- ## Device Devices are addressed by `(track_index, device_index)`. Parameters by `(track_index, device_index, param_index)`. ### Device properties | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/device/get/name` | `track device` | `track device string` | Device name | | `/live/device/get/class_name` | `track device` | `track device string` | Class name (e.g. `"Reverb"`, `"Eq8"`, `"AuPluginDevice"`) | | `/live/device/get/type` | `track device` | `track device int` | 0=audio_effect, 1=instrument, 2=midi_effect | | `/live/device/get/is_active` | `track device` | `track device bool` | On/off bypass state | | `/live/device/set/is_active` | `track device bool` | — | Toggle bypass | | `/live/device/start_listen/is_active` | `track device` | — | Push bypass changes | | `/live/device/stop_listen/is_active` | `track device` | — | | ### Bulk parameter operations Most efficient way to read/write all parameters at once. | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/device/get/num_parameters` | `track device` | `track device int` | Parameter count | | `/live/device/get/parameters/name` | `track device` | `track device string…` | All parameter names | | `/live/device/get/parameters/value` | `track device` | `track device float…` | All parameter values | | `/live/device/get/parameters/min` | `track device` | `track device float…` | All parameter minimums | | `/live/device/get/parameters/max` | `track device` | `track device float…` | All parameter maximums | | `/live/device/get/parameters/default_value` | `track device` | `track device float…` | All parameter defaults | | `/live/device/get/parameters/is_quantized` | `track device` | `track device bool…` | Whether each parameter is stepped/quantized | | `/live/device/set/parameters/value` | `track device val0 val1 …` | — | Set all parameters; values mapped by position | ### Individual parameter operations | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/device/get/parameter/name` | `track device param` | `track device param string` | Parameter name | | `/live/device/get/parameter/value` | `track device param` | `track device param float` | Current value | | `/live/device/get/parameter/value_string` | `track device param` | `track device param string` | Human-readable value (e.g. `"2500 Hz"`) | | `/live/device/get/parameter/min` | `track device param` | `track device param float` | Minimum value | | `/live/device/get/parameter/max` | `track device param` | `track device param float` | Maximum value | | `/live/device/get/parameter/default_value` | `track device param` | `track device param float` | Default value | | `/live/device/set/parameter/value` | `track device param float` | — | Set parameter value | | `/live/device/start_listen/parameter/value` | `track device param` | — | Push value changes; fires to `/live/device/get/parameter/value` | | `/live/device/stop_listen/parameter/value` | `track device param` | — | | ### Rack chains For Instrument Racks, Effect Racks, and Drum Racks. `chain_index` is the chain position within the rack; `sub_device_index` is the device position within that chain. | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/device/get/num_chains` | `track device` | `track device int` | Number of chains | | `/live/device/get/chains/name` | `track device` | `track device string…` | Chain names | | `/live/device/get/chains/num_devices` | `track device` | `track device int…` | Devices per chain | | `/live/device/get/chains/devices/name` | `track device` | `track device string…` | Flattened names of all devices in all chains | | `/live/device/randomize_macros` | `track device` | — | Randomize all macro knobs | #### Sub-device parameter access | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/device/chain/get/parameter/value` | `track device chain sub_device param` | (all 5 indices + float) | Read a parameter inside a rack chain | | `/live/device/chain/set/parameter/value` | `track device chain sub_device param float` | — | Set it | | `/live/device/chain/start_listen/parameter/value` | `track device chain sub_device param` | — | Push value changes | | `/live/device/chain/stop_listen/parameter/value` | `track device chain sub_device param` | — | | ### Drum pads Only meaningful on a Drum Rack device. `pad_index` is 0-based within the drum pad array. | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/device/get/drum_pads/name` | `track device` | `track device string…` | Pad names | | `/live/device/get/drum_pads/note` | `track device` | `track device int…` | MIDI note per pad | | `/live/device/get/drum_pads/mute` | `track device` | `track device bool…` | Mute state per pad | | `/live/device/set/drum_pads/mute` | `track device pad_index bool` | — | Mute/unmute a pad | | `/live/device/get/drum_pads/solo` | `track device` | `track device bool…` | Solo state per pad | | `/live/device/set/drum_pads/solo` | `track device pad_index bool` | — | Solo/unsolo a pad | --- ## Scene Indexed by `scene_index`. All replies include the scene index as a prefix. ### Properties #### Read / write | Property | Type | Description | |----------|------|-------------| | `name` | string | Scene name | | `color` | int | Scene color (packed RGB) | | `color_index` | int | Scene color index | | `tempo` | float | Scene-specific BPM | | `tempo_enabled` | bool | Whether the scene overrides tempo | | `time_signature_numerator` | int | Scene-specific time signature numerator | | `time_signature_denominator` | int | Scene-specific time signature denominator | | `time_signature_enabled` | bool | Whether the scene overrides the time signature | #### Read-only | Property | Type | Description | |----------|------|-------------| | `is_empty` | bool | No clips in any slot | | `is_triggered` | bool | Scene is queued to launch | ### Methods and aggregate queries | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/scene/fire` | `scene_index` | — | Launch the scene | | `/live/scene/fire_as_selected` | `scene_index` | — | Launch and advance selection to next scene | | `/live/scene/get/num_clips` | `scene_index` | `scene_index int` | Number of occupied slots in the scene | | `/live/scene/get/clip_slots/has_clip` | `scene_index` | `scene_index bool…` | One bool per track slot | --- ## View Controls the Live UI selection state. No index required. ### Selected scene | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/view/get/selected_scene` | — | `int` | Index of the selected scene | | `/live/view/set/selected_scene` | `scene_index` | — | Select a scene | | `/live/view/start_listen/selected_scene` | — | — | Push selection changes | | `/live/view/stop_listen/selected_scene` | — | — | | ### Selected track | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/view/get/selected_track` | — | `int` | Index within `tracks + return_tracks` | | `/live/view/set/selected_track` | `track_index` | — | Select a track | | `/live/view/start_listen/selected_track` | — | — | Push selection changes | | `/live/view/stop_listen/selected_track` | — | — | | ### Selected clip / device | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/view/get/selected_clip` | — | `track_index clip_index` | Currently displayed clip in Detail view | | `/live/view/set/selected_clip` | `track_index clip_index` | — | Show a clip in the Detail view | | `/live/view/get/selected_device` | — | `track_index device_index` | Selected device on current track | ### Focus | Address | Args | Description | |---------|------|-------------| | `/live/view/show_clip_detail_view` | — | Switch Detail view to Clip | | `/live/view/show_device_detail_view` | — | Switch Detail view to Device Chain | | `/live/view/focus_browser` | — | Show the browser | | `/live/view/get/focused_document_view` | — | Reply: `"Session"` or `"Arranger"` | --- ## Application | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/application/get/version` | — | `major minor build` | Live version as three integers | | `/live/application/get/average_process_usage` | — | `float` | CPU average usage (0–1) | | `/live/application/get/peak_process_usage` | — | `float` | CPU peak usage (0–1) | --- ## Browser Content browsing and loading. No index required. ### Category listings Returns a flat list of item names at the top level of each category. | Address | Reply | Description | |---------|-------|-------------| | `/live/browser/get/audio_effects` | `string…` | Audio effect names | | `/live/browser/get/instruments` | `string…` | Instrument names | | `/live/browser/get/midi_effects` | `string…` | MIDI effect names | | `/live/browser/get/samples` | `string…` | Sample names | | `/live/browser/get/sounds` | `string…` | Sound names | | `/live/browser/get/clips` | `string…` | Clip names | | `/live/browser/get/packs` | `string…` | Pack names | | `/live/browser/get/plugins` | `string…` | Plugin names | ### Loading | Address | Args | Description | |---------|------|-------------| | `/live/browser/load_item` | `path_part [path_part …]` | Load item by hierarchical path. Each argument narrows the search one level deep. Searches all categories. | | `/live/browser/preview_item` | `path_part [path_part …]` | Preview item without loading. | | `/live/browser/stop_preview` | — | Stop audio preview. | **Example — load a Wavetable preset:** ``` /live/browser/load_item "Instruments" "Wavetable" "Bass" "Dark Bass.adv" ``` ### Hotswap | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/browser/get/hotswap_target` | — | `string` | Name of the current hotswap target device, or `""` | | `/live/browser/begin_hotswap` | `track_index device_index` | — | Set hotswap target to a specific device | --- ## Groove Controls the global groove pool. | Address | Args | Reply | Description | |---------|------|-------|-------------| | `/live/groove/get/grooves` | — | `string…` | Names of all grooves in the pool | | `/live/groove/get/amount` | `groove_name` | `groove_name float` | Groove intensity (0–1) | | `/live/groove/set/amount` | `groove_name float` | — | Set groove intensity | Assign a groove to a specific clip via `/live/clip/set/groove` and `/live/clip/get/groove`. --- ## Development setup ```sh # Create the venv (already committed to .venv/ location) python -m venv .venv # Activate (Windows PowerShell) .venv\Scripts\Activate.ps1 # Activate (macOS / Linux) source .venv/bin/activate # Install dev tools (python-osc for testing, pytest) pip install -e ".[dev]" ``` The runtime code (`AbletonOSC/`) has no external dependencies and runs as-is inside Ableton's embedded Python 3.x. --- ## Architecture ``` AbletonOSC/ ├── __init__.py create_instance() — Ableton's entry point ├── manager.py Manager(ControlSurface) — owns OSC server and all handlers ├── osc_server.py Pure-Python UDP OSC encode/decode; non-blocking poll ├── handler.py AbletonOSCHandler base class ├── song.py SongHandler /live/song/* ├── track.py TrackHandler /live/track/* ├── return_track.py ReturnTrackHandler /live/return_track/* ├── clip.py ClipHandler /live/clip/* ├── clip_slot.py ClipSlotHandler /live/clip_slot/* ├── device.py DeviceHandler /live/device/* /live/device/chain/* ├── scene.py SceneHandler /live/scene/* ├── view.py ViewHandler /live/view/* ├── application.py ApplicationHandler /live/application/* ├── browser.py BrowserHandler /live/browser/* └── groove.py GrooveHandler /live/groove/* ``` ### Tick loop Ableton's embedded Python does not support threading. The OSC server uses a non-blocking UDP socket and is polled by overriding `ControlSurface.update_display()`, which Live calls approximately every 100 ms. ### Listener lifecycle Each `start_listen` call stores `(remove_fn, callback)` in `handler._listener_store` keyed by a string like `"track.2.mute"`. `clear_listeners()` iterates the store and detaches everything — called automatically on script disconnect and on `/live/api/reload`. ### Handler base class `AbletonOSCHandler` provides: - `_add(address, callback)` — register an OSC handler - `_send(address, args)` — push an unsolicited message to the last-seen client - `_register_listener(key, target, prop, notify_address, prefix)` — attach a Live listener that calls `add__listener()` and pushes the value on change - `_remove_listener(key)` — detach a specific listener - `clear_listeners()` — detach all listeners registered by this handler ### Adding a new handler 1. Create `AbletonOSC/my_handler.py` subclassing `AbletonOSCHandler`. 2. Override `init_api()` and call `self._add(address, callback)` for each endpoint. 3. Import and instantiate in `manager.py`'s `_setup_handlers()`.