mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-24 01:54:22 +00:00
VST3: Add support for legacy CC output events
This commit is contained in:
parent
7dca2fb488
commit
4a613dfad2
2 changed files with 228 additions and 80 deletions
|
|
@ -2531,17 +2531,16 @@ private:
|
|||
auto plugInOutputChannels = pluginInstance->getTotalNumOutputChannels();
|
||||
|
||||
// Wavelab workaround: wave-lab lies on the number of inputs/outputs so re-count here
|
||||
int vstInputs;
|
||||
for (vstInputs = 0; vstInputs < data.numInputs; ++vstInputs)
|
||||
if (getPointerForAudioBus<FloatType> (data.inputs[vstInputs]) == nullptr
|
||||
&& data.inputs[vstInputs].numChannels > 0)
|
||||
break;
|
||||
const auto countValidChannels = [] (Vst::AudioBusBuffers* buffers, int32 num)
|
||||
{
|
||||
return int (std::distance (buffers, std::find_if (buffers, buffers + num, [] (Vst::AudioBusBuffers& buf)
|
||||
{
|
||||
return getPointerForAudioBus<FloatType> (buf) == nullptr && buf.numChannels > 0;
|
||||
})));
|
||||
};
|
||||
|
||||
int vstOutputs;
|
||||
for (vstOutputs = 0; vstOutputs < data.numOutputs; ++vstOutputs)
|
||||
if (getPointerForAudioBus<FloatType> (data.outputs[vstOutputs]) == nullptr
|
||||
&& data.outputs[vstOutputs].numChannels > 0)
|
||||
break;
|
||||
const auto vstInputs = countValidChannels (data.inputs, data.numInputs);
|
||||
const auto vstOutputs = countValidChannels (data.outputs, data.numOutputs);
|
||||
|
||||
{
|
||||
auto n = jmax (vstOutputs, getNumAudioBuses (false));
|
||||
|
|
|
|||
|
|
@ -468,45 +468,19 @@ public:
|
|||
//==============================================================================
|
||||
static void toMidiBuffer (MidiBuffer& result, Steinberg::Vst::IEventList& eventList)
|
||||
{
|
||||
const int32 numEvents = eventList.getEventCount();
|
||||
const auto numEvents = eventList.getEventCount();
|
||||
|
||||
for (Steinberg::int32 i = 0; i < numEvents; ++i)
|
||||
{
|
||||
Steinberg::Vst::Event e;
|
||||
|
||||
if (eventList.getEvent (i, e) == Steinberg::kResultOk)
|
||||
{
|
||||
switch (e.type)
|
||||
{
|
||||
case Steinberg::Vst::Event::kNoteOnEvent:
|
||||
result.addEvent (MidiMessage::noteOn (createSafeChannel (e.noteOn.channel),
|
||||
createSafeNote (e.noteOn.pitch),
|
||||
(Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity)),
|
||||
e.sampleOffset);
|
||||
break;
|
||||
if (eventList.getEvent (i, e) != Steinberg::kResultOk)
|
||||
continue;
|
||||
|
||||
case Steinberg::Vst::Event::kNoteOffEvent:
|
||||
result.addEvent (MidiMessage::noteOff (createSafeChannel (e.noteOff.channel),
|
||||
createSafeNote (e.noteOff.pitch),
|
||||
(Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity)),
|
||||
e.sampleOffset);
|
||||
break;
|
||||
const auto message = toMidiMessage (e);
|
||||
|
||||
case Steinberg::Vst::Event::kPolyPressureEvent:
|
||||
result.addEvent (MidiMessage::channelPressureChange (createSafeChannel (e.polyPressure.channel),
|
||||
denormaliseToMidiValue (e.polyPressure.pressure)),
|
||||
e.sampleOffset);
|
||||
break;
|
||||
|
||||
case Steinberg::Vst::Event::kDataEvent:
|
||||
result.addEvent (MidiMessage::createSysExMessage (e.data.bytes, (int) e.data.size),
|
||||
e.sampleOffset);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (message.isValid)
|
||||
result.addEvent (message.item, e.sampleOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -546,49 +520,14 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
Steinberg::Vst::Event e{};
|
||||
auto maybeEvent = createVstEvent (msg, metadata.data);
|
||||
|
||||
if (msg.isNoteOn())
|
||||
{
|
||||
e.type = Steinberg::Vst::Event::kNoteOnEvent;
|
||||
e.noteOn.channel = createSafeChannel (msg.getChannel());
|
||||
e.noteOn.pitch = createSafeNote (msg.getNoteNumber());
|
||||
e.noteOn.velocity = normaliseMidiValue (msg.getVelocity());
|
||||
e.noteOn.length = 0;
|
||||
e.noteOn.tuning = 0.0f;
|
||||
e.noteOn.noteId = -1;
|
||||
}
|
||||
else if (msg.isNoteOff())
|
||||
{
|
||||
e.type = Steinberg::Vst::Event::kNoteOffEvent;
|
||||
e.noteOff.channel = createSafeChannel (msg.getChannel());
|
||||
e.noteOff.pitch = createSafeNote (msg.getNoteNumber());
|
||||
e.noteOff.velocity = normaliseMidiValue (msg.getVelocity());
|
||||
e.noteOff.tuning = 0.0f;
|
||||
e.noteOff.noteId = -1;
|
||||
}
|
||||
else if (msg.isSysEx())
|
||||
{
|
||||
e.type = Steinberg::Vst::Event::kDataEvent;
|
||||
e.data.bytes = metadata.data + 1;
|
||||
e.data.size = (uint32) msg.getSysExDataSize();
|
||||
e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx;
|
||||
}
|
||||
else if (msg.isChannelPressure())
|
||||
{
|
||||
e.type = Steinberg::Vst::Event::kPolyPressureEvent;
|
||||
e.polyPressure.channel = createSafeChannel (msg.getChannel());
|
||||
e.polyPressure.pitch = createSafeNote (msg.getNoteNumber());
|
||||
e.polyPressure.pressure = normaliseMidiValue (msg.getChannelPressureValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! maybeEvent.isValid)
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& e = maybeEvent.item;
|
||||
e.busIndex = 0;
|
||||
e.sampleOffset = metadata.samplePosition;
|
||||
|
||||
result.addEvent (e);
|
||||
}
|
||||
}
|
||||
|
|
@ -606,6 +545,216 @@ private:
|
|||
static float normaliseMidiValue (int value) noexcept { return jlimit (0.0f, 1.0f, (float) value / 127.0f); }
|
||||
static int denormaliseToMidiValue (float value) noexcept { return roundToInt (jlimit (0.0f, 127.0f, value * 127.0f)); }
|
||||
|
||||
static Steinberg::Vst::Event createNoteOnEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
Steinberg::Vst::Event e{};
|
||||
e.type = Steinberg::Vst::Event::kNoteOnEvent;
|
||||
e.noteOn.channel = createSafeChannel (msg.getChannel());
|
||||
e.noteOn.pitch = createSafeNote (msg.getNoteNumber());
|
||||
e.noteOn.velocity = normaliseMidiValue (msg.getVelocity());
|
||||
e.noteOn.length = 0;
|
||||
e.noteOn.tuning = 0.0f;
|
||||
e.noteOn.noteId = -1;
|
||||
return e;
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createNoteOffEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
Steinberg::Vst::Event e{};
|
||||
e.type = Steinberg::Vst::Event::kNoteOffEvent;
|
||||
e.noteOff.channel = createSafeChannel (msg.getChannel());
|
||||
e.noteOff.pitch = createSafeNote (msg.getNoteNumber());
|
||||
e.noteOff.velocity = normaliseMidiValue (msg.getVelocity());
|
||||
e.noteOff.tuning = 0.0f;
|
||||
e.noteOff.noteId = -1;
|
||||
return e;
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createSysExEvent (const MidiMessage& msg, const uint8* midiEventData) noexcept
|
||||
{
|
||||
Steinberg::Vst::Event e{};
|
||||
e.type = Steinberg::Vst::Event::kDataEvent;
|
||||
e.data.bytes = midiEventData + 1;
|
||||
e.data.size = (uint32) msg.getSysExDataSize();
|
||||
e.data.type = Steinberg::Vst::DataEvent::kMidiSysEx;
|
||||
return e;
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createLegacyMIDIEvent (int channel, int controlNumber, int value, int value2 = 0)
|
||||
{
|
||||
Steinberg::Vst::Event e{};
|
||||
e.type = Steinberg::Vst::Event::kLegacyMIDICCOutEvent;
|
||||
e.midiCCOut.channel = int8 (createSafeChannel (channel));
|
||||
e.midiCCOut.controlNumber = uint8 (jlimit (0, 255, controlNumber));
|
||||
e.midiCCOut.value = int8 (createSafeNote (value));
|
||||
e.midiCCOut.value2 = int8 (createSafeNote (value2));
|
||||
return e;
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createChannelPressureEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
return createLegacyMIDIEvent (msg.getChannel(),
|
||||
Steinberg::Vst::kAfterTouch,
|
||||
msg.getChannelPressureValue());
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createControllerEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
return createLegacyMIDIEvent (msg.getChannel(),
|
||||
msg.getControllerNumber(),
|
||||
msg.getControllerValue());
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createCtrlPolyPressureEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
return createLegacyMIDIEvent (msg.getChannel(),
|
||||
Steinberg::Vst::kCtrlPolyPressure,
|
||||
msg.getNoteNumber(),
|
||||
msg.getAfterTouchValue());
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createPitchWheelEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
return createLegacyMIDIEvent (msg.getChannel(),
|
||||
Steinberg::Vst::kPitchBend,
|
||||
msg.getRawData()[1],
|
||||
msg.getRawData()[2]);
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createProgramChangeEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
return createLegacyMIDIEvent (msg.getChannel(),
|
||||
Steinberg::Vst::kCtrlProgramChange,
|
||||
msg.getProgramChangeNumber());
|
||||
}
|
||||
|
||||
static Steinberg::Vst::Event createCtrlQuarterFrameEvent (const MidiMessage& msg) noexcept
|
||||
{
|
||||
return createLegacyMIDIEvent (msg.getChannel(),
|
||||
Steinberg::Vst::kCtrlQuarterFrame,
|
||||
msg.getQuarterFrameValue());
|
||||
}
|
||||
|
||||
template <typename Item>
|
||||
struct BasicOptional final
|
||||
{
|
||||
BasicOptional() noexcept = default;
|
||||
BasicOptional (const Item& i) noexcept : item { i }, isValid { true } {}
|
||||
|
||||
Item item;
|
||||
bool isValid{};
|
||||
};
|
||||
|
||||
static BasicOptional<Steinberg::Vst::Event> createVstEvent (const MidiMessage& msg,
|
||||
const uint8* midiEventData) noexcept
|
||||
{
|
||||
if (msg.isNoteOn())
|
||||
return createNoteOnEvent (msg);
|
||||
|
||||
if (msg.isNoteOff())
|
||||
return createNoteOffEvent (msg);
|
||||
|
||||
if (msg.isSysEx())
|
||||
return createSysExEvent (msg, midiEventData);
|
||||
|
||||
if (msg.isChannelPressure())
|
||||
return createChannelPressureEvent (msg);
|
||||
|
||||
if (msg.isPitchWheel())
|
||||
return createPitchWheelEvent (msg);
|
||||
|
||||
if (msg.isProgramChange())
|
||||
return createProgramChangeEvent (msg);
|
||||
|
||||
if (msg.isController())
|
||||
return createControllerEvent (msg);
|
||||
|
||||
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);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static BasicOptional<MidiMessage> toMidiMessage (const Steinberg::Vst::LegacyMIDICCOutEvent& e)
|
||||
{
|
||||
if (e.controlNumber <= 127)
|
||||
return MidiMessage::controllerEvent (createSafeChannel (int16 (e.channel)),
|
||||
createSafeNote (int16 (e.controlNumber)),
|
||||
createSafeNote (int16 (e.value)));
|
||||
|
||||
switch (e.controlNumber)
|
||||
{
|
||||
case Steinberg::Vst::kAfterTouch:
|
||||
return MidiMessage::channelPressureChange (createSafeChannel (int16 (e.channel)),
|
||||
createSafeNote (int16 (e.value)));
|
||||
|
||||
case Steinberg::Vst::kPitchBend:
|
||||
return MidiMessage::pitchWheel (createSafeChannel (int16 (e.channel)),
|
||||
(e.value & 0x7f) | ((e.value2 & 0x7f) << 7));
|
||||
|
||||
case Steinberg::Vst::kCtrlProgramChange:
|
||||
return MidiMessage::programChange (createSafeChannel (int16 (e.channel)),
|
||||
createSafeNote (int16 (e.value)));
|
||||
|
||||
case Steinberg::Vst::kCtrlQuarterFrame:
|
||||
return MidiMessage::quarterFrame (createSafeChannel (int16 (e.channel)),
|
||||
createSafeNote (int16 (e.value)));
|
||||
|
||||
case Steinberg::Vst::kCtrlPolyPressure:
|
||||
return MidiMessage::aftertouchChange (createSafeChannel (int16 (e.channel)),
|
||||
createSafeNote (int16 (e.value)),
|
||||
createSafeNote (int16 (e.value2)));
|
||||
|
||||
default:
|
||||
// If this is hit, we're trying to convert a LegacyMIDICCOutEvent with an unknown controlNumber.
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static BasicOptional<MidiMessage> toMidiMessage (const Steinberg::Vst::Event& e)
|
||||
{
|
||||
switch (e.type)
|
||||
{
|
||||
case Steinberg::Vst::Event::kNoteOnEvent:
|
||||
return MidiMessage::noteOn (createSafeChannel (e.noteOn.channel),
|
||||
createSafeNote (e.noteOn.pitch),
|
||||
(Steinberg::uint8) denormaliseToMidiValue (e.noteOn.velocity));
|
||||
|
||||
case Steinberg::Vst::Event::kNoteOffEvent:
|
||||
return MidiMessage::noteOff (createSafeChannel (e.noteOff.channel),
|
||||
createSafeNote (e.noteOff.pitch),
|
||||
(Steinberg::uint8) denormaliseToMidiValue (e.noteOff.velocity));
|
||||
|
||||
case Steinberg::Vst::Event::kPolyPressureEvent:
|
||||
return MidiMessage::aftertouchChange (createSafeChannel (e.polyPressure.channel),
|
||||
createSafeNote (e.polyPressure.pitch),
|
||||
(Steinberg::uint8) denormaliseToMidiValue (e.polyPressure.pressure));
|
||||
|
||||
case Steinberg::Vst::Event::kDataEvent:
|
||||
return MidiMessage::createSysExMessage (e.data.bytes, (int) e.data.size);
|
||||
|
||||
case Steinberg::Vst::Event::kLegacyMIDICCOutEvent:
|
||||
return toMidiMessage (e.midiCCOut);
|
||||
|
||||
case Steinberg::Vst::Event::kNoteExpressionValueEvent:
|
||||
case Steinberg::Vst::Event::kNoteExpressionTextEvent:
|
||||
case Steinberg::Vst::Event::kChordEvent:
|
||||
case Steinberg::Vst::Event::kScaleEvent:
|
||||
return {};
|
||||
}
|
||||
|
||||
// If this is hit, we've been sent an event type that doesn't exist in the VST3 spec.
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct Vst3MidiControlEvent
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue