mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-07 03:50:11 +02:00
Serialize midi events using vectors
Easier than to implement C-style arrays in Bitsery and this way we also don't have to worry about cleanup. Serum still segfaults with multiple events so something is still off.
This commit is contained in:
@@ -35,6 +35,7 @@ shared_library(
|
||||
[
|
||||
'src/common/communication.cpp',
|
||||
'src/common/logging.cpp',
|
||||
'src/common/serialization.cpp',
|
||||
'src/plugin/host-bridge.cpp',
|
||||
'src/plugin/plugin.cpp',
|
||||
],
|
||||
@@ -49,6 +50,7 @@ executable(
|
||||
[
|
||||
'src/common/communication.cpp',
|
||||
'src/common/logging.cpp',
|
||||
'src/common/serialization.cpp',
|
||||
'src/wine-host/plugin-bridge.cpp',
|
||||
'src/wine-host/vst-host.cpp',
|
||||
],
|
||||
|
||||
@@ -31,7 +31,8 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket,
|
||||
// There are some events that need specific structs that we can't simply
|
||||
// serialize as a string because they might contain null bytes
|
||||
if (is_dispatch && opcode == effProcessEvents) {
|
||||
payload = *static_cast<VstEvents*>(data);
|
||||
// Move the elements into a vector for easier serialization
|
||||
payload = DynamicVstEvents(*static_cast<VstEvents*>(data));
|
||||
} else {
|
||||
// TODO: More of these structs
|
||||
|
||||
|
||||
@@ -181,7 +181,9 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket,
|
||||
return const_cast<char*>(s.c_str());
|
||||
},
|
||||
// TODO: Check if the deserialization leaks memory
|
||||
[&](VstEvents& events) -> void* { return &events; },
|
||||
[&](DynamicVstEvents& events) -> void* {
|
||||
return &events.as_c_events();
|
||||
},
|
||||
[&](NeedsBuffer&) -> void* { return buffer.data(); }},
|
||||
event.payload);
|
||||
const intptr_t return_value = callback(plugin, event.opcode, event.index,
|
||||
|
||||
@@ -161,8 +161,8 @@ void Logger::log_event(bool is_dispatch,
|
||||
overload{
|
||||
[&](const std::nullptr_t&) { message << "<nullptr>"; },
|
||||
[&](const std::string& s) { message << "\"" << s << "\""; },
|
||||
[&](const VstEvents& events) {
|
||||
message << "<" << events.numEvents << " midi_events>";
|
||||
[&](const DynamicVstEvents& events) {
|
||||
message << "<" << events.events.size() << " midi_events>";
|
||||
},
|
||||
[&](const NeedsBuffer&) { message << "<writable_buffer>"; }},
|
||||
payload);
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// yabridge: a Wine VST bridge
|
||||
// Copyright (C) 2020 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include "serialization.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
DynamicVstEvents::DynamicVstEvents(const VstEvents& c_events)
|
||||
: events(c_events.numEvents) {
|
||||
// Copy from the C-style array into a vector for serialization
|
||||
for (int i = 0; i < c_events.numEvents; i++) {
|
||||
events[i] = c_events.events[0][i];
|
||||
}
|
||||
}
|
||||
|
||||
VstEvents& DynamicVstEvents::as_c_events() {
|
||||
// Populate the vst_events struct with data from the vector
|
||||
vst_events.numEvents = events.size();
|
||||
vst_events.events[0] = events.data();
|
||||
|
||||
return vst_events;
|
||||
}
|
||||
+67
-28
@@ -14,6 +14,8 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <bitsery/adapter/buffer.h>
|
||||
#include <bitsery/ext/pointer.h>
|
||||
#include <bitsery/ext/std_optional.h>
|
||||
@@ -25,6 +27,7 @@
|
||||
|
||||
#include <variant>
|
||||
|
||||
// These constants are limits used by bitsery
|
||||
|
||||
/**
|
||||
* The maximum number of audio channels supported.
|
||||
@@ -34,6 +37,12 @@ constexpr size_t max_audio_channels = 32;
|
||||
* The maximum number of samples in a buffer.
|
||||
*/
|
||||
constexpr size_t max_buffer_size = 16384;
|
||||
/**
|
||||
* The maximum number of midi events in a single `VstEvents` struct.
|
||||
*
|
||||
* TODO: Can this go higher?
|
||||
*/
|
||||
constexpr size_t max_midi_events = 32;
|
||||
/**
|
||||
* 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
|
||||
@@ -63,6 +72,43 @@ struct overload : Ts... {
|
||||
template <class... Ts>
|
||||
overload(Ts...)->overload<Ts...>;
|
||||
|
||||
/**
|
||||
* A wrapper around `VstEvents` that stores the data in a vector instead of a
|
||||
* C-style array. Needed until bitsery supports C-style arrays
|
||||
* https://github.com/fraillt/bitsery/issues/28. An advantage of this approach
|
||||
* is that RAII will handle cleanup for us.
|
||||
*
|
||||
* 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 obtained again usign the `as_c_events()` method.
|
||||
*/
|
||||
class DynamicVstEvents {
|
||||
public:
|
||||
DynamicVstEvents(){};
|
||||
|
||||
explicit DynamicVstEvents(const VstEvents& c_events);
|
||||
|
||||
/**
|
||||
* Construct a `VstEvents` struct from the events vector. This contains a
|
||||
* pointer to that vector's elements, so the returned object should not
|
||||
* outlive this struct.
|
||||
*/
|
||||
VstEvents& as_c_events();
|
||||
|
||||
// XXX: The original `VstEvents` stuct hasonly one C-style array of
|
||||
// `VstEvent`s, but I've seen some implementation that have two. Is
|
||||
// this only for alignment or does this have an actual use?
|
||||
std::vector<VstEvent> events;
|
||||
|
||||
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.
|
||||
*/
|
||||
VstEvents vst_events;
|
||||
};
|
||||
|
||||
/**
|
||||
* Marker struct to indicate that that the event requires some buffer to write
|
||||
* its results into. This is to prevent us from having to unnecessarily sending
|
||||
@@ -94,7 +140,7 @@ struct NeedsBuffer {};
|
||||
* assume that this is the default if none of the above options apply.
|
||||
*/
|
||||
using EventPayload =
|
||||
std::variant<std::nullptr_t, std::string, VstEvents, NeedsBuffer>;
|
||||
std::variant<std::nullptr_t, std::string, DynamicVstEvents, NeedsBuffer>;
|
||||
|
||||
/**
|
||||
* An event as dispatched by the VST host. These events will get forwarded to
|
||||
@@ -102,8 +148,14 @@ using EventPayload =
|
||||
* arguments sent to the `AEffect::dispatch` function.
|
||||
*/
|
||||
struct Event {
|
||||
// TODO: Possibly update to account for VstEvents
|
||||
using buffer_type = ArrayBuffer<max_string_length + 32>;
|
||||
// TODO: Possibly use a vector here sicne we can't know the maximum size for
|
||||
// certain
|
||||
using buffer_type = ArrayBuffer<sizeof(VstMidiEvent) * max_midi_events>;
|
||||
|
||||
// Ensure that the buffer can be aligned correctly and that strings will fit
|
||||
static_assert(std::tuple_size<buffer_type>::value % 16 == 0);
|
||||
static_assert(std::tuple_size<buffer_type>::value >=
|
||||
max_string_length + 32);
|
||||
|
||||
int opcode;
|
||||
int index;
|
||||
@@ -136,31 +188,18 @@ struct Event {
|
||||
|
||||
// I couldn't get this serializer to work seperately without
|
||||
// `EventPayload` in a struct
|
||||
s.ext(payload,
|
||||
bitsery::ext::StdVariant{
|
||||
[](S&, std::nullptr_t&) {},
|
||||
[](S& s, std::string& string) {
|
||||
s.text1b(string, max_string_length);
|
||||
},
|
||||
[](S& s, VstEvents& events) {
|
||||
s.value4b(events.numEvents);
|
||||
|
||||
// This will only ever read a single event since
|
||||
// that's how the `VstEvents` struct is defined,
|
||||
// hence the assertion. If multiple events can be
|
||||
// passed at once then `VstEvents` should be
|
||||
// modified.
|
||||
// TODO: This is definitely not the case, somehow fix this
|
||||
assert(events.numEvents <= 1);
|
||||
s.container(
|
||||
events.events, [](S& s, VstEvent*(&event_ptr)) {
|
||||
s.ext(event_ptr, bitsery::ext::PointerOwner(),
|
||||
[](S& s, VstEvent& event) {
|
||||
s.container1b(event.dump);
|
||||
});
|
||||
});
|
||||
},
|
||||
[](S&, NeedsBuffer&) {}});
|
||||
s.ext(payload, bitsery::ext::StdVariant{
|
||||
[](S&, std::nullptr_t&) {},
|
||||
[](S& s, std::string& string) {
|
||||
s.text1b(string, max_string_length);
|
||||
},
|
||||
[](S& s, DynamicVstEvents& events) {
|
||||
s.container(events.events, max_midi_events,
|
||||
[](S& s, VstEvent& event) {
|
||||
s.container1b(event.dump);
|
||||
});
|
||||
},
|
||||
[](S&, NeedsBuffer&) {}});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user