/ VST Home / Technical Documentation

[3.8.0] MIDI Learn 2 (MIDI 2.0 support)

On this page:

Related pages:


Introduction

MIDI Learn Interface: Vst:: IMidiLearn2.

If this interface is implemented by the edit controller, the host will call this method whenever there is live MIDI-CC input for the plug-in. This way, the plug-in can change its MIDI-CC parameter mapping and notify the host using the Vst:: IComponentHandler::restartComponent with the kMidiCCAssignmentChanged flag.

Use this if you want to implement custom MIDI-Learn functionality in your plug-in.

Note: The struct Midi2Controller refers to MIDI 2 Registered and Assignable Controllers. These UMP MIDI 2 message types are specified by the MIDI Association to map and translate directly to MIDI 1 RPN and NRPN. In that regard, they are both MIDI 1 and MIDI 2.

Generally, plug-ins should avoid allowing n-to-m mappings of controllers and parameters, as this could lead to inconsistent states in how controllers and parameters are represented within the host, which could confuse users. Nevertheless, there may be special situations where such mappings are intended, e.g., when layering sounds within a multitimbral plug-in. Therefore, such mappings are allowed.

Example

In mycontroller.h

//------------------------------------------------------------------------
// here an example of how a VST 3 plug-in could support this IMidiLearn2 interface.
// we need to define somewhere the iids:
  
//in MyController class declaration
class MyController : public Vst::EditController, public Vst::IMidiLearn2
{
    // ...
    //--- IMidiLearn2 ---------------------------------
    tresult PLUGIN_API onLiveMidi2ControllerInput (BusIndex index, MidiChannel channel,
                                                   Midi2Controller midiCC) SMTG_OVERRIDE;
    
	tresult PLUGIN_API onLiveMidi1ControllerInput (BusIndex index, MidiChannel channel,
	                                               CtrlNumber midiCC) SMTG_OVERRIDE;
    // ...
  
    OBJ_METHODS (MyController, Vst::EditController)
    DEFINE_INTERFACES
        // ...
        DEF_INTERFACE (Vst::IMidiLearn2)
    END_DEFINE_INTERFACES (Vst::EditController)
    //...
}

In mycontroller.cpp

#include "pluginterfaces/vst/ivstmidimapping2.h
 
namespace Steinberg {
    namespace Vst {
        DEF_CLASS_IID (IMidiLearn2)
    }
}
  
//------------------------------------------------------------------------
tresult PLUGIN_API  MyController::onLiveMidi2ControllerInput (BusIndex index, MidiChannel channel, Midi2Controller midiCC)
{
    // if we are not in doMIDILearn (triggered by a UI button for example) or wrong channel then return
    if (!doMIDILearn || index != 0 || channel != 0 || midiLearnParamID == InvalidParamID)
        return kResultFalse;
 
    auto currentMapping = midiCCMapping.find ({CCType::CC, midiCC});
    if (currentMapping == midiCCMapping.end () || currentMapping->second != midiLearnParamID)
    {
        removeCurrentMidiLearnParamAssignment ();
        midiCCMapping[{CCType::CC, midiCC}] = midiLearnParamID;
        
        if (auto _componentHandler = getComponentHandler ())
            _componentHandler->restartComponent (kMidiCCAssignmentChanged);
    }
    return kResultTrue;
}

//------------------------------------------------------------------------
tresult PLUGIN_API  MyController::onLiveMidi1ControllerInput (BusIndex index, MidiChannel channel, CtrlNumber midiCC)
{
    // if we are not in doMIDILearn (triggered by a UI button for example) or wrong channel then return
    if (!doMIDILearn || index != 0 || channel != 0 || midiLearnParamID == InvalidParamID)
        return kResultFalse;
 
    CCKey key { midiCC.registered ? CCType::RPN : CCType::NRPN, (midiCC.bank << 7) | midiCC.index };
    auto currentMapping = midiCCMapping.find (key);
    if (currentMapping == midiCCMapping.end () || currentMapping->second != midiLearnParamID)
    {
        removeCurrentMidiLearnParamAssignment ();
        midiCCMapping[key] = midiLearnParamID;

        if (auto _componentHandler = getComponentHandler ())
            _componentHandler->restartComponent (kMidiCCAssignmentChanged);
    }
    return kResultTrue;
}