Note off velocity in Tracktion Plugin

Hi, I am trying to write a Plugin in Tracktion.
It’s a plugin that handles MIDI.
I noticed that when a MidiMessage of type NoteOff arrives at the MyPlugin::applyToBuffer(const PluginRenderContext& fc) method its velocity is always 0.
I would need to have the same velocity as the corresponding noteOn.
How can I do it ?

That’s not how MIDI works, note-off velocity is the speed at which the note is released, the opposite of note-on.

However, most MIDI controllers don’t have this feature so you’re unlikely to see it recorded unless you’re using an expressive controller.

using the Tracktion MidiClip is there any way to achieve this behavior ? Then insert a note in the sequence and when the noteOn is fired shoot the noteOff at the same velocity ?

Why would you want to do this? The note-off velocity is an important performance event …

@dave96 @ibisum
my plugin on the arrival of a note triggers generic events that at NoteOn do one thing and at NoteOff another. The triggers are tied to the number of notes and velocity so I need to figure out, at NoteOff, what the actual velocity is to trigger the correct event.

Example :

Arrive NoteOn 10 with vel 10 → trigger EventA
Arrive NoteOn 10 with vel 50 → trigger EventC

Arrive NoteOff 10 with vel 10 → un-trigger EventA

Arrive NoteOn 20 with vel 10 → trigger EventB

Arrive NoteOff 10 with vel 50 → un-trigger EventC
Arrive NoteOn 20 with vel 10 → un-trigger EventB

I want to reach this goal.

At the moment the arrived NoteOff has this shape :

Arrive NoteOff 10 with vel 0
Arrive NoteOff 10 with vel 0
Arrive NoteOff 20 with vel 0

So I can’t match the correct event to un-trigger

So I just had a quick look at the API and am surprised to find there isn’t a way to set the note off velocity for a MIDI note. This is obviously an oversight and I’ll add it to the to-do list.

1 Like

Something like this perhaps:

#include <tracktion_engine/tracktion_engine.h>

void processMidiClip(tracktion_engine::MidiClip& midiClip)
{
    // Get the MidiList from the clip
    auto& midiList = midiClip.getMidiList();

    // Container to track active Note On messages
    std::map<int, juce::MidiMessage*> activeNotes;

    // Iterate through the events
    for (auto event : midiList)
    {
        auto& midiMessage = event->getMidiMessage();
        auto noteNumber = midiMessage.getNoteNumber();

        if (midiMessage.isNoteOn())
        {
            // Store the Note On event with the note number as the key
            activeNotes[noteNumber] = &midiMessage;
        }
        else if (midiMessage.isNoteOff())
        {
            // Find the corresponding Note On event
            auto it = activeNotes.find(noteNumber);
            if (it != activeNotes.end())
            {
                // Access the Note On velocity
                float noteOnVelocity = it->second->getVelocity();

                // Do something with the Note On velocity and the Note Off event
                DBG("Note Off for Note Number: " << noteNumber
                    << ", Corresponding Note On Velocity: " << noteOnVelocity);

                // Remove the Note On as it has been matched
                activeNotes.erase(it);
            }
        }
    }
}

Thank you very much, I am currently stuck because of this “error”. I hope you can solve it as soon as possible !
I am waiting for yours and thank you very much !

Thank you very much,
I will take a look at it.

Hello @dave96
Any news ?

@dave96 any news with that ?

Hi @dave96
Using the new Waveform13 i noticed that you added the note-off velocity called “lift” property in the code.

I was wondering how I can use it in my code, creating a midiNote and setting its note-off velocity.
It would be very helpful.
Thanks in advance.

@Fernando - MIDI Note Off messages have a velocity value associated with them, as per the MIDI standard. So, to use MIDI Note Off Velocity is the same as using MIDI Note On Velocity.

From juce_MidiMessage.h:

    /** Creates a key-up message.

        @param channel      the midi channel, in the range 1 to 16
        @param noteNumber   the key number, 0 to 127
        @param velocity     in the range 0 to 1.0
        @see isNoteOff
    */
    static MidiMessage noteOff (int channel, int noteNumber, float velocity) noexcept;

This has been a part of the MIDI standard since the beginning, and is usually pretty easy to deal with ..

hello @ibisum
Thanks for the answer.
With this method I create a MIDI Message like noteOff.
In tracktion to add a midi note to the sequence you use the methods of the tracktion_midiList.h class, that is:

MidiNote* addNote (const MidiNote&, juce::UndoManager*);
MidiNote* addNote (int pitch, BeatPosition startBeat, BeatDuration lengthInBeats, int velocity, int colourIndex, juce::UndoManager*);

I believe it is then the tracktion engine that starting from the midi sequence will create and send to the midi output (or to a plugin) the MidiMessage corresponding to the midi note under the playhead.
More specifically, at the start of the note it sends a MidiMessage of type noteOn and at the end one of type noteOff.
So, following the tracktion engine, I cannot create a MidiMessage and add it to the sequence as I like but I create a MidiNote (or other type MidiSysexEvent) and then the tracktion engine will transform the event into MidiMessages.

Having said that, I believe it is a property that I have to give to the MidiNote.

In fact, using Waveform13 and looking at the edit status, if I create a MidiNote I will have this valueTree:

      <SEQUENCE ver="1" channelNumber="1">
        <NOTE p="67" b="0.875" l="1.0" v="96" c="0"/>
      </SEQUENCE>

While as soon as I change the velocity value on the note-off the state becomes this:

      <SEQUENCE ver="1" channelNumber="1">
        <NOTE p="67" b="0.875" l="1.0" v="96" c="0" lift="0"/>
      </SEQUENCE>

By adding the “lift” property directly on the MidiNote. I imagine that this information is interpreted by the tracktion engine and makes it understand to generate the corresponding noteOff of that velocity, or at least I think so.

The thing is, in the code I can’t figure out how to set this property.

The lift property is only taken in to account for MPE sequences. It’s a requirement of MPE.

If you want to hack something in, you probably want to modify addToSequence which is what gets called for normal notes.

The line that currently looks like this:

seq.addEvent (juce::MidiMessage::noteOff (channelNumber, noteNumber), upTime);

should probably look something like this:

const auto noteOffVelocity = state.hasProperty (IDs::lift) ? int (state[IDs::lift]) / 127.0f : 0.0f;
seq.addEvent (juce::MidiMessage::noteOff (channelNumber, noteNumber, noteOffVelocity), upTime);

That should work if you set the lift property like this:

midiNote.state.setProperty (IDs::lift, velocity0to127, um);

But eventually we’ll probably want to add functions around that to avoid having to directly modify the state.

1 Like

@dave96

It works.
Thank u so much.

But eventually we’ll probably want to add functions around that to avoid having to directly modify the state .

Yes. It would be very helpful cause I don’t need to directly modify the tracktion engine.

If i would to use an MPE Sequence istead of a normal one, how should i use it in my code ? A little example would be very appreciated.