// 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 . #pragma once #include #include #include #include #include #include "../bitsery/ext/in-place-variant.h" #include "../bitsery/ext/native-pointer.h" #include "../bitsery/traits/small-vector.h" #include "../common.h" namespace clap { namespace events { /** * The actual event data. `clap::events::Event` stores these as a variant. * Ideally we'd store only the non-header payload data, but the * `clap_input_events::get()` function requires us to return a pointer to the * header, so if we did that then we'd need to create a second buffer containing * the serialzed events. */ namespace payload { /** * The payload for `clap_event_note`. This is used for multiple event types, * which are encoded through `event.header.type`. */ struct Note { clap_event_note_t event; template void serialize(S& s) { s.object(event.header); s.value4b(event.note_id); s.value2b(event.port_index); s.value2b(event.channel); s.value2b(event.key); s.value8b(event.velocity); } }; /** * The payload for `clap_event_note_expression`. */ struct NoteExpression { clap_event_note_expression_t event; template void serialize(S& s) { s.object(event.header); s.value4b(event.expression_id); s.value4b(event.note_id); s.value2b(event.port_index); s.value2b(event.channel); s.value2b(event.key); s.value8b(event.value); } }; /** * The payload for `clap_event_param_value`. */ struct ParamValue { clap_event_param_value_t event; template void serialize(S& s) { s.object(event.header); s.value4b(event.param_id); // The cookie is a pointer. Using `native_size_t`/the host system's // pointer size here will allow bridged 32-bit plugins to work // correctly. // XXX: This will silently blow up when using 32-bit yabridge on a // 64-bit system with 64-bit plugins, but that's such a specific // use case that we won't even bother. Building 32-bit yabridge // with CLAP support on 64-bit symbols has been disabled to prevent // this from being an issue. s.ext(event.cookie, bitsery::ext::NativePointer{}); s.value4b(event.note_id); s.value2b(event.port_index); s.value2b(event.channel); s.value2b(event.key); s.value8b(event.value); } }; /** * The payload for `clap_event_param_mod`. */ struct ParamMod { clap_event_param_mod_t event; template void serialize(S& s) { s.object(event.header); s.value4b(event.param_id); // Same as the above s.ext(event.cookie, bitsery::ext::NativePointer{}); s.value4b(event.note_id); s.value2b(event.port_index); s.value2b(event.channel); s.value2b(event.key); s.value8b(event.amount); } }; /** * The payload for `clap_event_param_gesture`. This is used for multiple event * types, which are encoded through `event.header.type`. */ struct ParamGesture { clap_event_param_gesture_t event; template void serialize(S& s) { s.object(event.header); s.value4b(event.param_id); } }; /** * The payload for `clap_event_transport`. */ struct Transport { clap_event_transport_t event; template void serialize(S& s) { s.object(event.header); s.value4b(event.flags); s.value8b(event.song_pos_beats); s.value8b(event.song_pos_seconds); s.value8b(event.tempo); s.value8b(event.tempo_inc); s.value8b(event.loop_start_beats); s.value8b(event.loop_end_beats); s.value8b(event.loop_start_seconds); s.value8b(event.loop_end_seconds); s.value8b(event.bar_start); s.value4b(event.bar_number); s.value2b(event.tsig_num); s.value2b(event.tsig_denom); } }; /** * The payload for `clap_event_midi`. */ struct Midi { clap_event_midi_t event; template void serialize(S& s) { s.object(event.header); s.value2b(event.port_index); s.container1b(event.data); } }; /** * The payload for `clap_event_midi_sysex`. */ struct MidiSysex { clap_event_midi_sysex_t event; /** * The actual SysEx event data. The pointer in `event` is set to the string * data after the event has been created. As long as this event is not moved * that pointer will remain valid. * * We're not expecting a lot of SysEx events, and `std::string`'s small * string optimization should make it possible to send small sysex events * without allocations. An alternative that won't allocate as quickly would * be to store the data in a vector and to only store a tag here, but I * don't think it's necessary at the moment. */ std::string buffer; template void serialize(S& s) { s.object(event.header); s.value2b(event.port_index); s.text1b(buffer, 1 << 16); // NOTE: These will need to be set when retrieving the event using // `clap_input_events::get()`. We could set the pointer here, but // in the off chance that there are a lot more events than we can // handle and the vector is reallocated to avoid dropping events, // then these pointers would become dangling. Making sure these // are null until the event is retrieved is probably for the best. event.buffer = nullptr; event.size = 0; } }; /** * The payload for `clap_event_midi2`. */ struct Midi2 { clap_event_midi2_t event; template void serialize(S& s) { s.object(event.header); s.value2b(event.port_index); s.container4b(event.data); } }; } // namespace payload /** * Encodes a CLAP event. These can be parsed from a `clap_event_header_t*` and * reconstructed back to a `clap_event_header`. */ struct alignas(16) Event { /** * Parse a CLAP event. Returns a nullopt if yabridge does not support the * event. */ static std::optional parse(const clap_event_header_t& generic_event); /** * Get the `clap_event_header_t*` representation for this event. The pointer * is valid as long as this struct isn't moved. */ const clap_event_header_t* get() const; /** * The actual event data. These also contain the header because storing the * entire `clap_event_*_t` struct is the only way to serialize the event * list in a way that doesn't require us to create a second event list in * that format after deserializing the events. An alternative would be to * write the event in the proper format to a buffer before returning it from * `clap_input_events::get()`, but that would cause unexpected lifetime * issues. */ mutable std::variant< payload::Note, payload::NoteExpression, payload::ParamValue, payload::ParamMod, payload::ParamGesture, // Most events are about the same length, but having the transport in // here sadly doubles this struct's size // TODO: Pack the events at some point, this will require special // handling for SysEx events payload::Transport, payload::Midi, payload::MidiSysex, payload::Midi2> payload; template void serialize(S& s) { s.ext(payload, bitsery::ext::InPlaceVariant{}); } }; /** * A list storing one or more CLAP events. Can be used for both input and output * events. */ class EventList { public: /** * We only provide a default constructor here, because we need to fill the * existing object with new events every processing cycle to avoid * reallocating a new object every time. */ EventList() noexcept; /** * Read data from a `clap_input_events_t` into this existing object. This * minimizes reallocations by keeping `events_` as is. */ void repopulate(const clap_input_events_t& in_events); /** * Remove all events. Used at the start of the process function for the * output event list. */ void clear() noexcept; /** * Write the stored events to the host's `clap_output_events_t`. */ void write_back_outputs(const clap_output_events_t& out_events) const; /** * Get a `clap_input_events_t` interface for this event list that the plugin * can read the events from. This is only valid as long as this object is * not moved. */ const clap_input_events_t* input_events(); /** * Get a `clap_output_events_t` interface for this event list that the * plugin can push events to. This is only valid as long as this object is * not moved. */ const clap_output_events_t* output_events(); static uint32_t CLAP_ABI in_size(const struct clap_input_events* list); static const clap_event_header_t* CLAP_ABI in_get(const struct clap_input_events* list, uint32_t index); static bool CLAP_ABI out_try_push(const struct clap_output_events* list, const clap_event_header_t* event); /** * Return the number of events we store. Used in debug logs. */ inline size_t size() const noexcept { return events_.size(); } template void serialize(S& s) { s.container(events_, 1 << 16); } private: llvm::SmallVector events_; // These are populated in the `input_events()` and `output_events()` methods clap_input_events_t input_events_vtable_{}; clap_output_events_t output_events_vtable_{}; }; } // namespace events } // namespace clap template void serialize(S& s, clap_event_header_t& event_header) { // Feels a bit weird serializing this, but assuming the host/plugin set it // correctly it will be fine. And this is kind of a host implementation // detail for storing the events in a packed list anyways. s.value4b(event_header.size); s.value4b(event_header.time); s.value2b(event_header.space_id); s.value2b(event_header.type); s.value4b(event_header.flags); }