Implement VST2 SysEx events

Apparently these _are_ actually used. Sometimes.
This commit is contained in:
Robbert van der Helm
2021-08-04 21:37:54 +02:00
parent dceafd3016
commit 9160de6483
5 changed files with 54 additions and 7 deletions
+16 -1
View File
@@ -41,13 +41,28 @@ DynamicVstEvents::DynamicVstEvents(const VstEvents& c_events)
// Copy from the C-style array into a vector for serialization
for (int i = 0; i < c_events.numEvents; i++) {
events[i] = *c_events.events[i];
// If we encounter a SysEx event, also store the payload data in an
// associative list (so we can potentially still avoid allocations)
const auto sysex_event =
reinterpret_cast<VstMidiSysExEvent*>(c_events.events[i]);
if (sysex_event->type == kVstSysExType) {
sysex_data.emplace_back(
i, std::string(sysex_event->sysexDump, sysex_event->byteSize));
}
}
}
VstEvents& DynamicVstEvents::as_c_events() {
// As explained in `vst_events_buffer`'s docstring we have to build the
// `VstEvents` struct by hand on the heap since it's actually a dynamically
// sized object
// sized object. If we encountered any SysEx events, then we'll need to
// update the pointers in `events` to point to the correct data location.
for (const auto& [event_idx, data] : sysex_data) {
auto& sysex_event =
reinterpret_cast<VstMidiSysExEvent&>(events[event_idx]);
sysex_event.sysexDump = const_cast<char*>(data.data());
}
// First we need to allocate enough memory for the entire object. The events
// are stored as pointers to objects in the `events` vector that we sent
+22 -2
View File
@@ -89,7 +89,9 @@ struct ChunkData {
* A wrapper around `VstEvents` that stores the data in a vector instead of a
* C-style array. Needed until bitsery supports C-style arrays
* https://github.com/fraillt/bitsery/issues/28. An advantage of this approach
* is that RAII will handle cleanup for us.
* is that RAII will handle cleanup for us. We'll handle both regular MIDI
* events as well as SysEx here. If we somehow encounter a different kind of
* event, we'll just treat it as regular MIDI and print a warning.
*
* Before serialization the events are read from a C-style array into a vector
* using this class's constructor, and after deserializing the original struct
@@ -116,14 +118,32 @@ class alignas(16) DynamicVstEvents {
/**
* MIDI events are sent just before the audio processing call. Technically a
* host can call `effProcessEvents()` multiple times, but in practice this
* of course doesn't happen.
* of course doesn't happen. In case the host or plugin sent SysEx data, we
* will need to update the `dumpBytes` field to point to the data stored in
* the `sysex_data` field before dumping everything to `vst_events_buffer`.
*/
boost::container::small_vector<VstEvent, 64> events;
/**
* If the host or a plugin sends SysEx data, then we will store that data
* here. I've only seen this happen with the combination of an Arturia
* MiniLab keyboard, REAPER, and D16 Group plugins. We'll store this as an
* associative list of `(index, data)` pairs, where `index` corresponds to
* an event in `events`. There's no 'small_unordered_map' in
* Boost.Container, so this will have to do.
*/
boost::container::small_vector<std::pair<native_size_t, std::string>, 8>
sysex_data;
template <typename S>
void serialize(S& s) {
s.container(events, max_midi_events,
[](S& s, VstEvent& event) { s.container1b(event.dump); });
s.container(sysex_data, max_midi_events,
[](S& s, std::pair<native_size_t, std::string>& pair) {
s.value8b(pair.first);
s.text1b(pair.second, max_buffer_size);
});
}
private: