Skip to content

Commit

Permalink
VST3: Fix issue where aftertouch messages from the host were incorrec…
Browse files Browse the repository at this point in the history
…tly converted to LegacyMIDICCOut messages
  • Loading branch information
reuk committed Sep 24, 2020
1 parent a32b3e7 commit 90664b4
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 21 deletions.
2 changes: 1 addition & 1 deletion modules/juce_audio_basics/midi/juce_MidiMessage.h
Expand Up @@ -401,7 +401,7 @@ class JUCE_API MidiMessage
/** Returns true if the message is an aftertouch event.
For aftertouch events, use the getNoteNumber() method to find out the key
that it applies to, and getAftertouchValue() to find out the amount. Use
that it applies to, and getAfterTouchValue() to find out the amount. Use
getChannel() to find out the channel.
@see getAftertouchValue, getNoteNumber
Expand Down
Expand Up @@ -2657,7 +2657,7 @@ class JuceVST3Component : public Vst::IComponent,

#if JucePlugin_ProducesMidiOutput
if (isMidiOutputBusEnabled && data.outputEvents != nullptr)
MidiEventList::toEventList (*data.outputEvents, midiBuffer);
MidiEventList::pluginToHostEventList (*data.outputEvents, midiBuffer);
#endif

return kResultTrue;
Expand Down
99 changes: 83 additions & 16 deletions modules/juce_audio_processors/format_types/juce_VST3Common.h
Expand Up @@ -401,8 +401,8 @@ class ComSmartPtr
class MidiEventList : public Steinberg::Vst::IEventList
{
public:
MidiEventList() {}
virtual ~MidiEventList() {}
MidiEventList() = default;
virtual ~MidiEventList() = default;

JUCE_DECLARE_VST3_COM_REF_METHODS
JUCE_DECLARE_VST3_COM_QUERY_METHODS
Expand Down Expand Up @@ -455,9 +455,43 @@ class MidiEventList : public Steinberg::Vst::IEventList
}
}

static void hostToPluginEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer,
Steinberg::Vst::IParameterChanges* parameterChanges,
Steinberg::Vst::IMidiMapping* midiMapping)
{
toEventList (result,
midiBuffer,
parameterChanges,
midiMapping,
EventConversionKind::hostToPlugin);
}

static void pluginToHostEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer)
{
toEventList (result,
midiBuffer,
nullptr,
nullptr,
EventConversionKind::pluginToHost);
}

private:
enum class EventConversionKind
{
// Hosted plugins don't expect to receive LegacyMIDICCEvents messages from the host,
// so if we're converting midi from the host to an eventlist, this mode will avoid
// converting to Legacy events where possible.
hostToPlugin,

// If plugins generate MIDI internally, then where possible we should preserve
// these messages as LegacyMIDICCOut events.
pluginToHost
};

static void toEventList (Steinberg::Vst::IEventList& result, MidiBuffer& midiBuffer,
Steinberg::Vst::IParameterChanges* parameterChanges = nullptr,
Steinberg::Vst::IMidiMapping* midiMapping = nullptr)
Steinberg::Vst::IParameterChanges* parameterChanges,
Steinberg::Vst::IMidiMapping* midiMapping,
EventConversionKind kind)
{
enum { maxNumEvents = 2048 }; // Steinberg's Host Checker states that no more than 2048 events are allowed at once
int numEvents = 0;
Expand Down Expand Up @@ -491,7 +525,7 @@ class MidiEventList : public Steinberg::Vst::IEventList
}
}

auto maybeEvent = createVstEvent (msg, metadata.data);
auto maybeEvent = createVstEvent (msg, metadata.data, kind);

if (! maybeEvent.isValid)
continue;
Expand All @@ -503,7 +537,6 @@ class MidiEventList : public Steinberg::Vst::IEventList
}
}

private:
Array<Steinberg::Vst::Event, CriticalSection> events;
Atomic<int> refCount;

Expand Down Expand Up @@ -562,6 +595,17 @@ class MidiEventList : public Steinberg::Vst::IEventList
return e;
}

static Steinberg::Vst::Event createPolyPressureEvent (const MidiMessage& msg)
{
Steinberg::Vst::Event e{};
e.type = Steinberg::Vst::Event::kPolyPressureEvent;
e.polyPressure.channel = createSafeChannel (msg.getChannel());
e.polyPressure.pitch = createSafeNote (msg.getNoteNumber());
e.polyPressure.pressure = normaliseMidiValue (msg.getAfterTouchValue());
e.polyPressure.noteId = -1;
return e;
}

static Steinberg::Vst::Event createChannelPressureEvent (const MidiMessage& msg) noexcept
{
return createLegacyMIDIEvent (msg.getChannel(),
Expand Down Expand Up @@ -617,7 +661,8 @@ class MidiEventList : public Steinberg::Vst::IEventList
};

static BasicOptional<Steinberg::Vst::Event> createVstEvent (const MidiMessage& msg,
const uint8* midiEventData) noexcept
const uint8* midiEventData,
EventConversionKind kind) noexcept
{
if (msg.isNoteOn())
return createNoteOnEvent (msg);
Expand All @@ -643,11 +688,20 @@ class MidiEventList : public Steinberg::Vst::IEventList
if (msg.isQuarterFrame())
return createCtrlQuarterFrameEvent (msg);

// VST3 gives us two ways to communicate poly pressure changes.
// There's a dedicated PolyPressureEvent, and also a LegacyMIDICCOutEvent with a
// `controlNumber` of `kCtrlPolyPressure`. We're sending the LegacyMIDI version.
if (msg.isAftertouch())
return createCtrlPolyPressureEvent (msg);
{
switch (kind)
{
case EventConversionKind::hostToPlugin:
return createPolyPressureEvent (msg);

case EventConversionKind::pluginToHost:
return createCtrlPolyPressureEvent (msg);
}

jassertfalse;
return {};
}

return {};
}
Expand Down Expand Up @@ -738,13 +792,26 @@ class MidiEventList : public Steinberg::Vst::IEventList

static bool toVst3ControlEvent (const MidiMessage& msg, Vst3MidiControlEvent& result)
{
result.controllerNumber = -1;
if (msg.isController())
{
result = { (Steinberg::Vst::CtrlNumber) msg.getControllerNumber(), msg.getControllerValue() / 127.0};
return true;
}

if (msg.isController()) result = { (Steinberg::Vst::CtrlNumber) msg.getControllerNumber(), msg.getControllerValue() / 127.0};
else if (msg.isPitchWheel()) result = { Steinberg::Vst::kPitchBend, msg.getPitchWheelValue() / 16383.0};
else if (msg.isAftertouch()) result = { Steinberg::Vst::kAfterTouch, msg.getAfterTouchValue() / 127.0};
if (msg.isPitchWheel())
{
result = { Steinberg::Vst::kPitchBend, msg.getPitchWheelValue() / 16383.0};
return true;
}

if (msg.isChannelPressure())
{
result = { Steinberg::Vst::kAfterTouch, msg.getChannelPressureValue() / 127.0};
return true;
}

return (result.controllerNumber != -1);
result.controllerNumber = -1;
return false;
}

JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiEventList)
Expand Down
Expand Up @@ -2970,9 +2970,10 @@ class VST3PluginInstance : public AudioPluginInstance
midiOutputs->clear();

if (acceptsMidi())
MidiEventList::toEventList (*midiInputs, midiBuffer,
destination.inputParameterChanges,
midiMapping);
MidiEventList::hostToPluginEventList (*midiInputs,
midiBuffer,
destination.inputParameterChanges,
midiMapping);

destination.inputEvents = midiInputs;
destination.outputEvents = midiOutputs;
Expand Down

0 comments on commit 90664b4

Please sign in to comment.