diff --git a/README.md b/README.md index a03b82fc..e0334eaa 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,6 @@ There are a few things that should be done before releasing this, including: - Fix implementation bugs: - KiloHearts plugins fail during initialization. - Serum crashes when closing bitwig (but otherwise exits just fine). - - Serum crashes if you keep playing midi notes while the GUI is blocked. - Related to the above, and probably because of the current limit of 512 midi - events. - Melda plugins crash when opening their GUI. - Add missing details if any to the architecture section. - Document what this has been tested on and what does or does not work. diff --git a/src/common/serialization.cpp b/src/common/serialization.cpp index ea102895..5a4030a4 100644 --- a/src/common/serialization.cpp +++ b/src/common/serialization.cpp @@ -25,13 +25,25 @@ DynamicVstEvents::DynamicVstEvents(const VstEvents& c_events) } VstEvents& DynamicVstEvents::as_c_events() { - vst_events.numEvents = events.size(); + // As explained in `vst_events_buffer`'s docstring we have to build the + // `VstEvents` struct by hand on the heap since it's actually a dynamically + // sized object - // Populate the vst_events struct with data from the vector. This will - // overflow past the defined length of `vst_events.events` because it's - // actually a VLA. This is why I put some padding at the end of this struct. - std::transform(events.begin(), events.end(), &vst_events.events[0], + // 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); + const size_t buffer_size = + 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` + // vector + VstEvents* vst_events = + reinterpret_cast(vst_events_buffer.data()); + vst_events->numEvents = events.size(); + std::transform(events.begin(), events.end(), vst_events->events, [](VstEvent& event) -> VstEvent* { return &event; }); - return vst_events; + return *vst_events; } diff --git a/src/common/serialization.h b/src/common/serialization.h index 433b638b..cc473385 100644 --- a/src/common/serialization.h +++ b/src/common/serialization.h @@ -42,7 +42,7 @@ constexpr size_t max_buffer_size = 16384; /** * The maximum number of midi events in a single `VstEvents` struct. */ -constexpr size_t max_midi_events = 256; +constexpr size_t max_midi_events = max_buffer_size / sizeof(size_t); /** * The maximum size in bytes of a string or buffer passed through a void pointer * in one of the dispatch functions. This is used to create buffers for plugins @@ -122,7 +122,7 @@ void serialize(S& s, VstTimeInfo& time_info) { * * 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 usign the `as_c_events()` method. + * can be reconstructed using the `as_c_events()` method. */ class alignas(16) DynamicVstEvents { public: @@ -144,20 +144,18 @@ class alignas(16) DynamicVstEvents { private: /** - * A `VstEvents` struct based on the `events` vector. Use the - * `as_c_events()` method to populate and return this after the `events` - * vector has been filled. + * Some buffer we can build a `VstEvents`. 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 + * `vestige/aeffectx.h` the struct contains a single element `VstEvent` + * pointer array, but the actual length of this array is + * `VstEvents::numEvents`. Because there is no real limit on the number of + * midi events the host can send at once we have to build this object on the + * heap by hand. */ - VstEvents vst_events; - /** - * The `VstEvents` struct is defined to look like it contains a one or two - * element array of `VstEvent` pointers. The actual truth is that the - * `VstEvents::event` array is actually a variable length array with length - * `VstEvents::numEvents`. This is probably not part of any header files - * because VLAs are not part of any C++ standard. This struct is here to - * make sure there is enough room to copy the elements into. - */ - size_t dummy[max_midi_events]; + std::vector vst_events_buffer; }; /**