Articles

What's New in Live 11, Part 2

The release of Ableton Live 11 introduced a number of new features for working with MIDI notes:

  • Support for MPE

  • Note probability

  • Support for velocity deviation

  • Support for release velocity

The Max for Live API has been updated to provide access to this new data in Live, and to allow for manipulating notes without discarding this new data.

The second part of our look at what’s new in Live for Max for Live users will provide a brief overview of the new features in the Live notes API.

We’re also including a pair of example Max for Live devices which contain snippets that you can copy and paste to help you hit the ground running as you learn to use these new features.

And don’t worry if you’re not really familiar with dictionaries (dicts) or note IDs in Max — two mechanisms in Max you’ll be using when you work with the notes API — we’ve tried to walk you through how dicts and note IDs are used in the example device snippets.

Live11_notes_API_devices.zip
application/zip 57.84 KB
Click here to download the example Max for Live devices

Before and after

For those of you who are already familiar with working with the notes API in Live 10 (or for those of you who’d rather read patches than text), let’s start with a device from the download (Notes API - Before and After.amxd) that will give you a side-by-side comparison of how common tasks used to be done, and the new Live 11 notes API ways of doing them:

Note: the Notes API - Before and After device is frozen. After unfreezing, you can find its frozen abstractions and javascript files in your Documents folder, in Max 8/Max for Live Devices/Notes API - Before and After Project.


You’ll find examples here of all the common API-based notes operations you already know (getting, adding, removing, modifying (all notes or selected notes), duplicating and observing) with examples of the new notes API versions you can copy, paste, and modify as needed to quickly add the new functionality to your own devices.

The API specification

You will find the full specification for the notes API in the Live Object Model document listed as part of the Clip specification.

The new features of the Live 11 notes API involves working with note IDs and dictionaries in Max (for more details on this, see below).

The new notes API methods are:

  • get_notes_extended: returns a dict with all notes in a specified time and pitch range

  • get_selected_notes_extended: returns a dict with all selected notes

  • add_new_notes: accepts a notes dict without note IDs to add new notes

  • apply_note_modifications: accepts a notes dict with note IDs of notes to modify

  • remove_notes_extended: removes all notes in a pitch and time range

  • get_notes_by_id: gets a note for a certain note ID

  • remove_notes_by_id: removes a note with a certain note ID

To see exactly what parameters to provide to each of these methods and what they return, please refer to the specification in the Live Object Model document.

The old notes API methods are still available under conditions

The old note access methods are still available for legacy devices in Live 11, but using them is discouraged by warnings in the Max Window.

  • get_notes

  • set_notes

  • get_selected_notes

To prevent unintentionally removing new note data like MPE, the following old methods that remove notes will show a warning popup:

  • remove_notes

  • replace_selected_notes

The warning pop-ups can be permanently silenced by the user by choosing Don't show again.

Accessing the API with dicts

The dict (short for “dictionary") object family in Max provides versatile access to all kinds of data. A dict represents series of key-value pairs in a leveled structure. The structure of these dicts follow that of the JSON format, which will be helpful for those of you who are familiar with web development: a key can point to a string, float or int, or to another dict.

Additionally, a key can point to an array of any of these things.

The Notes API Basics.amxd Max for Live device included in the download provides a basic example of how to use the notes API with Max dict objects:

The order of key/value pairs in a dict is not fixed, nor is the number of keys and values. This is crucial to the new notes API, because it needs to be extendible; we’ll want older devices to continue to work without modifications if new note information is added in the future.

Dicts are passed by reference

When outputting a dict to another object, instead of copying all the data over a patch cord, a reference to this data is transmitted. That is why you see something like dictionary u944000887 when you show a dict in a message box.

Note: Modifying a dict will affect all subsequent usages of this dict. To avoid problems, you should make a copy of a dict before modifying it. You can find a demonstration of this in the IntroToDict subpatcher you’ll find in the Notes API - Before and After device.

For more information about dicts, take a look at the more in-depth dict reference in the Max documentation.

Keeping track of notes with note IDs

The previous method for modifying a note in Live 11 involved getting the note’s properties, removing them, and then adding a new note with similar properties. Adding MPE support meant that this old method would result in discarding all per-note MPE data.

Live 11 guarantees that all note data is preserved by letting you provide new values for an existing note ID when you perform modification rather than removing and replacing note IDs.

Note IDs are simply integers that uniquely identify a note within a clip. You cannot generate these IDs yourself; you can only find out about them by getting existing notes from a clip.

The new procedure for modifying a note with the API looks like this:

  1. Get current notes with get_notes_extended, which provides you with a dict of all the notes.

  2. Get the note_id of the note you need to modify from the resulting dict.

  3. Create a new dict containing the new note values that retains the existing note_id.

  4. Send this new dict back to the Clip with apply_note_modifications.

Note IDs are different from other references in Max

Note IDs are different from existing reference types in Max, like Live Object Model IDs (e.g, id 56) or uuids (e.g., dictionary u48298492). These references are always globally unique and have a keyword prepended, whereas note IDs are unique only within a clip and are just ints (3). For more details, see the IntroToNoteIDs subpatcher in the Before and After device.

The notes API in Javascript

Javascript is uniquely well-equipped for dealing with the new notes API in Live 11 — the notes data is structured as JSON, and JSON is invented for Javascript. All new notes API operations are available to you; take a look at the Before and After example device, in the Javascript subpatcher for bits of example code that are ready to be copied into your own device patches.

We hope that these articles have given you a good first look at the improvements and new features in Live 11 for Max for Live users, and we’re excited to see what you’ll make using them!

Corrections

2021-04-22: In a previous version of Notes API - Before and After.amxd, a pitch span of 127 was used when the intention was to apply an operation to the full pitch range. This has now been updated to 128.

2021-05-28: Added examples for outputting notes data from Javascript to dict and for inputting notes data from dict to Javascript.

by Mattijs KneppersGregory Taylor on March 16, 2021

Creative Commons License
Brodie Matthews's icon

This sounds awesome now i must upgrade.Thanks

AudioMatt's icon

access to midi learn for custom UIs!?!????!

dfwaudio's icon

Ok. That works great. Maybe the "Notes API - Before and After.amxd" file was missing in the first download? ;) Thanks a lot for the new features and finally the very good explaination!

broc's icon


Many thanks for this excellent article!

Mattijs Kneppers's icon

Hey all,

I wanted to add a quick note that with the current version of Max, there is a limitation in the size of the dict that can be passed back and forth to and from Live, which might result in a warning message in the Max window when trying to change or request a lot of notes. A fix for this is in the pipeline though, this limitation should be gone in the next version of Live.

Jan Andersson's icon

Many thanks for the JS examples. A quick question. I am adding notes to a clip in several steps (i.e., new notes to existing notes). Is there a simple way to get the note ID of the notes added the last time (we can assume that the note ID are known of the old notes if that helps). I can think of a couple of cumbversome ways to do this but if there is an easy one that would be great. Again what you are doing is superinteresting.

All the best,
Jan

Mattijs Kneppers's icon

Thanks Jan!

The only approach I know of is indeed to store a list of note IDs in the clip and to scan the notes again for new IDs after the new notes were added. I agree it would be better for convenience and performance if the add_new_notes method would directly return the newly added note ids. I've noted that as a feature request.

Jan Andersson's icon

Great,

that would help!
/Jan

dfwaudio's icon

Maybe here someone can help please. I'm trying to iterate through all the notes and put them out from js. I edited 'NotesAPIget.js' but don't find out how to output all elements from the array and not only one element.

autowatch = 1
inlets = 2
outlets = 1

var clipApi = null

function id(id) {
    clipApi = new LiveAPI("id " + id)
    if (clipApi.path === "") clipApi = null
}

function printLatestNote(from_time, time_span)
{
    if (clipApi == null) throw("No clip")

    var notesJson = clipApi.call("get_notes_extended", 0, 127, from_time, time_span)
    var notesObject = JSON.parse(notesJson)
    var notesArray = notesObject.notes
    post("length " + notesArray.length + "\n")
    printNote(notesArray[0])
}

function printNote(note) {
    outlet(0,    + note.note_id,
                + note.pitch,
                + note.start_time,
                + note.duration,
                + note.velocity,
                + note.release_velocity,
                + note.mute,
                + note.probability,
                + note.velocity_deviation)

    post("probability: " + note.probability + "\n")

}

Thanks for every help

broc's icon

Try replacing
printNote(notesArray[0])
with
notesArray.forEach(printNote)

dfwaudio's icon

Thanks so much, Broc. Works perfect. Tried a few different lines, also with forEach function but i didn't understand the 'grammar'. Have to learn a lot of javascript... it's so powerfull.

Fabrizio Poce's icon

Great article! [sneak preview from the beta testing arena: limitations with the dict size for MIDI notes and the API have been addressed in the latest beta; so soon limitations should be gone].

Brodie Matthews's icon

For sets of chords it would be great to send a dictionary as a sub dictionary to another dicitonary like dict.pack but using the dict name or like dict.sub @name or using replace this would be awesome.

Mattijs Kneppers's icon

Hi Brodie,

I am not fully sure what your use case is, but some things that are hard to do with dict objects can be done much more easily with Javascript.

I just added two examples in the Javascript subpatcher of the Notes API Before and After device that demonstrate how to transfer notes data to and from dict objects.

Brodie Matthews's icon

That sounds cool Mattijs thanks.Really big stuff!

Jan Andersson's icon

Maybe a little late but I take the chance anyway. I am struggling with selecting notes programmatically (JS). Assume for example that I have 3 notes starting in beat 1 that I have selected. How can I move the selection to, say, beat 2, i.e., deselecting notes in beat 1 and selecting multiple notes in beat 2. Are there any LOM functions that can be used?

Mattijs Kneppers's icon

Hi Jan, that is currently not possible. But if all goes well, this will be added in a future version of Live

Jan Andersson's icon

OK, that sounds good. Please also add the ability to control the Session Clip insert marker.

Mattijs Kneppers's icon

Hi Jan, it would be great of you could send an email to support@cycling74.com about your feature wish, the ability to control the Session Clip insert marker. Ideally, perhaps you could roughly describe your use case. Thanks!

Jan Andersson's icon

OK, will do

Ricardo Matias's icon

What about "select_notes" has anyone figured out how to do it with the new API?

Mattijs Kneppers's icon

Hi Ricardo, the option to select notes has been added in Live 11.1. If you have access to the Live public beta, you should be able to call `select_notes_by_id` on a Clip object to select notes. This has not been documented in Max yet, but can be found out by sending `getinfo` to a live.object that is set to a Clip object.

Ernest's icon

Thanks for a very informative article! Do you know if the new version supports parallel processing within .amxd objects? My design for a multiphonic synthesizer in Max8 and gen~ needs a poly~ object to provide 32 reliable voices with 30% total CPU usage on a 4GHz i7, so I am distributing it as a compiled .exe with reWire drivers and MIDI interface. I'd like to add more controllers, but in MIDI channels more than ~110 parameters per channel have to done with sysex rather than CC# controller values. While sysex could support more, I'd much rather convert it into a M4L device and use Ableton's preset manager, because with 16 channels of multiphony, I've already hit the limit of ~1,700+NRPN MIDI CC values for device parameters.

Mattijs Kneppers's icon

Hi Ernest, thanks for the kind words. Parallel processing of poly~ voices in Live is not (yet) supported unfortunately.

Iain Duncan's icon

Just wanted to say I am excited about the growth of the Notes API. I have Scheme for Max running nicely and locked in tight in Live now, so interacting with the sequence data from Scheme/Lisp is fully doable and will open a lot of possibilities! Live + Max + s7 Scheme is a joy.

visco viscont's icon

Hi Mattijs,

is it possible to get and set note MPE data (pitch, slide and pressure) via get_notes_extended and add_new_notes ?

An An's icon

Hi Mattijs,
in the documentation, for example for Clip.get_notes_by_id(..), we can also pass in a dictionary with a "note_ids" key, associated with a list of ids. I can't seem to get this to work, and see no example for this available. Can you confirm that this is actually implemented and working? Related to this, I also can't define which fields of the note I want returned (according to the docs, I could do that with a "return" list inside the same dictionary).

This is the same for every other function that is documented similarly. At least those that I tried.

dfwaudio's icon

Hello. When selected notes are moved up or down with cursor keys in live11 the existing notes aren't overwritten. The existing notes remain if the edited notes are moved further.
Is there a corresponding command in maxforlive which maintains the existing notes?
apply_note_modifications and replace_selected_notes doesn't work this way.

Mattijs Kneppers's icon

Hi DFWAUDIO, that's a good point, there is currently no way to have selected notes be kept separate from notes they (temporarily) replace. I added this in our feature wishes tracker.

For now, you could maybe work around this by storing all the notes of a clip in a dict before moving a selection of notes.

Eero Pitkänen's icon

Am I missing something - or is it not possible to modify the MPE event data via the API yet? The MPE compatibility means only that the data is carried over modifications? I am currently editing the PerNoteEvent data a bit hackish directly inside the Clip XMLs...

Mattijs Kneppers's icon

Hi Eero, that is correct, it is not yet possible to modify the MPE automation graphs as stored in clips in Live. The notes API only supports copying the MPE data implicitly when moving notes around. This is certainly a wish for the future, but support for this hasn't made it into Live yet.

For clarity: manipulating incoming MPE data in real-time -is- possible with Max for Live, using the mpeparse object and its kin.

Norman Freund's icon

Would have never guessed that is how it works, a shame the user manual does not spell this out. Tried your method and works, much appreciated.

Dalmazzo's icon

Thanks a lot for the tutorial; it is very informative. I'm using the example called Add to add a chord, for instance, a C maj7. But I'm struggling to find a way to adjust the pitch and detune each note of the chord in some specific cents. I've been trying to use MPE Control, but I can only adjust the whole chord after generating it, using a mpeparse and then a poly~PitchSlide. I can't find a way to adjust each MPE individually per note in the chord when generating the chord.
The concept is quite simple, but to achieve it seems to be not possible from Max?

Mattijs Kneppers's icon

Hey Dalmazzo,

Indeed at the moment there is no way to create or change MPE data for a note via the notes API.

What you can do is change the MPE data while playing notes, which is what devices like MicroTuner do.