// yabridge: a Wine plugin bridge // Copyright (C) 2020-2022 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 . #include "event-list.h" #include "../../utils.h" YaDataEvent::YaDataEvent() noexcept {} YaDataEvent::YaDataEvent(const Steinberg::Vst::DataEvent& event) noexcept : type(event.type), buffer(event.bytes, event.bytes + event.size) {} Steinberg::Vst::DataEvent YaDataEvent::get() const noexcept { static_assert(sizeof(decltype(buffer)::value_type) == sizeof(uint8)); return Steinberg::Vst::DataEvent{ .size = static_cast(buffer.size()), .type = type, .bytes = reinterpret_cast(buffer.data())}; } YaNoteExpressionTextEvent::YaNoteExpressionTextEvent() noexcept {} YaNoteExpressionTextEvent::YaNoteExpressionTextEvent( const Steinberg::Vst::NoteExpressionTextEvent& event) noexcept : type_id(event.typeId), note_id(event.noteId), text(tchar_pointer_to_u16string(event.text, event.textLen)) {} Steinberg::Vst::NoteExpressionTextEvent YaNoteExpressionTextEvent::get() const noexcept { return Steinberg::Vst::NoteExpressionTextEvent{ .typeId = type_id, .noteId = note_id, .textLen = static_cast(text.size()), .text = u16string_to_tchar_pointer(text)}; } YaChordEvent::YaChordEvent() noexcept {} YaChordEvent::YaChordEvent(const Steinberg::Vst::ChordEvent& event) noexcept : root(event.root), bass_note(event.bassNote), mask(event.mask), text(tchar_pointer_to_u16string(event.text, event.textLen)) {} Steinberg::Vst::ChordEvent YaChordEvent::get() const noexcept { return Steinberg::Vst::ChordEvent{ .root = root, .bassNote = bass_note, .mask = mask, .textLen = static_cast(text.size()), .text = u16string_to_tchar_pointer(text)}; } YaScaleEvent::YaScaleEvent() noexcept {} YaScaleEvent::YaScaleEvent(const Steinberg::Vst::ScaleEvent& event) noexcept : root(event.root), mask(event.mask), text(tchar_pointer_to_u16string(event.text, event.textLen)) {} Steinberg::Vst::ScaleEvent YaScaleEvent::get() const noexcept { return Steinberg::Vst::ScaleEvent{ .root = root, .mask = mask, .textLen = static_cast(text.size()), .text = u16string_to_tchar_pointer(text)}; } YaEvent::YaEvent() noexcept {} YaEvent::YaEvent(const Steinberg::Vst::Event& event) noexcept : bus_index(event.busIndex), sample_offset(event.sampleOffset), ppq_position(event.ppqPosition), flags(event.flags) { // Now we need the correct event type switch (event.type) { case Steinberg::Vst::Event::kNoteOnEvent: payload = event.noteOn; break; case Steinberg::Vst::Event::kNoteOffEvent: payload = event.noteOff; break; case Steinberg::Vst::Event::kDataEvent: payload = YaDataEvent(event.data); break; case Steinberg::Vst::Event::kPolyPressureEvent: payload = event.polyPressure; break; case Steinberg::Vst::Event::kNoteExpressionValueEvent: payload = event.noteExpressionValue; break; case Steinberg::Vst::Event::kNoteExpressionTextEvent: payload = YaNoteExpressionTextEvent(event.noteExpressionText); break; case Steinberg::Vst::Event::kChordEvent: payload = YaChordEvent(event.chord); break; case Steinberg::Vst::Event::kScaleEvent: payload = YaScaleEvent(event.scale); break; case Steinberg::Vst::Event::kLegacyMIDICCOutEvent: payload = event.midiCCOut; break; default: // XXX: When encountering something we don't know about, should we // throw or silently ignore it? We can't properly log about // this directly from here. break; } } Steinberg::Vst::Event YaEvent::get() const noexcept { // We of course can't fully initialize a field with an untagged union #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wmissing-field-initializers" Steinberg::Vst::Event event{.busIndex = bus_index, .sampleOffset = sample_offset, .ppqPosition = ppq_position, .flags = flags}; #pragma GCC diagnostic pop std::visit( overload{ [&](const Steinberg::Vst::NoteOnEvent& specific_event) { event.type = Steinberg::Vst::Event::kNoteOnEvent; event.noteOn = specific_event; }, [&](const Steinberg::Vst::NoteOffEvent& specific_event) { event.type = Steinberg::Vst::Event::kNoteOffEvent; event.noteOff = specific_event; }, [&](const YaDataEvent& specific_event) { event.type = Steinberg::Vst::Event::kDataEvent; event.data = specific_event.get(); }, [&](const Steinberg::Vst::PolyPressureEvent& specific_event) { event.type = Steinberg::Vst::Event::kPolyPressureEvent; event.polyPressure = specific_event; }, [&](const Steinberg::Vst::NoteExpressionValueEvent& specific_event) { event.type = Steinberg::Vst::Event::kNoteExpressionValueEvent; event.noteExpressionValue = specific_event; }, [&](const YaNoteExpressionTextEvent& specific_event) { event.type = Steinberg::Vst::Event::kNoteExpressionTextEvent; event.noteExpressionText = specific_event.get(); }, [&](const YaChordEvent& specific_event) { event.type = Steinberg::Vst::Event::kChordEvent; event.chord = specific_event.get(); }, [&](const YaScaleEvent& specific_event) { event.type = Steinberg::Vst::Event::kScaleEvent; event.scale = specific_event.get(); }, [&](const Steinberg::Vst::LegacyMIDICCOutEvent& specific_event) { event.type = Steinberg::Vst::Event::kLegacyMIDICCOutEvent; event.midiCCOut = specific_event; }}, payload); return event; } YaEventList::YaEventList() noexcept { FUNKNOWN_CTOR } void YaEventList::clear() noexcept { events_.clear(); } void YaEventList::repopulate(Steinberg::Vst::IEventList& event_list) { // Copy over all events. Everything gets converted to `YaEvent`s. We sadly // can't construct these in place because we don't know the event type yet. events_.clear(); events_.reserve(event_list.getEventCount()); for (int i = 0; i < event_list.getEventCount(); i++) { // We're skipping the `kResultOk` assertions here Steinberg::Vst::Event event; event_list.getEvent(i, event); events_.emplace_back(event); } } YaEventList::~YaEventList() noexcept {FUNKNOWN_DTOR} size_t YaEventList::num_events() const noexcept { return events_.size(); } void YaEventList::write_back_outputs( Steinberg::Vst::IEventList& output_events) const { for (auto& event : events_) { Steinberg::Vst::Event reconstructed_event = event.get(); output_events.addEvent(reconstructed_event); } } #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor" IMPLEMENT_FUNKNOWN_METHODS(YaEventList, Steinberg::Vst::IEventList, Steinberg::Vst::IEventList::iid) #pragma GCC diagnostic pop int32 PLUGIN_API YaEventList::getEventCount() { return static_cast(events_.size()); } tresult PLUGIN_API YaEventList::getEvent(int32 index, Steinberg::Vst::Event& e /*out*/) { if (index < 0 || index >= static_cast(events_.size())) { return Steinberg::kInvalidArgument; } // Reconstructing an event is cheap, but some events may contain pointers to // heap data stored within the `events` vector so this event will still have // the same lifetime as this class e = events_[index].get(); return Steinberg::kResultOk; } tresult PLUGIN_API YaEventList::addEvent(Steinberg::Vst::Event& e /*in*/) { events_.push_back(e); return Steinberg::kResultOk; }