Use different types for strings and binary data

This commit is contained in:
Robbert van der Helm
2020-04-27 00:51:22 +02:00
parent bdd83de7a9
commit 527db1f49f
5 changed files with 73 additions and 50 deletions
+4 -6
View File
@@ -11,15 +11,13 @@ There are a few things that should be done before releasing this, including:
- Fix implementation bugs: - Fix implementation bugs:
- Phase Plant doesn't play any sound until the editor has been opened? - Phase Plant doesn't play any sound until the editor has been opened?
- Phase Plant sometimes crashes during `effEditIdle()`, not sure what - Large chunk buffers can't be sent over the socket in one go, this causes
situation triggers this. issues with presets that are multiple megabytes in size.
- Phase plant can't load large chunks. Seems to be a problem with Phase Plant - Serum crashed and audio engine froze while browsing through Serum presets in
since other plugins don't have this issue. the browser?
- KiloHearts plugins create a ridiculous amount of file descriptor leaks in - KiloHearts plugins create a ridiculous amount of file descriptor leaks in
wineserver when esync is enabled. I haven't come across any other plugins wineserver when esync is enabled. I haven't come across any other plugins
that do this. Not sure if this is fixable in yabridge. that do this. Not sure if this is fixable in yabridge.
- Serum crashed and audio engine froze while browsing through Serum presets in
the browser?
- Polish GUIs even further. There are some todos left in - Polish GUIs even further. There are some todos left in
`src/wine-host/editor.{h,cpp}`. `src/wine-host/editor.{h,cpp}`.
- Add missing details if any to the architecture section. - Add missing details if any to the architecture section.
+35 -25
View File
@@ -190,30 +190,39 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket,
// arbitrary C-style string. // arbitrary C-style string.
std::array<char, max_string_length> string_buffer; std::array<char, max_string_length> string_buffer;
string_buffer[0] = 0; string_buffer[0] = 0;
// This buffer is only used for retrieving chunk data and will be allocated
// as needed
std::vector<uint8_t> binary_buffer;
void* data = std::visit( void* data = std::visit(
overload{ overload{[&](const std::nullptr_t&) -> void* { return nullptr; },
[&](const std::nullptr_t&) -> void* { return nullptr; }, [&](const std::string& s) -> void* {
[&](const std::string& s) -> void* { return const_cast<char*>(s.c_str());
return const_cast<char*>(s.c_str()); },
}, [&](const std::vector<uint8_t>& buffer) -> void* {
[&](size_t& window_handle) -> void* { return const_cast<uint8_t*>(buffer.data());
// This is the X11 window handle that the editor should reparent },
// itself to. We have a special wrapper around the dispatch [&](size_t& window_handle) -> void* {
// function that intercepts `effEditOpen` events and creates a // This is the X11 window handle that the editor should
// Win32 window and then finally embeds the X11 window Wine // reparent itself to. We have a special wrapper around the
// created into this wnidow handle. // dispatch function that intercepts `effEditOpen` events
return reinterpret_cast<void*>(window_handle); // and creates a Win32 window and then finally embeds the
}, // X11 window Wine created into this wnidow handle.
[&](const AEffect&) -> void* { return nullptr; }, return reinterpret_cast<void*>(window_handle);
[&](DynamicVstEvents& events) -> void* { },
return &events.as_c_events(); [&](const AEffect&) -> void* { return nullptr; },
}, [&](DynamicVstEvents& events) -> void* {
[&](WantsChunkBuffer&) -> void* { return string_buffer.data(); }, return &events.as_c_events();
[&](VstIOProperties& props) -> void* { return &props; }, },
[&](VstParameterProperties& props) -> void* { return &props; }, [&](WantsChunkBuffer&) -> void* {
[&](WantsVstRect&) -> void* { return string_buffer.data(); }, binary_buffer.resize(binary_buffer_size);
[&](const WantsVstTimeInfo&) -> void* { return nullptr; }, return binary_buffer.data();
[&](WantsString&) -> void* { return string_buffer.data(); }}, },
[&](VstIOProperties& props) -> void* { return &props; },
[&](VstParameterProperties& props) -> void* { return &props; },
[&](WantsVstRect&) -> void* { return string_buffer.data(); },
[&](const WantsVstTimeInfo&) -> void* { return nullptr; },
[&](WantsString&) -> void* { return string_buffer.data(); }},
event.payload); event.payload);
const intptr_t return_value = callback(plugin, event.opcode, event.index, const intptr_t return_value = callback(plugin, event.opcode, event.index,
@@ -256,8 +265,9 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket,
// stored in an array to which a pointer is stored in // stored in an array to which a pointer is stored in
// `data`, with the return value from the event determines // `data`, with the return value from the event determines
// how much data the plugin has written // how much data the plugin has written
return std::string(*static_cast<char**>(data), const uint8_t* chunk_data = *static_cast<uint8_t**>(data);
return_value); return std::vector<uint8_t>(chunk_data,
chunk_data + return_value);
}, },
[&](VstIOProperties& props) -> EventResposnePayload { [&](VstIOProperties& props) -> EventResposnePayload {
return props; return props;
+4
View File
@@ -173,6 +173,7 @@ void Logger::log_event(bool is_dispatch,
message << "<" << s.size() << " bytes>"; message << "<" << s.size() << " bytes>";
} }
}, },
[&](const std::vector<uint8_t>&) { message << "<chunk>"; },
[&](const intptr_t&) { message << "<nullptr>"; }, [&](const intptr_t&) { message << "<nullptr>"; },
[&](const AEffect&) { message << "<nullptr>"; }, [&](const AEffect&) { message << "<nullptr>"; },
[&](const DynamicVstEvents& events) { [&](const DynamicVstEvents& events) {
@@ -226,6 +227,9 @@ void Logger::log_event_response(bool is_dispatch,
message << ", <" << s.size() << " bytes>"; message << ", <" << s.size() << " bytes>";
} }
}, },
[&](const std::vector<uint8_t>& buffer) {
message << "<" << buffer.size() << "byte chunk>";
},
[&](const AEffect&) { message << ", <AEffect_object>"; }, [&](const AEffect&) { message << ", <AEffect_object>"; },
[&](const VstIOProperties&) { message << ", <io_properties>"; }, [&](const VstIOProperties&) { message << ", <io_properties>"; },
[&](const VstParameterProperties& props) { [&](const VstParameterProperties& props) {
+23 -14
View File
@@ -51,11 +51,11 @@ constexpr size_t max_midi_events = max_buffer_size / sizeof(size_t);
[[maybe_unused]] constexpr size_t max_string_length = 64; [[maybe_unused]] constexpr size_t max_string_length = 64;
/** /**
* The size for a buffer in which we're receiving chunks. Allow for up to 1 GB * The size for a buffer in which we're receiving chunks. Allow for up to 50 MB
* chunks. Hopefully no plugin will come anywhere near this limit, but it will * chunks. Hopefully no plugin will come anywhere near this limit, but it will
* add up when plugins start to audio samples in their presets. * add up when plugins start to audio samples in their presets.
*/ */
constexpr size_t binary_buffer_size = 1 << 30; constexpr size_t binary_buffer_size = 50 << 20;
// The cannonical overloading template for `std::visitor`, not sure why this // The cannonical overloading template for `std::visitor`, not sure why this
// isn't part of the standard library // isn't part of the standard library
@@ -218,6 +218,10 @@ struct WantsString {};
* size. We can replace `std::string` with `char*` once it does for * size. We can replace `std::string` with `char*` once it does for
* clarity's sake. * clarity's sake.
* *
* - A byte vector for handling chunk data during `effSetChunk()`. We can't
reuse the regular string handling here since the data may contain null
bytes and `std::string::as_c_str()` might cut off everything after the
first null byte.
* - An X11 window handle. * - An X11 window handle.
* - Specific data structures from `aeffextx.h`. For instance an event with the * - Specific data structures from `aeffextx.h`. For instance an event with the
* opcode `effProcessEvents` the hosts passes a `VstEvents` struct containing * opcode `effProcessEvents` the hosts passes a `VstEvents` struct containing
@@ -226,15 +230,19 @@ struct WantsString {};
* *
* - Some empty buffer for the plugin to write its own data to, for instance for * - Some empty buffer for the plugin to write its own data to, for instance for
* a plugin to report its name or the label for a certain parameter. There are * a plugin to report its name or the label for a certain parameter. There are
* two separate cases here: * two separate cases here. This is typically a short null terminated
* C-string. We'll assume this as the default case when none of the above
* options apply.
* *
* - Either the plugin writes arbitrary data and uses its return value to * - Either the plugin writes arbitrary data and uses its return value to
* indicate how much data was written (i.e. for the `effGetChunk` opcode). * indicate how much data was written (i.e. for the `effGetChunk` opcode).
* For this we use a vector of bytes instead of a string since
* - Or the plugin will write a short null terminated C-string there. We'll * - Or the plugin will write a short null terminated C-string there. We'll
* assume that this is the default if none of the above options apply. * assume that this is the default if none of the above options apply.
*/ */
using EventPayload = std::variant<std::nullptr_t, using EventPayload = std::variant<std::nullptr_t,
std::string, std::string,
std::vector<uint8_t>,
size_t, size_t,
AEffect, AEffect,
DynamicVstEvents, DynamicVstEvents,
@@ -251,9 +259,10 @@ void serialize(S& s, EventPayload& payload) {
bitsery::ext::StdVariant{ bitsery::ext::StdVariant{
[](S&, std::nullptr_t&) {}, [](S&, std::nullptr_t&) {},
[](S& s, std::string& string) { [](S& s, std::string& string) {
// `binary_buffer_size` and not `max_string_length` because we s.text1b(string, max_string_length);
// also use this to send back large chunk data },
s.text1b(string, binary_buffer_size); [](S& s, std::vector<uint8_t>& buffer) {
s.container1b(buffer, binary_buffer_size);
}, },
[](S& s, size_t& window_handle) { s.value8b(window_handle); }, [](S& s, size_t& window_handle) { s.value8b(window_handle); },
[](S& s, AEffect& effect) { s.object(effect); }, [](S& s, AEffect& effect) { s.object(effect); },
@@ -312,16 +321,16 @@ struct Event {
* *
* - Nothing, on which case only the return value from the callback function * - Nothing, on which case only the return value from the callback function
* gets passed along. * gets passed along.
* - Some buffer stored in a `std::string`. This is typically read from and * - A (short) string.
* written as C-style string, but in the case of `effGetChunk` this is some * - Some binary blob stored as a byte vector. During `effGetChunk` this will
* blob of binary data that should be written to `HostBridge::chunk_data` contain some chunk data that should be written to `HostBridge::chunk_data`.
* instenad.
* - A specific struct in response to an event such as `audioMasterGetTime` or * - A specific struct in response to an event such as `audioMasterGetTime` or
* `audioMasterIOChanged`. * `audioMasterIOChanged`.
* - An X11 window pointer for the editor window. * - An X11 window pointer for the editor window.
*/ */
using EventResposnePayload = std::variant<std::nullptr_t, using EventResposnePayload = std::variant<std::nullptr_t,
std::string, std::string,
std::vector<uint8_t>,
AEffect, AEffect,
VstIOProperties, VstIOProperties,
VstParameterProperties, VstParameterProperties,
@@ -334,10 +343,10 @@ void serialize(S& s, EventResposnePayload& payload) {
bitsery::ext::StdVariant{ bitsery::ext::StdVariant{
[](S&, std::nullptr_t&) {}, [](S&, std::nullptr_t&) {},
[](S& s, std::string& string) { [](S& s, std::string& string) {
// `binary_buffer_size` and not `max_string_length` s.text1b(string, max_string_length);
// because we also use this to send back large chunk },
// data [](S& s, std::vector<uint8_t>& buffer) {
s.text1b(string, binary_buffer_size); s.container1b(buffer, binary_buffer_size);
}, },
[](S& s, AEffect& effect) { s.object(effect); }, [](S& s, AEffect& effect) { s.object(effect); },
[](S& s, VstIOProperties& props) { s.object(props); }, [](S& s, VstIOProperties& props) { s.object(props); },
+7 -5
View File
@@ -206,11 +206,13 @@ class DispatchDataConverter : DefaultDataConverter {
case effGetChunk: case effGetChunk:
return WantsChunkBuffer(); return WantsChunkBuffer();
break; break;
case effSetChunk: case effSetChunk: {
const uint8_t* chunk_data = static_cast<const uint8_t*>(data);
// When the host passes a chunk it will use the value parameter // When the host passes a chunk it will use the value parameter
// to tell us its length // to tell us its length
return std::string(static_cast<const char*>(data), value); return std::vector<uint8_t>(chunk_data, chunk_data + value);
break; } break;
case effProcessEvents: case effProcessEvents:
return DynamicVstEvents(*static_cast<const VstEvents*>(data)); return DynamicVstEvents(*static_cast<const VstEvents*>(data));
break; break;
@@ -243,8 +245,8 @@ class DispatchDataConverter : DefaultDataConverter {
// Write the chunk data to some publically accessible place in // Write the chunk data to some publically accessible place in
// `HostBridge` and write a pointer to that struct to the data // `HostBridge` and write a pointer to that struct to the data
// pointer // pointer
const auto buffer =
const auto buffer = std::get<std::string>(response.payload); std::get<std::vector<uint8_t>>(response.payload);
chunk.assign(buffer.begin(), buffer.end()); chunk.assign(buffer.begin(), buffer.end());
*static_cast<void**>(data) = chunk.data(); *static_cast<void**>(data) = chunk.data();