diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h index 32047ca4..6f5bc45e 100644 --- a/src/common/serialization/clap.h +++ b/src/common/serialization/clap.h @@ -22,6 +22,7 @@ #include "../bitsery/ext/message-reference.h" #include "../utils.h" +#include "clap/events.h" #include "clap/ext/audio-ports.h" #include "clap/ext/gui.h" #include "clap/ext/latency.h" diff --git a/src/common/serialization/clap/events.h b/src/common/serialization/clap/events.h new file mode 100644 index 00000000..f93d1bbc --- /dev/null +++ b/src/common/serialization/clap/events.h @@ -0,0 +1,307 @@ +// 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 "../bitsery/ext/in-place-variant.h" +#include "../common.h" + +namespace clap { +namespace events { + +/** + * The actual event data. `clap::events::Event` stores these as a variant + * alongside the timestamp and flags for the original event header. + */ +namespace payload { + +/** + * `Note` can be a variety of note events. + */ +enum class NoteEventType : uint8_t { + On, + Off, + Choke, + End, +}; + +/** + * The payload for `clap_event_note`. + */ +struct Note { + NoteEventType event_type; + + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; + + double velocity; + + template + void serialize(S& s) { + s.value1b(event_type); + s.value4b(note_id); + s.value2b(port_index); + s.value2b(channel); + s.value2b(key); + s.value8b(velocity); + } +}; + +/** + * The payload for `clap_event_note_expression`. + */ +struct NoteExpression { + clap_note_expression expression_id; + + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; + + double value; + + template + void serialize(S& s) { + s.value4b(expression_id); + s.value4b(note_id); + s.value2b(port_index); + s.value2b(channel); + s.value2b(key); + s.value8b(value); + } +}; + +/** + * The payload for `clap_event_param_value`. + */ +struct ParamValue { + clap_id param_id; + // This 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. + native_size_t cookie; + + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; + + double value; + + template + void serialize(S& s) { + s.value4b(param_id); + s.value8b(cookie); + s.value4b(note_id); + s.value2b(port_index); + s.value2b(channel); + s.value2b(key); + s.value8b(value); + } +}; + +/** + * The payload for `clap_event_param_mod`. + */ +struct ParamMod { + clap_id param_id; + // Same as above + native_size_t cookie; + + int32_t note_id; + int16_t port_index; + int16_t channel; + int16_t key; + + double amount; + + template + void serialize(S& s) { + s.value4b(param_id); + s.value8b(cookie); + s.value4b(note_id); + s.value2b(port_index); + s.value2b(channel); + s.value2b(key); + s.value8b(amount); + } +}; + +/** + * `ParamGesture` can both be a `CLAP_EVENT_PARAM_GESTURE_BEGIN` and a + * `CLAP_EVENT_PARAM_GESTURE_END`. + */ +enum class ParamGestureType : uint8_t { + Begin, + End, +}; + +/** + * The payload for `clap_event_param_gesture`. + */ +struct ParamGesture { + ParamGestureType gesture_type; + clap_id param_id; + + template + void serialize(S& s) { + s.value1b(gesture_type); + s.value4b(param_id); + } +}; + +/** + * The payload for `clap_event_transport`. + */ +struct Transport { + uint32_t flags; + + clap_beattime song_pos_beats; + clap_sectime song_pos_seconds; + + double tempo; + double tempo_inc; + + clap_beattime loop_start_beats; + clap_beattime loop_end_beats; + clap_sectime loop_start_seconds; + clap_sectime loop_end_seconds; + + clap_beattime bar_start; + int32_t bar_number; + + uint16_t tsig_num; + uint16_t tsig_denom; + + template + void serialize(S& s) { + s.value4b(flags); + s.value8b(song_pos_beats); + s.value8b(song_pos_seconds); + s.value8b(tempo); + s.value8b(tempo_inc); + s.value8b(loop_start_beats); + s.value8b(loop_end_beats); + s.value8b(loop_start_seconds); + s.value8b(loop_end_seconds); + s.value8b(bar_start); + s.value4b(bar_number); + s.value2b(tsig_num); + s.value2b(tsig_denom); + } +}; + +/** + * The payload for `clap_event_midi`. + */ +struct Midi { + uint16_t port_index; + uint8_t data[3]; + + template + void serialize(S& s) { + s.value2b(port_index); + s.container4b(data); + } +}; + +/** + * The payload for `clap_event_midi_sysex`. + */ +struct MidiSysex { + uint16_t port_index; + // 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.value2b(port_index); + s.text1b(buffer, 1 << 16); + } +}; + +/** + * The payload for `clap_event_midi2`. + */ +struct Midi2 { + uint16_t port_index; + uint32_t data[4]; + + template + void serialize(S& s) { + s.value2b(port_index); + s.container4b(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 { + /** + * The time from the event header. + */ + uint32_t time; + /** + * The flags from the event header. + */ + uint32_t flags; + + /** + * The actual event data. This also encodes the type, size, and space ID. + */ + std::variant + payload; + + template + void serialize(S& s) { + s.value4b(time); + s.value4b(flags); + s.ext(payload, bitsery::ext::InPlaceVariant{}); + } +}; + +} // namespace events +} // namespace clap