diff --git a/src/common/serialization.cpp b/src/common/serialization.cpp index 5a4030a4..64cce827 100644 --- a/src/common/serialization.cpp +++ b/src/common/serialization.cpp @@ -32,9 +32,9 @@ VstEvents& DynamicVstEvents::as_c_events() { // 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 // over the socket. - static_assert(std::extent::value == 1); + static_assert(std::extent_v == 1); const size_t buffer_size = - sizeof(VstEvents) + (events.size() - 1) * sizeof(VstEvent*); + sizeof(VstEvents) + ((events.size() - 1) * sizeof(VstEvent*)); vst_events_buffer.resize(buffer_size); // Now we can populate the VLA with pointers to the objects in the `events` @@ -47,3 +47,40 @@ VstEvents& DynamicVstEvents::as_c_events() { return *vst_events; } + +DynamicSpeakerArrangement::DynamicSpeakerArrangement( + const VstSpeakerArrangement& speaker_arrangement) + : flags(speaker_arrangement.flags), + speakers(speaker_arrangement.num_speakers) { + using speaker_type = + std::remove_extent_t; + static_assert(std::is_same_v); + + // Copy from the C-style array into a vector for serialization + speakers.assign( + speaker_arrangement.speakers, + speaker_arrangement.speakers + + (speaker_arrangement.num_speakers * sizeof(speaker_type))); +} + +VstSpeakerArrangement& DynamicSpeakerArrangement::as_c_speaker_arrangement() { + // Just like in `DynamicVstEvents::as_c_events()`, we will use our buffer + // vector to allocate enough heap space and then reconstruct the original + // `VstSpeakerArrangement` object passed to the constructor. + static_assert(std::extent_v == + 2); + const size_t buffer_size = sizeof(VstSpeakerArrangement) + + ((speakers.size() - 2) * sizeof(VstSpeaker)); + speaker_arrangement_buffer.resize(buffer_size); + + // Now we'll just copy over the elements from our vector to the VLA in this + // struct + VstSpeakerArrangement* speaker_arrangement = + reinterpret_cast( + speaker_arrangement_buffer.data()); + speaker_arrangement->flags = flags; + speaker_arrangement->num_speakers = speakers.size(); + std::copy(speakers.begin(), speakers.end(), speaker_arrangement->speakers); + + return *speaker_arrangement; +} diff --git a/src/common/serialization.h b/src/common/serialization.h index 8b3ed9de..e0286dbf 100644 --- a/src/common/serialization.h +++ b/src/common/serialization.h @@ -190,8 +190,9 @@ class alignas(16) DynamicVstEvents { private: /** - * Some buffer we can build a `VstEvents`. This object can be populated with - * contents of the `VstEvents` vector using the `as_c_events()` method. + * Some buffer we can build a `VstEvents` object in. This object can be + * populated with contents of the `VstEvents` vector using the + * `as_c_events()` method. * * The reason why this is necessary is because the `VstEvents` struct is * actually a variable size object. In the definition in @@ -204,6 +205,54 @@ class alignas(16) DynamicVstEvents { std::vector vst_events_buffer; }; +/** + * A wrapper around `VstSpeakerArrangement` that works the same way as the above + * wrapper for `VstEvents`. This is needed because the `VstSpeakerArrangement` + * struct is actually a variable sized array. Even though it will be very + * unlikely that we'll encounter systems with more than 8 speakers, it is + * something we should be able to support. + * + * 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 + * can be reconstructed using the `as_c_speaker_arrangement()` method. + */ +class alignas(16) DynamicSpeakerArrangement { + public: + DynamicSpeakerArrangement(){}; + + explicit DynamicSpeakerArrangement( + const VstSpeakerArrangement& speaker_arrangement); + + /** + * Construct a dynamically sized `VstSpeakerArrangement` struct based on + * this object. + */ + VstSpeakerArrangement& as_c_speaker_arrangement(); + + /** + * The flags field from `VstSpeakerArrangement` + */ + int flags; + + /** + * Information about the speakers in a particular input or output + * configuration. + */ + std::vector speakers; + + private: + /** + * Some buffer we can build a `VstSpeakerArrangement` object in. This object + * can be populated using the `as_c_speaker_arrangement()` method. + * + * This is necessary because the `VstSpeakerArrangement` struct contains a + * dynamically sized array of length `VstSpeakerArrangement::num_speakers`. + * We build this object in a byte sized vector to make allocating enough + * heap space easy and safe. + */ + std::vector speaker_arrangement_buffer; +}; + /** * Marker struct to indicate that that the event writes arbitrary data into one * of its own buffers and uses the void pointer to store start of that data, @@ -348,8 +397,8 @@ struct Event { * gets passed along. * - A (short) string. * - Some binary blob stored as a byte vector. During `effGetChunk` this will - contain some chunk data that should be written to - `PluginBridge::chunk_data`. + * contain some chunk data that should be written to + * `PluginBridge::chunk_data`. * - A specific struct in response to an event such as `audioMasterGetTime` or * `audioMasterIOChanged`. * - An X11 window pointer for the editor window. diff --git a/src/common/vst24.h b/src/common/vst24.h index ac854ae9..3fb38394 100644 --- a/src/common/vst24.h +++ b/src/common/vst24.h @@ -80,6 +80,9 @@ struct VstSpeaker { * during `eff{Get,Set}SpeakerArrangement`. Reverse engineered from Renoise by * attaching gdb and dumping both the `value` and `data` pointers when the host * calls opcode 42. + * + * Use the `DynamicSpeakerArrangement` class to serialize and construct these + * objects. */ struct VstSpeakerArrangement { int flags; @@ -88,5 +91,5 @@ struct VstSpeakerArrangement { * Variable length array of speakers. Similar to how `VstEvents` works, but * with an array of objects instead of an array of pointers to objects. */ - VstSpeaker speakers[8]; + VstSpeaker speakers[2]; };