Allows information exchange between the Plug-in and Host about which key switches are currently used.
Introduction
Some instrument Plug-ins support Key switching functionality, which allows a user to switch between different layered sounds while playing notes, to achieve this the user has to pressed a specific key associated to this wanted layer before playing new notes.
A typical example is a sample-based-Player Plug-in with a violin sound which could include different layers or articulations: pizzicato, legato, tremolo... By pressing or by keeping pressed 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...
With VST Expression Map (feature of Cubase since 4.0), these Key switches could be used in score editor to associated a symbol (articulation) to a note and each time this note will be played the corresponding Key switch will be used (send to the Plug-in as noteOn event).
In order to help the creation of such Map, VST 3.5 defines a new interface Steinberg::Vst::IKeyswitchController. When a (instrument) Plug-in supports such interface, the host could get from the Plug-in the current set of used Key switches (megatrigg / articulation: Steinberg::Vst::KeyswitchInfo) for a given channel of a Event Bus and then automatically use them (like in Cubase 6) to create VST Expression Map.
How does it work?
Here the Step by Step example.
We want a Plug-in with 1 event bus and mono-timbral (1 channel) 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)
{
tresult result = AudioEffect::initialize (context);
if (result == kResultTrue)
{
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:
...
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 keyswitches):
int32 MyExampleController::getKeyswitchCount (
int32 busIndex,
int16 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)
{
if (busIndex == 0 && channel == 0 && (keySwitchIndex == 0 || keySwitchIndex == 1)
{
memset (&info, 0, sizeof (KeyswitchInfo));
info.typeId = kKeyRangeTypeID;
if (keySwitchIndex == 0)
{
USTRING (
"Accentuation").copyTo (info.title, 128);
USTRING (
"Acc").copyTo (info.shortTitle, 128);
info.keyswitchMin = 12;
info.keyswitchMax = 13;
info.keyRemapped = 24;
}
else
{
USTRING (
"Softly").copyTo (info.title, 128);
USTRING (
"Soft").copyTo (info.shortTitle, 128);
info.keyswitchMin = 14;
info.keyswitchMax = 15;
info.keyRemapped = 26;
}
info.unitID = -1;
info.flags = 0;
}
}
- Last step, in the processor component we have to adapt the process call to switch to the wanted articulation:
tresult MyExampleProcessor::process (ProcessData& data)
{
....
IEventList* inputEvents = data.inputEvents;
if (inputEvents)
{
Event e;
int32 numEvents = inputEvents->getEventCount ();
for (
int32 i = 0; i < numEvents; i++)
{
{
switch (e.type)
{
case Event::kNoteOnEvent:
{
switch (e.noteOn.pitch)
{
case 12:
case 13:
case 24:
currentLayer = kAccentuationLayer;
break;
case 14:
case 15:
case 26:
currentLayer = kSoftlyLayer;
break;
default:
...
}
} break;
case Event::kNoteOffEvent:
{
switch (e.noteOff.pitch)
{
case 12:
case 13:
case 24:
case 14:
case 15:
case 26:
currentLayer = kDefaultLayer;
break;
default:
...
}
} break;
....
}
}
}
}
...
}
That is it!
Back to Contents