/ VST Home / Technical Documentation
[3.5.0] Key Switch
On this page:
Related pages:
Introduction
Allows information exchange between the plug-in and the host about which key switches are currently used.
Extended plug-in interface IEditController for key switches support: Vst::IKeyswitchController
- [plug imp]
- [extends IEditController]
- [released: 3.5.0]
- [optional]
Some instrument plug-ins support key switching functionality, which allows the users to switch between different layered sounds while playing notes. To achieve this, the users have to press a specific key associated to the wanted layer before playing new notes.
A typical example is a sample-based player plug-in with a violin sound, which can include different layers or articulations: pizzicato, legato, tremolo, etc. By pressing and/or holding a specific key, for example C-1, before playing new notes, the plug-in will choose to play pizzicato, by using D-1, it will play legato, and so on.
With VST Expression Map (introduced by Steinberg in Cubase 5), these key switches can be used in the Score Editor, to associate a symbol (articulation) to a note. Each time this note is played, the corresponding key switch will be used (sent to the plug-in as noteOn event).
In order to help the creation of such a map, VST 3.5 defines a new interface Steinberg::Vst::IKeyswitchController. If an (instrument) plug-in supports such an interface, the host can get the current set of used key switches from the plug-in (megatrigg / articulation: Steinberg::Vst::KeyswitchInfo) for a given channel of an event bus, and then automatically use them (like in Cubase 6) to create a VST Expression Map.
How does it work?
Here the step by step example. We want a plug-in with 1 event bus, which is mono-timbral (1 channel), and which supports 2 key switches:
- The instrument plug-in should have one input event bus (could be more):
//------------------------------------------------------------------
tresult PLUGIN_API MyExampleProcessor::initialize (FUnknown* context)
{
//---always initialize the parent-------
tresult result = AudioEffect::initialize (context);
if (result == kResultTrue)
{
// we want a Stereo Output
addAudioOutput (STR16 ("Stereo Output"), SpeakerArr::kStereo);
// create Event In bus (1 bus with only 1 channel)
addEventInput (STR16 ("Event Input"), 1);
}
return result;
}
- The controller should have the interface Steinberg::Vst::IKeyswitchController, here in the class declaration:
//------------------------------------------------------------------
class MyExampleController: public EditController, public IKeyswitchController
{
public:
//...
//---from IKeyswitchController
virtual int32 PLUGIN_API getKeyswitchCount (int32 busIndex, int16 channel);
virtual tresult PLUGIN_API getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, KeyswitchInfo& info);
//...
OBJ_METHODS (MyExampleController, EditController)
DEFINE_INTERFACES
DEF_INTERFACE (IKeyswitchController)
END_DEFINE_INTERFACES (EditController)
REFCOUNT_METHODS(EditController)
//...
};
- Now we have to implement the interface Steinberg::Vst::IKeyswitchController (only 2 functions), in our example Steinberg::Vst::IKeyswitchController::getKeyswitchCount should return 2 (2 key switches):
//------------------------------------------------------------------
int32 MyExampleController::getKeyswitchCount (int32 busIndex, int16 channel)
{
// we accept only the first bus and 1 channel
if (busIndex == 0 && channel == 0)
return 2;
return 0;
}
- Then, we have to implement Steinberg::Vst::IKeyswitchController::getKeyswitchInfo, which allows to inform the host about what the plug-in supports:
//------------------------------------------------------------------
tresult PLUGIN_API MyExampleController::getKeyswitchInfo (int32 busIndex, int16 channel, int32 keySwitchIndex, KeyswitchInfo& info)
{
// we accept only the first bus and 1 channel and only 2 keyswitches
if (busIndex == 0 && channel == 0 && (keySwitchIndex == 0 || keySwitchIndex == 1)
{
memset (&info, 0, sizeof (KeyswitchInfo));
// in this case we want that Keyswitch should be maintained pressed for playing
info.typeId = kKeyRangeTypeID;
// we could use keyRemapped to make easier the use of the keyswitch (near the available instrument key range)
// take care that there are no overlap between keyswitches and real key (playing sound)
if (keySwitchIndex == 0)
{
USTRING ("Accentuation").copyTo (info.title, 128);
USTRING ("Acc").copyTo (info.shortTitle, 128);
// if the user keeps pressed C-1 or C#-1 or C-0 then the Accentuation sound should be played
info.keyswitchMin = 12; // C-1
info.keyswitchMax = 13; // C#-1
info.keyRemapped = 24; // C-0
}
else
{
USTRING ("Softly").copyTo (info.title, 128);
USTRING ("Soft").copyTo (info.shortTitle, 128);
// if the user keeps pressed D-1 or D#-1 or D-0 then the Softly sound should be played
info.keyswitchMin = 14; // D-1
info.keyswitchMax = 15; // D#-1
info.keyRemapped = 26; // D-0
}
info.unitID = -1; // not used
info.flags = 0; // not used
return kResultTrue;
}
return kResultFalse;
}
- Last step, in the processor component, we have to adapt the process call to switch to the wanted articulation:
//------------------------------------------------------------------
tresult MyExampleProcessor::process (ProcessData& data)
{
// ....
// get the input event queue
IEventList* inputEvents = data.inputEvents;
if (inputEvents)
{
Event e;
int32 numEvents = inputEvents->getEventCount ();
// for each events check it..
for (int32 i = 0; i < numEvents; i++)
{
if (inputEvents->getEvent (i, e) == kResultTrue)
{
switch (e.type)
{
//-----------------------
case Event::kNoteOnEvent:
{
// here a note On
// check if this is a Keyswitch
switch (e.noteOn.pitch)
{
// Accentuation Keyswitch
case 12:
case 13:
case 24:
currentLayer = kAccentuationLayer;
break;
// Softly Keyswitch
case 14:
case 15:
case 26:
currentLayer = kSoftlyLayer;
break;
default:
// play the note in the currentLayer
// ...
}
} break;
//-----------------------
case Event::kNoteOffEvent:
{
// check if keyswitch is released
switch (e.noteOff.pitch)
{
// Accentuation Keyswitch
case 12:
case 13:
case 24:
case 14:
case 15:
case 26:
currentLayer = kDefaultLayer;
break;
default:
// released note...
//...
}
} break;
//....
}
}
}
}
//...
}
That is it!