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.

    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:
    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.

    • Mar 17 2021 | 8:51 am
      This sounds awesome now i must upgrade.Thanks
    • Mar 17 2021 | 12:19 pm
      access to midi learn for custom UIs!?!????!
    • Mar 17 2021 | 12:54 pm
      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!
    • Mar 17 2021 | 2:38 pm
      Many thanks for this excellent article!
    • Mar 17 2021 | 4:31 pm
      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.
    • Mar 18 2021 | 11:43 am
      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
    • Mar 18 2021 | 12:17 pm
      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.
    • Mar 18 2021 | 6:45 pm
      Great,
      that would help! /Jan
    • Mar 21 2021 | 11:40 am
      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
    • Mar 21 2021 | 10:17 pm
      Try replacing printNote(notesArray[0]) with notesArray.forEach(printNote)
    • Mar 22 2021 | 3:35 pm
      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.
    • May 27 2021 | 9:10 pm
      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].
    • May 27 2021 | 10:06 pm
      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.
    • May 28 2021 | 8:30 am
      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.
    • May 28 2021 | 12:58 pm
      That sounds cool Mattijs thanks.Really big stuff!
    • Aug 11 2021 | 1:32 pm
      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?
    • Aug 11 2021 | 3:01 pm
      Hi Jan, that is currently not possible. But if all goes well, this will be added in a future version of Live
    • Aug 11 2021 | 3:24 pm
      OK, that sounds good. Please also add the ability to control the Session Clip insert marker.
    • Aug 16 2021 | 8:20 am
      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!
    • Aug 16 2021 | 3:21 pm
      OK, will do
    • Oct 06 2021 | 1:55 pm
      What about "select_notes" has anyone figured out how to do it with the new API?
    • Oct 06 2021 | 2:23 pm
      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.
    • Oct 07 2021 | 8:28 pm
      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.
    • Oct 08 2021 | 8:05 am
      Hi Ernest, thanks for the kind words. Parallel processing of poly~ voices in Live is not (yet) supported unfortunately.
    • Oct 11 2021 | 2:56 pm
      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.
    • Nov 18 2021 | 7:09 pm
      Hi Mattijs,
      is it possible to get and set note MPE data (pitch, slide and pressure) via get_notes_extended and add_new_notes ?
    • Jan 23 2022 | 4:25 pm
      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.
    • Apr 11 2022 | 3:52 pm
      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.
    • Apr 11 2022 | 4:22 pm
      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.
    • Apr 19 2022 | 8:51 pm
      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...
    • Apr 20 2022 | 10:41 am
      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.