diff --git a/src/common/serialization/vst3/base.h b/src/common/serialization/vst3/base.h index a2437457..5a1b12d0 100644 --- a/src/common/serialization/vst3/base.h +++ b/src/common/serialization/vst3/base.h @@ -28,8 +28,9 @@ // Yet Another layer of includes, but these are some VST3-specific typedefs that // we'll need for all of our interfaces -using Steinberg::TBool, Steinberg::int8, Steinberg::int32, Steinberg::int64, - Steinberg::uint32, Steinberg::uint64, Steinberg::tresult; +using Steinberg::TBool, Steinberg::int8, Steinberg::int16, Steinberg::int32, + Steinberg::int64, Steinberg::uint8, Steinberg::uint32, Steinberg::uint64, + Steinberg::tresult; /** * Both `TUID` (`int8_t[16]`) and `FIDString` (`char*`) are hard to work with diff --git a/src/common/serialization/vst3/event-list.h b/src/common/serialization/vst3/event-list.h new file mode 100644 index 00000000..ac3e93d7 --- /dev/null +++ b/src/common/serialization/vst3/event-list.h @@ -0,0 +1,294 @@ +// yabridge: a Wine VST bridge +// Copyright (C) 2020 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include +#include + +#include "base.h" + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-virtual-dtor" + +/** + * A wrapper around `DataEvent` for serialization purposes, as this event + * contains a heap array. + */ +struct YaDataEvent { + YaDataEvent(); + + /** + * Copy data from an existing `DataEvent`. + */ + YaDataEvent(const Steinberg::Vst::DataEvent& event); + + /** + * Reconstruct a `DataEvent` from this object. + * + * @note This object may contain pointers to data stored in this object, and + * must thus not outlive it. + */ + Steinberg::Vst::DataEvent get() const; + + uint32 type; + std::vector data; + + template + void serialize(S& s) { + s.value4b(type); + s.container1b(data, 1 << 16); + } +}; + +/** + * A wrapper around `NoteExpressionTextEvent` for serialization purposes, as + * this event contains a heap array. + */ +struct YaNoteExpressionTextEvent { + YaNoteExpressionTextEvent(); + + /** + * Copy data from an existing `NoteExpressionTextEvent`. + */ + YaNoteExpressionTextEvent( + const Steinberg::Vst::NoteExpressionTextEvent& event); + + /** + * Reconstruct a `NoteExpressionTextEvent` from this object. + * + * @note This object may contain pointers to data stored in this object, and + * must thus not outlive it. + */ + Steinberg::Vst::NoteExpressionTextEvent get() const; + + Steinberg::Vst::NoteExpressionTypeID type_id; + int32 note_id; + + std::u16string text; + + template + void serialize(S& s) { + s.value4b(type_id); + s.value4b(note_id); + s.container2b(text, 1 << 16); + } +}; + +/** + * A wrapper around `ChordEvent` for serialization purposes, as this event + * contains a heap array. + */ +struct YaChordEvent { + YaChordEvent(); + + /** + * Copy data from an existing `ChordEvent`. + */ + YaChordEvent(const Steinberg::Vst::ChordEvent& event); + + /** + * Reconstruct a `ChordEvent` from this object. + * + * @note This object may contain pointers to data stored in this object, and + * must thus not outlive it. + */ + Steinberg::Vst::ChordEvent get() const; + + int16 root; + int16 bass_note; + int16 mask; + + std::u16string text; + + template + void serialize(S& s) { + s.value2b(root); + s.value2b(bass_note); + s.value2b(mask); + s.container2b(text, 1 << 16); + } +}; + +/** + * A wrapper around `ScaleEvent` for serialization purposes, as this event + * contains a heap array. + */ +struct YaScaleEvent { + YaScaleEvent(); + + /** + * Copy data from an existing `ScaleEvent`. + */ + YaScaleEvent(const Steinberg::Vst::ScaleEvent& event); + + /** + * Reconstruct a `ScaleEvent` from this object. + * + * @note This object may contain pointers to data stored in this object, and + * must thus not outlive it. + */ + Steinberg::Vst::ScaleEvent get() const; + + int16 root; + int16 mask; + + std::u16string text; + + template + void serialize(S& s) { + s.value2b(root); + s.value2b(mask); + s.container2b(text, 1 << 16); + } +}; + +/** + * A wrapper around `Event` for serialization purposes, as some event types + * include heap pointers. + */ +struct YaEvent { + YaEvent(); + + /** + * Copy data from an `Event`. + */ + YaEvent(const Steinberg::Vst::Event& event); + + /** + * Reconstruct an `Event` from this object. + * + * @note This object may contain pointers to data stored in this object, and + * must thus not outlive it. + */ + Steinberg::Vst::Event get() const; + + // These fields directly reflect those from `Event` + int32 bus_index; + int32 sample_offset; + Steinberg::Vst::TQuarterNotes ppq_position; + Steinberg::Vst::Event::EventFlags flags; + + // `Event` stores an event type and a union, we'll encode both in a variant. + // We can use simple types directly, and we need serializable wrappers + // around move event types with heap pointers. + std::variant + payload; + + template + void serialize(S& s) { + s.value4b(bus_index); + s.value4b(sample_offset); + s.value8b(ppq_position); + s.value2b(flags); + s.ext(payload, bitsery::ext::StdVariant{}); + } +}; + +/** + * Wraps around `IEventList` for serialization purposes. Used in + * `YaProcessData`. + */ +class YaEventList : public Steinberg::Vst::IEventList { + public: + /** + * Default constructor with an empty event list. The plugin can use this to + * output data. + */ + YaEventList(); + + /** + * Read data from an existing `IEventList` object. + */ + YaEventList(Steinberg::Vst::IEventList& original_events); + + ~YaEventList(); + + DECLARE_FUNKNOWN_METHODS + + // From `IEventList` + virtual int32 PLUGIN_API getEventCount() override; + // We're making the assumption here that events are immutable (which should + // be the case, but it's never mentioned anywhere) + virtual tresult PLUGIN_API + getEvent(int32 index, Steinberg::Vst::Event& e /*out*/) override; + virtual tresult PLUGIN_API + addEvent(Steinberg::Vst::Event& e /*in*/) override; + + template + void serialize(S& s) { + s.container(events, 1 << 16); + } + + private: + std::vector events; +}; + +namespace Steinberg { +namespace Vst { +template +void serialize(S& s, NoteOnEvent& event) { + s.value2b(event.channel); + s.value2b(event.pitch); + s.value4b(event.tuning); + s.value4b(event.velocity); + s.value4b(event.length); + s.value4b(event.noteId); +} + +template +void serialize(S& s, NoteOffEvent& event) { + s.value2b(event.channel); + s.value2b(event.pitch); + s.value4b(event.velocity); + s.value4b(event.noteId); + s.value4b(event.tuning); +} + +template +void serialize(S& s, PolyPressureEvent& event) { + s.value2b(event.channel); + s.value2b(event.pitch); + s.value4b(event.pressure); + s.value4b(event.noteId); +} + +template +void serialize(S& s, NoteExpressionValueEvent& event) { + s.value4b(event.typeId); + s.value4b(event.noteId); + s.value8b(event.value); +} + +template +void serialize(S& s, LegacyMIDICCOutEvent& event) { + s.value1b(event.controlNumber); + s.value1b(event.channel); + s.value1b(event.value); + s.value1b(event.value2); +} +} // namespace Vst +} // namespace Steinberg + +#pragma GCC diagnostic pop