| get_sessionA | Survey the running set: script handshake, song fields, tracks, clips, devices. The call to make first. It proves the Remote Script is answering before anything
else is attempted, and it hands back the track and scene indices every other
tool here is addressed by.
Returns:
Dictionary with script handshake info, song fields, the track list with its
indices, group track indices, and clip and device detail where asked for.
Note:
The round-trip handshake takes about 450 ms, so this is the expensive read. Take
it once at the start and narrow with get_track, get_clip and get_devices
afterwards rather than calling this again between edits. Group tracks have no arm
state ('armed' returns null). If Live collection introspection reports zero
tracks, index probing is used as a fallback.
|
| get_trackA | Read one track's mixer state, flags, device chain, sends and clip slots. One track in one round trip, where get_session covers the whole set. Use this to
read a mixer before writing it with set_mix, and get_devices when the device
chain is the only thing wanted.
Returns:
Dictionary with the mixer fields, the arm state and whether arming applies,
the device names and count, the send values, the clip slot count and the
indices of the filled ones, and a per-collection flag saying whether each
count is exact.
Note:
``volume`` and ``panning`` are normalised, not dB: 0.85 is 0 dB (measured),
and the scale is not linear. ``display`` carries the dB reading where Live
offered one.
``arm`` is read only after ``can_be_armed`` says the question applies,
because reading it on a group track raises inside Live and would break the
whole batch (measured). It comes back as ``null`` where it does not apply.
Device names, send values and which clip slots are filled are measured by
probing indices in one batch, because Live's collections report no length.
Read ``counts_exact`` before treating any of those counts as complete.
|
| get_clipA | Read one Session clip's properties: length, loop, warp, pitch, markers, flags. Returns:
Dictionary with ``has_clip``, and where that is true the clip path, whether
it is MIDI, and the property fields. An empty slot answers ``has_clip:
false`` and says so rather than failing.
Note:
Notes and automation are not properties: use ``read_clip_notes`` and
``read_automation``.
``warping``, ``warp_mode`` and ``pitch_coarse`` are reachable only through a
generic path. Each was read, written, read back and restored on 2026-08-29
against Live 12.4.5, and all three rows are ``verified``. ``pitch_fine`` is
read-verified only, and the integer mapping behind ``warp_mode`` is still a
hypothesis: confirm it against ``clip.available_warp_modes``.
**An audio clip's times are in seconds while warping is off, and in beats while
it is on.** ``times_in`` says which, and the per-field ``unit`` follows it. A MIDI
clip is always in beats. Measured 2026-09-19 against Live 12.4.6 on three
unwarped clips of one 228.792167 s file: ``loop_end`` read 228.79216666666667 on
all three, and a write of a larger ``end_marker`` clamped to that same number.
**Toggling ``warping`` rescales the stored markers and does not undo it.** On the
same clip, turning warping on took ``end_marker`` from 240.918 to 253.682, a
factor of 1.05298, and turning it back off left it at 253.682 while ``loop_end``
returned to the file length. The factor is the clip's own warp tempo over 60
(1.05298 is 63.18 BPM; another clip moved 0.5704 to 1.1408 at 120 BPM and not at
all at 60). Every toggle multiplies again, so a clip toggled twice carries markers
well past the end of its own file and nothing reports it. Read the markers after
any write to ``warping`` and set them back deliberately.
|
| get_devicesA | List a track's device chain: names, class names, on/off, parameter counts. Reads the chain and changes nothing in it. The device indices it reports are the
ones set_parameter, delete_device and ``song.move_device`` are addressed by.
Returns:
Dictionary with the device count and one entry per device carrying its index,
name, class name, on/off state, parameter count and ``configure_needed`` flag.
A track with an empty chain answers ``device_count: 0`` rather than an error,
while an index with no track behind it is refused with ``index_out_of_range``.
Note:
Read the parameter count first. A third-party plug-in reporting exactly one
parameter has not been configured, which the ``configure_needed`` flag says
outright. That is a limit in Live, not in this server, and only Live's GUI
lifts it. The probe behind that flag settles one question, whether the count
is exactly one or more than one, so where it could not settle it the count
arrives as ``parameter_count_at_least`` instead.
For the parameters themselves, with their ranges and display units, call
``describe`` with ``with_parameters=True``: this tool counts them and does
not list them. For the mixer, sends and clip slots on the same track, call
get_track instead of adding a second call here.
|
| describeA | Introspect any Live object: class, properties, children, and methods. The route to the dynamic surface: what a loaded plug-in actually exposes, which
no catalog can know in advance because it is decided at runtime and, for
third-party plug-ins, by what the user picked up in Configure mode.
Returns:
Dictionary with the object class, its properties, its child collections, the
methods the allowlist permits, and the parameter survey where asked for.
Note:
Prefer this over lom_describe for a device, and prefer lom_describe for
anything else. The difference is ``with_parameters``: lom_describe reports a
device's parameters as a count, and a count is what a Live ``Vector`` that
refuses ``len()`` fails to give, so a plug-in with parameters can be reported
as having none. Measured against Live 12.4.5. When that happens this tool
falls back to probing parameter indices, reports the names it found, and says
which of the two answers you are looking at.
Methods are never callable through a path. Take the name from here and invoke
it through lom_call, which accepts only names on the Remote Script's own
allowlist. For a device parameter, set it with set_parameter rather than
writing the path by hand.
|
| create_trackA | Insert a MIDI, audio, or return track and return its resolved index. Returns:
Dictionary with the resolved index of the new track, its kind and name, and
the track count before and after.
Note:
Inserting at index N shifts the track that was at N, and every track after it, up
by one. Every track index held from before the call is then wrong, including ones
in a plan not yet executed. Append with -1 where the position does not matter, and
otherwise take the resolved index out of this answer rather than assuming it.
Creating a track does not put an instrument on it. Select it and call
load_device for that, then create_clip for something to play.
|
| load_deviceA | Search Live's browser and load a device onto a track: a search step, then a load step. Returns:
Dictionary of search candidates and the selected track on the search step,
or the load result and the resulting device chain on the load step.
Note:
A load cannot be aimed inside a rack: measured 2026-09-07 against Live 12.4.5,
``rack.view.selected_chain`` read back as written and the device still landed on the
track. An effect is appended; an instrument replaces the one already there and
discards it silently, with its settings and the clip envelopes pointing at it: check
with get_devices first. Loading plug-ins in rapid succession can crash Live
(measured: a batch load during a plug-in rescan with a licence dialog open took Live
down), so load one at a time. A load also arms the target track and disarms every
other one (measured 2026-09-19 against Live 12.4.6: of two armed tracks and an
unarmed target, the target came back armed and the others disarmed), and nothing
here reports it. An instrument load renames a track that still has Live's default
name; an effect never renames.
Success means the call ran, not that the track sounds: a measured Drum Rack load
reported success on a silent track, and an empty rack is what that looks like, so
read ``song.tracks[N].devices[i].chains``, which get_devices does not report. A null
``selected_track_same``, ``displaced`` or ``configure_needed`` means the comparison
failed, not ``false``; the first compares name and track count, not identity.
``loaded_device_index`` is the position of the new device, computed only where
the two chains single out exactly one, and null otherwise.
``status: incomplete`` with no candidates means the walk stopped early, not that the
item is absent: retry ``unreached_roots``; only ``complete`` rules it out. The main
track has no index: write ``song.view.selected_track`` with ``{"__path__":
"song.master_track"}`` through lom_set, then load.
|
| create_clipA | Create an empty MIDI clip of a given length in a Session slot. The target must be a MIDI track. An audio slot takes a sample instead, which is a
different call: lom_call with path='song.tracks[N].clip_slots[M]',
method='create_audio_clip' and one absolute file path (catalog row
clip_slot.create_audio_clip).
Returns:
Dictionary with the creation status, the clip path, and the read-back length.
Note:
An occupied slot is refused rather than overwritten, so nothing is lost by
aiming at the wrong slot. To replace a clip, delete_clip first.
The clip arrives empty. Fill it with write_clip_notes, and copy it onto the
Arrangement timeline with arrange once it holds something.
Live gives a new Session clip a loop: ``looping`` reads back true with
``loop_start`` at 0 and ``loop_end`` at the length asked for. That is Live's
own default for Session clips and this does not set it. Turn it off through
``lom_set`` on ``clip.looping`` where the part is to play once.
|
| delete_clipA | Delete the clip in a Session slot, its notes and envelopes with it. Requires confirm=True. With confirm=False it reports the clip name, length and
note count that the call would remove, and changes nothing.
Returns:
Dictionary reporting the deletion, or the loss report when unconfirmed.
Note:
This empties one Session slot. Slot indices do not shift, so nothing else
moves and no other index goes stale.
Emptying a clip is not the same as deleting it: write_clip_notes with an
empty list and mode='replace' leaves the clip in place with its length,
loop and envelopes intact. Prefer that where the slot should stay filled.
A clip already copied into the Arrangement by arrange is a separate clip and
survives this.
|
| delete_trackA | Delete a track with everything on it. Requires confirm=True. The widest deletion here: the devices, every Session clip and every Arrangement
clip on that track go with it. With confirm=False it reports the track name, the
devices, the filled Session slots and the Arrangement clip count the call would
remove, and changes nothing.
Returns:
Dictionary reporting the deletion, or the loss report when unconfirmed.
Note:
Deleting shifts every later track down one index, so any track index read
before this call is wrong afterwards. Re-read with get_session, and where
several tracks are going, delete from the highest index downwards so the
earlier indices stay valid.
For something narrower, delete_clip empties one slot and delete_device takes
one device out of the chain, neither of which renumbers anything.
|
| delete_deviceA | Delete one device from a track chain, its settings with it. Requires confirm=True. With confirm=False it reports the name of the track, and
the device's name, class and parameter count, and changes nothing.
Returns:
Dictionary reporting deletion status or pending loss report.
Note:
The track is named in the report because a device name does not identify
one. A device class like Utility or EQ Eight sits on several tracks in a
normal set, so a loss report naming only the device reads the same whether
the index points at the intended chain or at a neighbour's. Check the track
name against the one you meant before confirming.
Deleting shifts every later device in the chain down one index, so a second
delete aimed at an index read before the first one lands on a different
device. Read the chain again with get_devices between deletes.
Two neighbouring intentions are not this tool. To reorder a chain nothing
needs deleting: call ``song.move_device`` through lom_call. To swap the
instrument on a track, load the replacement with load_device, which replaces
the instrument already there in one step (measured). Deleting first only
loses the settings earlier.
``kind`` reaches the return and main chains, which are addressed the same way
get_devices addresses them. A return track carries its own index and the main
track has none, so ``track`` is not read at all when kind is master.
|
| write_clip_notesA | Write MIDI notes into a Session clip. Returns:
Dictionary containing write confirmation, validation reports, and optional diff.
Note:
Times are in beats and clip-local, so beat 0 is the clip's own start. ``replace``
(the default) makes the clip match the list given; Live's own note writing appends,
so without it a second write duplicates a melody (measured: 63 + 23 = 86 notes). Ask
for ``append`` by name to layer onto an existing performance. An empty list with
``replace`` empties the clip but keeps its length, loop and envelopes; delete_clip
takes all of them.
A list straight from ``read_clip_notes`` can be written back: the keys in
:data:`~live_maestro.music.notes.TOLERATED_NOTE_KEYS`, such as Live's ``note_id``,
are dropped and reported as ``input_keys_ignored``, and every other unrecognised key
is an error. ``pitch``, ``start_time`` and ``duration`` are never defaulted, so a
list spelled with ``pos``/``dur`` is refused before anything is sent instead of
becoming sixteenths stacked on beat 0.
Times and durations do not come back bit-identical. They return with a deviation in
both directions, about 4e-7 relative and reproducible to every digit across runs: a
sent 0.29 reads back as 0.29000010406260407, a sent 0.18 as 0.17999994796869798 and
a ``start_time`` of 2.29 as 2.290000104062604, while a duration of 0.5 comes back
exactly. The cause is not established: it is neither a tick grid of 96, 192, 480
or 960 per quarter, nor a single float32 conversion (float32 of 0.29 is
0.28999999). At 124 BPM the error is around
50 nanoseconds, so it matters only for comparison: never test a note time for
equality. The diff run here uses a tolerance, which is why it reports ``0 changed``
for values that differ in the seventh decimal.
|
| read_clip_notesA | Read MIDI notes of a Session clip, query a specific window, or count notes. Returns:
Dictionary containing notes, count, window parameters, and validation report.
Note:
Times and positions are clip-local beats. Muted notes are included and carry ``mute:
true``.
A single drum clip of 384 notes comes back as 57k characters, past the tool-result
cap, and ``Clip`` has no ``note_count`` in the LOM, so counting from out here
otherwise means transferring every note. ``count_only=True`` carries back one
integer instead. ``pitch_min`` and ``pitch_max`` narrow the read the way time does,
which is what makes a question like "what sits below this guitar's lowest string"
answerable on a clip too large to read whole. ``get_notes_extended`` through
``lom_call`` takes a pitch window too, but answers with opaque note handles that can
be counted and not read. With ``count_only`` there is nothing to validate, so
``check`` is ignored; with a window, ``check`` sees the window and not the clip.
Returned notes are sorted by time, then pitch. Live hands them back ordered by
pitch, which is the one order a musical instruction never means: four notes written
at beats 0, 1, 2, 3 with pitches 72, 60, 67, 62 come back 60, 62, 67, 72, so taking
every other entry off the raw list picks alternating pitches rather than alternating
beats, silently and plausibly. For Live's own order call ``get_notes_extended``
through ``lom_call``.
|
| quantize_clipA | Quantize MIDI clip note positions and lengths to a specified beat grid. Reads notes, calculates quantized positions, writes back using mode='replace',
and verifies the result.
Returns:
Dictionary with the write result, the number of notes moved, and how far.
Note:
Parameter interactions: ``strength`` scales note displacement towards ``grid`` from
0.0 (raw timing preserved) to 1.0 (full snap). Use partial strength (e.g. 0.5) to
tighten timing while preserving human groove feel. Setting ``quantize_ends=True``
snaps note ends to the grid too, adjusting note lengths. In contrast,
``quantize_ends=False`` shifts note onsets only and preserves articulation.
Quantising modifies notes in place and is not undoable. Read the notes first with
read_clip_notes if the original unquantized timing might be needed later.
To shift pitch instead of timing, use transpose_clip. To write custom or micro-timed
notes off-grid, use write_clip_notes.
|
| transpose_clipA | Transpose a MIDI clip by note pitch or an audio clip via pitch_coarse. Relative on both paths, so calling it twice with 2 moves the clip by 4 and there
is no absolute target to aim at. A MIDI clip has every note pitch rewritten. An
audio clip is repitched as a whole instead.
Returns:
Dictionary reporting the transposition, the notes affected, and any that hit
the range limit.
Note:
When to use: Shift the overall pitch of an entire clip by a constant semitone offset.
When NOT to use: Do not use transpose_clip to edit melody contours, re-voice chords,
or correct scale degrees. Use write_clip_notes instead. Do not use it for timing or
groove changes, which are handled by quantize_clip.
An audio clip is transposed by writing clip.pitch_coarse. The write is relative
there too, because the current value is read first and the semitones added to it,
so repeated calls do keep climbing. Live documents the bound as -48 to 48 but that
figure is not from a probe here. The read-back in the answer is the evidence for
any one write.
``out_of_range='clamp'`` and ``'drop'`` both lose information that
transposing back will not return: clamped notes have collapsed onto one
pitch and dropped ones are gone. Keep the default 'error' unless the clip
has been read out with read_clip_notes first.
|
| set_mixA | Set volume, pan, sends, mute and solo on one track in a single round trip. Every argument is optional and only the ones given are written, so this is the
cheap way to change several mixer controls at once. Read the current state with
get_track first, since the values here are absolute and not relative.
Returns:
Dictionary reporting each write with its before and after value, and the dB
display where Live offered one.
Note:
Volume and pan are normalised, so 0.85 is 0 dB (measured) rather than 85 per
cent of anything. Read ``display`` in the answer for Live's own dB reading
instead of converting the normalised number yourself.
The master track has no mute and no solo. Both are refused there rather than
silently ignored, and ``track`` is not read at all when kind is master.
This is the mixer only. For a knob on a device in the chain use
set_parameter, and for the tempo use set_tempo. To make a mixer control move
over time rather than jump, write an envelope with write_automation.
|
| set_parameterA | Set one device parameter by index or name and verify the read-back. Returns:
Dictionary reporting the write, the parameter index it resolved to, the
read-back value, and the display string where the device offered one.
Note:
Parameter values are normalised, not the unit the device displays. Read ``min``
and ``max`` (``describe`` with ``with_parameters=True``) rather than assuming
a range.
The curve from 0..1 onto the displayed unit is not linear and differs per
device. On one third-party compressor Attack is ``v^4 * 1000 ms``, so writing
"10 ms" linearly lands at 316 ms, a factor of 30, silently. Where the device
reports a display, aim with the display. Where it does not (all VST2), write
normalised and calibrate by eye once.
A parameter that shows a display also answers ``str_for_value``: lom_call with the
parameter path, ``method='str_for_value'`` and one value returns the string that
value would display, and moves nothing. That maps a unit onto the scale without
deriving the taper by trial (catalog row param.str_for_value).
A quantized parameter takes discrete steps, so a written 0.5 can legitimately read
back as something else, and that is reported as a clamp rather than as a failure.
|
| set_parameter_displayA | Set a device parameter by the reading it should show, not by its raw value. Returns:
Dictionary reporting the value written, the display it produced, the residual
error against the target, the unit, and how many probes and round trips it
took. On refusal, why - including the case where the device reports no units.
Note:
The inverse of ``set_parameter``, and there is no formula to invert: the curve from
the raw range onto the displayed unit differs per parameter and is rarely linear. On
one measured third-party compressor Attack is ``v^4 * 1000 ms``, Ratio is ``20^v``
and Threshold about ``40 * log10(v)``, so writing 10 at a parameter that displays
milliseconds lands at 316 ms, a factor of 30, and reports success.
The taper is sampled, not computed: one batch of probes asks what each raw value
would read as, the answers are bracketed around the target, a second batch refines
inside that bracket, and only then is a single value written and read back. Two
round trips, and nothing in the set moves until that last step. Probing with real
writes has been measured leaving Delay Feedback at 100 % (self-oscillation) and
Saturator Drive at 100 % across 20 parameters (catalog row
return_device.str_for_value); the probes here write nothing. They also stay strictly
inside the range and never ask at ``min`` or ``max``, where a plug-in's own
formatting code has crashed Live in native code no try/except reaches
(docs/limits.md, 'Asking for a display string at an endpoint (Live)').
Where the device reports no unit this refuses instead of aiming: every VST2 reports
a bare number (0 of 36 measured), so a display-driven search there would walk the
control to its end stop and call it a result. Use ``set_parameter`` with a
normalised value for those and calibrate by ear once. The result is the stored value
and its display, which is not audibility: a parameter on a device that is switched
off reads back exactly the same.
|
| write_automationA | Write an automation envelope into a Session clip and verify the read-back. Turns a handful of breakpoints into a written curve, then reads it back so the
result reports the stored envelope rather than the requested one.
Returns:
Dictionary reporting the write, how many points were laid down, and the
comparison between the stored curve and the generated one.
Note:
Automation lives in Session clips. There is no way to write an Arrangement
envelope directly here: write it into the Session clip and then copy the clip
onto the timeline with arrange, which carries the envelope with it.
A verified write proves the stored curve, never that it is audible. An
envelope on a parameter of a device that is switched off, or on a muted
track, reads back exactly as written and changes nothing anyone can hear.
With ``clear_first=False`` a repeated call layers points onto the ones
already there, so a retry after an unclear result can leave a curve that
matches neither attempt. Read it with read_automation before writing again.
To explicitly clear an envelope without writing new points, use clear_automation.
Playing the clip leaves the parameter at the value this curve last reached, and
stopping does not restore it. Capture the statics you care about before the first
playthrough: once a curve has run, nothing reports the value it covered.
|
| read_automationA | Sample a clip envelope into a list of values and report what it found. Reads the curve by evaluating it at ``points`` positions, so the answer is a
sampling of the envelope rather than the breakpoints that define it.
Returns:
Dictionary with the sampled values, the beat range they cover, the value
range they span, and flags saying whether an envelope exists at all.
Note:
Automation lives in Session clips, so an Arrangement clip has none to read.
als_read is the way to see breakpoints in a saved project instead.
Use write_automation to lay a curve down and clear_automation to remove one.
A flat result with an envelope flag of false means nothing is automated on
that parameter, which is not the same as an envelope holding the parameter's
current value.
|
| clear_automationA | Delete automation envelopes on a Session clip. Requires confirm=True. With confirm=False it changes nothing and reports the envelope it would remove: its
range, where the low and the high point sit, and how densely it was sampled to find
them. That report covers one named parameter; with all_envelopes it says only whether
the clip is automated at all, because has_envelopes is one flag for the whole clip. To
see which parameters those envelopes belong to, read clip.automation_envelopes with
lom_get and then each automation_envelopes[i].parameter.name.
Returns:
Dictionary reporting the clearance, or the envelopes that would go.
Note:
A cleared envelope is gone, and the parameter falls back to whatever value it holds
outside the clip rather than to the first breakpoint. Sample it with read_automation
first if the shape might be wanted back, since nothing here restores it.
The dry run samples the curve rather than reading its breakpoints, so a feature
narrower than the reported sampling step can still sit between two samples. Where
the exact shape matters, read it with read_automation at a resolution you choose, or
read the breakpoints out of the saved file with als_read.
To change a curve rather than remove it, write_automation with ``clear_first=True``
replaces it in one call and needs no clearing first.
|
| arrangeA | Copy Session clips onto the Arrangement timeline at beat positions. Supports two modes:
1. Single placement: specify track, slot, at_beat (and optionally to_track).
2. Batch placements: pass a placements list to duplicate several clips and verify
each one against the destination track it landed on.
A copy, not a move: the Session clip stays in its slot and the two are separate
clips afterwards, so editing one leaves the other alone. This is how a Session
idea becomes an arrangement, and how clip automation written by write_automation
reaches the Arrangement timeline.
Returns:
For single placement: dictionary with placement status, destination track,
clip counts before and after, the beat the clip actually landed on, and the
clip itself under placed_clip. Both modes identify a placement by comparing the
destination track before and after, so a clip that was already on the requested
beat never stands in for one that did not land.
For placements list: dictionary with per-placement requested_beat, actual_beat,
ok, and summary counts.
Note:
Call it once per placement. Repeating the same call adds another copy at the
same beat rather than replacing the first, so a retry after an unclear result
needs the clip count in this answer checked first.
A list retries worse than a single call, not better: a list that half landed
doubles what landed if it is sent again. 'destinations' carries the clip count
per destination track before and after, which is the number to check first.
Use create_clip and write_clip_notes to build the source clip, and
set_arrangement_time then play to hear where it landed.
|
| set_locatorA | Place or remove an Arrangement cue locator. Requires confirm=True. This toggles, and that is the whole difficulty with it. Live offers one call,
``song.set_or_delete_cue``, which adds a locator where none exists at that beat
and deletes the one that does, so the same arguments twice leave nothing behind.
Call with confirm=False first to see which of the two would happen.
Returns:
Dictionary reporting the toggle, and the cue points and their count after it.
Note:
A locator marks a position for a human reading the Arrangement. It does not
move the playhead. Use set_arrangement_time to move that, and set_loop to
mark a region rather than a point.
|
| set_arrangement_timeA | Move the Arrangement playhead to a beat and read the position back. Writes ``song.current_song_time``. This moves where playback would start. It
does not start it. Use play to begin, or play with ``from_beat`` to do both in
one call.
Returns:
Dictionary with the write confirmation and the read-back song time.
Note:
Writing ``song.current_song_time`` is bounded by ``song.song_length``;
attempting to set a position past ``song_length`` fails with Live error
``"Cannot set the Songtime behind the Songlength"``. Transport playback
in Session view advances freely past ``song_length``.
Do not sample a moving transport through lom_batch. A read that follows a
jump inside the same batch still reports the position from before the move,
and it reports it without an error (see lom_batch). One call per position.
|
| playA | Start transport playback and read back the is_playing status. Returns:
Dictionary reporting the start call and the read-back is_playing flag.
Note:
The read-back proves the transport is running, not that anything is audible:
a muted track, an empty Arrangement or a track with no instrument all play
silently. Use stop to halt, and set_arrangement_time to move the playhead
without starting.
Leave the transport as it was found. Nothing here stops it on its own, and a
set left playing keeps playing for whoever is at the machine.
A Session clip that has played leaves its track in Session view, and the
track then ignores the Arrangement until Live's Back to Arrangement button
is pressed. ``song.back_to_arranger`` reports that state and clears it
when written false (applied asynchronously, taking up to 2 read-back
attempts for ``read_back: "applied"``).
|
| stopA | Stop transport playback, and optionally every playing Session clip. Returns:
Dictionary reporting the transport stop and, where asked for, the clip stop.
Note:
With ``quantized=True`` the clips stop on the next grid point rather than at
once, so an immediate read can still show a clip playing without anything
having gone wrong. Use play to resume playback, or set_arrangement_time(at_beat=0)
to rewind the Arrangement timeline to the beginning.
Stopping does not restore a parameter a clip envelope was driving. The envelope
leaves the parameter at its last reached value and Live keeps no static baseline
underneath to restore.
|
| set_tempoA | Set the global song tempo in BPM and read back the stored value. One tempo governs the whole set, Session and Arrangement alike. There is no
per-track tempo. The one other place a tempo can come from is a scene, below.
Returns:
Dictionary reporting the write and the read-back tempo.
Note:
A tempo set here does not survive every scene launch. Firing a scene whose
``tempo_enabled`` is on writes ``song.tempo`` again from ``scene.tempo``, so
read ``song.tempo`` back after a launch rather than assuming this one held
(catalog rows ``scene.tempo`` and ``scene.tempo_enabled``, both read verified
2026-08-29 against Live 12.4.5).
BPM is a real unit, which makes this tool the exception among the setters. A
device parameter is normalised over whatever range the device declares: reach
those with set_parameter and read ``min`` and ``max`` through ``describe``
rather than assuming 0..1 (docs/limits.md). Volume, pan and sends are
normalised the same way, and set_mix carries them.
|
| set_loopA | Move the Arrangement loop brace, or switch looping on and off. Every argument is optional and only the ones given are written, so the brace can
be moved without touching the switch and the reverse.
Returns:
Dictionary reporting how many writes were made and the before and after
values for each of enabled, loop_start and loop_length.
Note:
The brace only loops the Arrangement transport. It has no bearing on Session
clip looping, which is a clip property. Read that with get_clip. ``length``
is a duration and not an end position.
Calling with no arguments at all writes nothing and reports the current
brace, which is the cheap way to read it.
|
| als_readA | Read a saved Ableton project (.als) or rack (.adg) from disk. Returns:
Dictionary containing project metadata, track list, devices, and automation structure.
Note:
This is Channel B, a peer of the live connection rather than a fallback. It
answers two questions the LOM never will: what is in someone else's project, and
where an envelope's breakpoints actually sit.
Automation lives in two places and the LOM can read only one of them. Measured
over the 174-project corpus (docs/limits.md section 9): 52 (30 %) have clip
envelopes but 159 (91 %) have
track automation, and a tool that counted clip envelopes alone reported 110 of
those projects as unautomated. The two layers are reported separately here and
are never added together.
|
| als_writeA | Edit a saved .als project file on disk. Requires confirm=True. The target file must not be the one Live currently holds: Live writes its own memory
over the file when it next saves, without a conflict warning, and the edit is gone. Live
running on a different set is fine.
Returns:
Dictionary reporting write status, changes made, and verification report.
Note:
``sidechain`` and ``configure`` reach what the LOM cannot; for the set open in Live
use ``lom_set``, ``set_parameter`` or ``set_mix`` instead. Measured across the
174-project corpus (docs/limits.md section 9), 74 of 82 projects in the house/trance
BPM window use sidechain compression, median 6 wirings each.
``configure`` fills a plug-in's parameter strip whatever the strip currently holds.
Two parameters written into an instance whose strip held nothing came back on reopen
with their real values, writable through ``lom_set`` with ``read_back: applied``.
Live allocates 128 slots per instance and leaves them all in the file, so nothing is
inserted: three fields of an existing element are filled.
``attribute`` sets one attribute anywhere in the file, addressed by an ElementTree
expression. An expression matching more than one element is refused unless ``index``
picks one, and ``index`` counts the matches of that expression rather than tracks in
the project: ``.//MidiTrack/Name/EffectiveName`` does not see the audio tracks at
all. ``als_read`` with ``locate='tempo'`` or ``locate='track_names'`` answers with
the expression, attribute and index for those fields; otherwise the expression is
built by hand, and ``confirm=False`` resolves it against the file and reports what
it selects.
|
| lom_getA | Read one property value by LOM path, with its catalogued meaning. The single-property read behind every getter here, for the field no getter
exposes. Where the catalog knows the path, the answer carries a ``catalog``
hint, and where it knows that a particular value is a sentinel it carries
``means`` as well: a ``scene.tempo`` of -1.0 reads back as "no tempo set for
this scene" rather than as a tempo below the legal minimum.
Returns:
Dictionary with path, value, type, an optional display string for a device
parameter, and the catalog hint and meaning where those exist.
Note:
A path that is not rooted at song, app or song.view is refused here without
Live being contacted. A path Live rejects comes back as ``ok: false`` with
Live's own ``code``, so a missing property is an error rather than a null.
Reach for a getter first where one exists: get_session for the whole set,
get_track, get_clip and get_devices for one of each, read_clip_notes for
notes, read_automation for envelopes. All of them cost one round trip for
many fields, where this costs one per field. Use lom_batch to read several
paths in a single trip, lom_describe when the property names are not known
yet, and lom_set to write.
|
| lom_setA | Write a property value by LOM path and return verification read-back. The generic write, and the unvalidated one: set_parameter, set_mix and set_tempo check
the catalog first. lom_call takes a method, lom_batch several writes in one round trip.
Returns:
Dictionary reporting requested, before, after, clamped, and changed status.
For a device parameter it also carries ``display`` and ``is_quantized``.
Note:
An out-of-range value is refused out loud and nothing is stored: ``...volume.value =
1.4`` answers ``live_error "Invalid value. Check the parameters range with
min/max"`` and leaves 0.85 standing, as do tempo 5000 and panning 5. What the
read-back catches is quantised snap: ``<Compressor>.parameters[10].value = 0.4``
(Model: Peak / RMS / Expand) stored 0 and reported ``clamped: true, read_back:
"clamped", is_quantized: true, display: "Peak"``, so a value between two steps
silently becomes another. ``is_quantized`` is what separates a snap to a step
from a clamp to a range.
A write can apply asynchronously, leaving ``read_back`` at ``not_observed``, which
is neither ``clamped`` nor a failure; a method call has no read-back at all; and an
unknown property name is ignored and reported as success, so check ``changed``
rather than ``ok``. A collection like ``song.tracks`` cannot be assigned: set a
property on an element instead.
A Live object cannot travel as a plain value. Where a property holds one
(``song.view.selected_track``, ``song.view.selected_scene``,
``song.view.detail_clip``, ``browser.hotswap_target``, ``clip.groove``, and on a
device ``input_routing_type`` and ``input_routing_channel``) pass
``value={"__path__": "song.tracks[2]"}`` (protocol section 5.4); handed plain JSON
the first five answer ``not_settable``. The two routing properties take their
reference out of ``<device>.available_input_routing_types`` and
``...available_input_routing_channels``, never a name string.
|
| lom_callA | Invoke an allowlisted method on a Live object. The escape hatch for the calls that have no tool of their own, such as
``song.move_device`` for reordering a device chain. What it can reach is bounded
by the Remote Script's allowlist, not by this signature.
Returns:
Dictionary reporting the call and its return value. A refused call names the
reason, and where a dedicated tool covers the same ground it comes back with
``use_instead`` naming that tool.
Note:
A method call has no read-back, so nothing here proves the effect the way a
write through lom_set or set_parameter does. Follow it with the matching read
(get_devices after a move, get_session after a structural change) rather than
treating a successful return as evidence.
Prefer the dedicated tool wherever one exists: create_clip, delete_track,
set_tempo, play and stop all verify what this cannot. Use lom_get and lom_set
for properties, which are not reachable as methods, and lom_batch to send
several calls in one round trip.
|
| lom_batchA | Execute multiple raw LOM operations (get, set, call) in a single round trip. Returns:
Dictionary containing ordered results for each operation.
Note:
A batch runs inside one handler call, synchronously on Live's main thread, and is
capped at 1000 operations (max_ops=1000): a larger one is rejected upfront and never
truncated. Measured 2026-09-16 against Live 12.4.5 (macOS 26.6.2, four-track set):
one request costs about 400 ms whatever its size, with 200 ops at 500 ms, 1600 mixed
reads at 1506 ms and 3200 song.tempo gets at 1718 ms, error_count 0 throughout; past
the 200-op point the marginal cost is about 0.72 ms per mixed real read and 0.44 ms
per song.tempo get. Write and call ops were not measured and cost more.
Nothing Live recomputes between operations is visible to a later operation in the
same batch: a read after a transport jump still reports the position from before the
move, and four jumps interleaved with four reads returned four identical pre-jump
values and no error. That is the failure shape to expect here. It does not raise. It
returns a clean set of numbers that look like a measurement of a parameter which
never changes, and the conclusion drawn from them is wrong. Sample a moving
transport with one call per position, never inside a batch.
An end_marker write on an Arrangement clip is refused here rather than sent.
Observed once, 2026-09-10 against Live 12.4.5: the write was accepted without an
error and end_time did not move; what it read back was never recorded, and
docs/limits.md section 5 carries the probe that would settle it. ``atomic`` stops at
the first error but undoes nothing, so read the state back after a partial failure.
Prefer a dedicated tool where one exists: get_session, get_track, get_devices and
set_mix batch their own reads and verify, which this does not.
|
| lom_describeA | Reflect on any LOM object: class, properties, children, callable methods. The raw reflection, reporting what the object itself declares. Use it to find
the property names on an object the catalog does not cover, then read them with
lom_get and write them with lom_set.
Returns:
Dictionary reporting object class, properties, child collections with their
counts, and the methods the Remote Script allowlist will let you call.
Note:
For a device, prefer ``describe`` with ``with_parameters=True``. This tool
reports a device's ``parameters`` child as a count, and a count is exactly
what a Live ``Vector`` that refuses ``len()`` fails to give, so a plug-in
with parameters can be reported as having none. ``describe`` falls back to
probing indices when that happens and says which answer it is handing back.
The methods listed here are names, not calls: invoke one through lom_call.
|
| lom_enumsA | Look up the integer behind a named Live enum member. Live stores an enum property as a bare integer, so a write needs the number and
a read hands back a number that means nothing on its own. This is the lookup in
both directions.
Returns:
Dictionary of member names against their integer values, or the list of enum
type names when ``type_name`` is empty.
Note:
Call this before writing an enum-valued property through lom_set or
set_parameter, rather than guessing that the members run in the order Live
draws them. This answers what a type's members are. lom_describe answers
what properties an object has, and neither answers the other.
|
| find_pathA | Find the mechanism that serves an intent, and the catalog rows when rows serve it. Returns:
Dictionary with the chosen route, any matching rows with their paths and docs,
and the next call to make when rows are not the answer.
Note:
There are four routes, and which one comes back matters more than the rows do.
``catalog_row`` means the rows carry the answer: each hit gives the path template to
fill in, the access verbs, the full doc, and ``scopes``, the other places the same
property exists, since track, return, master and chain mirror one another and a clip
row usually has an arrangement_clip twin. ``writes`` appears when the best row is a
read-only parameter object and names the sibling that actually takes a value.
``device_parameter`` means the answer is a knob inside a device, which this catalog
cannot enumerate because what a device exposes depends on the device: filter cutoff,
resonance, attack, threshold and everything else behind a front panel arrive this
way, with the procedure to reach them.
``blocked`` means Live's API cannot do it at all, with what to do in Live instead.
``intent_tool`` is reported in the ``tool`` field alongside the rows rather than in
place of them, because a tool that verifies its own work is the better route to the
same end but the path is still worth seeing. ``unresolved`` means nothing matched,
and ``unmatched`` lists the words that found nothing, which is usually where the
query went wrong.
A lookup costs about 1.5k tokens instead of the whole catalog. It does not touch
Live: it reads the catalog only, so nothing here proves anything about the set that
is open.
|
| find_soundA | Find an instrument or preset in Live's library by what you want it to sound like. Returns:
Dictionary with the root that was searched and why, the words that were searched
for, the ranked candidates each carrying ``item_path``, ``uri`` and ``category``,
how far the walks got, and the next call.
Note:
Nothing here has been listened to: the ranking reads the preset's name and the
browser category in its uri, so load one and listen before believing it fits.
``status`` is coverage, not fit: an empty ``candidates`` under ``complete`` does
mean no name there answers, while ``incomplete`` makes the list a lower bound.
Measured 2026-09-17 against Live 12.4.6, ``sounds`` completes, ``drums`` completes
at the depth this tool sends, and ``instruments`` cannot be searched to the end for
presets, so a negative from that root is never evidence of absence.
Each word costs one browser walk and blocks Live's main thread, so three are
searched and the rest named in ``words_not_searched``. ``considered`` is the sum
of items those walks examined, not the size of the root and not the size of the
library, so it exceeds either when two words search the same place. Both word
lists describe the walk, not the library: the ranking scores every intent word,
and shortened forms, against the names returned, so an unsearched word still
appears in ``matched``.
Measured 2026-09-17 against Live 12.4.6, "rhodes" and "moog" match no preset name in
``sounds``, and "kick" none in ``drums`` though a folder "Kick" matches under
``kind="any"``.
``category`` is derived from the uri's fragment rather than read from Live and is
null where the uri carries none; ``also_in`` names the other categories it was
filed under. To load one, call ``load_device`` with the candidate's ``load_with``
block, the target ``track`` and ``confirm=True``, preferring ``item_path``: a
``uri`` is walked for again and can run out of budget. Loading an instrument
silently replaces the one on that track, so read the chain with get_devices first.
|
| fire_sceneA | Fire a scene and report which beat the launch actually lands on. Returns:
Dictionary with the launch grid, the song position read on either side of the
call, and the beat the launch falls on, or both candidate beats where the
window spanned a boundary.
Note:
``fire`` answers that it succeeded and says nothing about when anything starts.
Live quantises a launch to the next grid boundary, so aiming a scene at a
particular bar from outside is a race: read the position, decide, fire, and Live
may already have passed the boundary the decision was made for. Measured against
Live 12.4.5: a read of ``song.current_song_time`` returned 55.6, the scene was
fired, and the launch landed at 96 rather than 64, one 8 bar boundary later than
intended. Nothing in the result said so.
This does not remove the race, which is Live's and is not reachable from here.
It makes the landing visible. The position is read immediately before and after
the call, and the answer names the beat a launch issued at either end of that
window falls on. Where the two differ, ``certain`` is false and both are given:
the launch is on one of them and which one is not knowable from outside.
With quantisation off the launch starts at once, so the window's own ends are
the answer and the beat is known no better than the window is wide.
This fires and does not wait. ``is_playing`` read straight afterwards is
normally false and that is correct, not a failure: nothing has started yet.
|
| transcribe_stemA | Turn a recording of one instrument into notes, cleaned of what it is not. Returns:
Dictionary with how many notes were heard and how many survived cleaning, the
pitch range, the span in beats, a sample of the notes, and the write result
where one was asked for.
Note:
**This transcribes pitch, and percussion has no pitch.** A drum kit returns cymbal
and shell resonances, reporting success: measured on a four minute drum stem, 39
notes for 488 beats, none of them a kick, and nothing across forty bars of steady
playing. Never write drums from here. Held material fails the other way: a wavering
sustained note reads as several, so a pad arrives at the right pitches with a rhythm
nobody played. Measured 2026-09-19 over four stems in their busiest 20 seconds,
written attacks ran 3.35 a second against 2.85 onsets on bass, 3.50 against 1.30 on
a strummed guitar, and 5.35 and 5.30 against 0.10 and 0.55 on pad and keys.
``transcription_suspect`` flags that disagreement, ``kind`` says which way, and
``transcription_checked`` says whether the comparison ran at all, since it needs a
macOS-only second decode: elsewhere no warning means nothing. Measured 2026-09-19
against exported MIDI for five pitched stems, pitch content agreed 93.7 to 96.2
percent: which notes, not when, the export's own timing having drifted. Harmonics
are removed where a lower, louder note at the same time explains them;
``monophonic`` then leaves nothing sounding at the same time as anything else.
``scripts/eval_transcription.py`` recomputes these.
Writing needs a clip: ``create_clip`` on the target slot first, long enough for the
whole part, then this with ``confirm=True``. ``span_beats`` runs from beat 0 to the
last note's end, so a clip of that length rounded up to a bar holds it. Writing
replaces every note in the clip, with no undo here and no rollback in the LOM: a
write that fails after the removal leaves nothing.
|
| sound_index_statusA | Is there an index of how this installation's library sounds, and what is in it? Call this before ``match_sound``. Without an index that tool has nothing to rank
against, and a fresh session has no way of knowing whether one was ever built.
Returns:
Dictionary with whether an index exists, where it is, when it was built, how
many items of each kind it carries, which libraries they came from, and what
was skipped when it was built.
Note:
The index is built from the audio Ableton renders for its own browser previews,
which sits beside the content under ``Ableton Folder Info/Previews``. It is not
committed and it describes one machine: rebuild it by running
``live-maestro-index`` after installing or removing packs.
|
| match_soundA | Rank the library by how close each item sounds to a piece of audio you have. Returns:
Dictionary with the descriptor taken from the file, the ranked candidates each
carrying its name, kind, category, library and score, and the call that loads
the one you pick.
Note:
A score orders this one answer, lower being closer: not a percentage or a distance,
and scores from two files do not compare. The order inside one answer is weaker than
it looks: ``elsewhere_in_the_file`` ranks a second stretch of the same recording to
show how weak; ``also_elsewhere`` says a candidate came back in both, the one
reading here that is evidence, not ordering. Measured 2026-09-19 over five separated
stems in 20 s windows, two windows of one stem shared about one name in ten and the
descriptor moved by three to six times the whole width of the top thirty. Ranked
over the whole library, the top candidate shared the probe's family 1 time in 7 at
20 s and 6 of 20 on 1.5 s slices: a bass stem answers with drum kits and a guitar
stem with pads; given ``instrument``, the same audio answers with basses and plucked
guitars. Name the family when you know it: this is a shortlist, not a ranking.
What is compared is Ableton's preview render of each item, so a busy-riff preview
describes itself differently from a held-chord one, and nothing here speaks for
playable range, velocity response or a single note's attack. The match is timbral
with level removed, so a quiet and a loud take of one sound match exactly; pitch is
not matched, so a bass line at 50 Hz and the same line at 100 Hz are near
neighbours. ``analysed`` says where the window landed, with peak and RMS; a silent
stretch answers ``nothing_to_match``, and without an index this answers
``no_index``. To load one, set ``track`` in its ``load_with`` block and call
``load_device``: the handle is a name, not a path, so the browser search can return
more than one item. Check before confirming.
|
| analyze_audioA | Measure the audio itself: loudness, true peak, stereo phase and band balance. Reads the LiveMaestro Analyzer Max for Live device over loopback OSC; without it this
reports device_not_found with the steps to load it.
- Loudness to EBU R128: momentary 400 ms, short term 3 s, gated integrated since
reset, range.
- True peak, oversampled, with sample peak, RMS and the crest factor between them.
- Stereo correlation full band and below 120 Hz, and low-end survival of a mono sum.
- Seven band energies on the mono sum, and their weighted centre, not an FFT centroid.
The device reads at its position in the chain, so it belongs last: an effect
after it is not in the reading. A master chain sits before the master fader, so a
master reading matches the output only at 0.0 dB. The two peak figures are maxima
since the last reset and only rise: to measure one, call with
reset=true and no accumulate, let a pass of the material through, then call again.
The averaged figures need no reset. What it proves is what the signal measured
passing the device, not the room.
Every measurement carries the transport state from immediately after it, so a reading
taken mid scene change can be recognised. ``in_transition`` is true while any track
reports fired_slot_index 0 or more, a launch waiting for the next quantization boundary,
and ``tracks_sounding`` counts tracks whose playing_slot_index is 0 or more while the
transport runs, 0 when stopped: clips Live plays, not what is audible. Unreadable state
gives ``transport`` null with a code and one sentence in ``transport_unavailable``, or
not_queried when the analyzer did not answer.
|