mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-06-16 00:13:55 +02:00
Use std::avriant to encode event payloads
It's a bit more complicated this way, but nwo we can easily add support for the opcode specific structs.
This commit is contained in:
@@ -7,10 +7,21 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket,
|
|||||||
void* data,
|
void* data,
|
||||||
float option,
|
float option,
|
||||||
std::optional<std::pair<Logger&, bool>> logging) {
|
std::optional<std::pair<Logger&, bool>> logging) {
|
||||||
auto payload =
|
// Encode the right payload type for this event. Check the documentation for
|
||||||
data == nullptr
|
// `EventPayload` for more information.
|
||||||
? std::nullopt
|
EventPayload payload = nullptr;
|
||||||
: std::make_optional(std::string(static_cast<char*>(data)));
|
if (data != nullptr) {
|
||||||
|
// TODO: Specific structs go here
|
||||||
|
|
||||||
|
// Assume buffers are zeroed out, this is probably not the case
|
||||||
|
char* c_string = static_cast<char*>(data);
|
||||||
|
if (c_string[0] != 0) {
|
||||||
|
payload = std::string(c_string);
|
||||||
|
} else {
|
||||||
|
payload = std::array<char, max_string_length>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (logging.has_value()) {
|
if (logging.has_value()) {
|
||||||
auto [logger, is_dispatch] = *logging;
|
auto [logger, is_dispatch] = *logging;
|
||||||
logger.log_event(is_dispatch, opcode, index, value, payload, option);
|
logger.log_event(is_dispatch, opcode, index, value, payload, option);
|
||||||
|
|||||||
+20
-24
@@ -134,6 +134,7 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket,
|
|||||||
void* data,
|
void* data,
|
||||||
float option,
|
float option,
|
||||||
std::optional<std::pair<Logger&, bool>> logging);
|
std::optional<std::pair<Logger&, bool>> logging);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive an event from a socket and pass it through to some callback function.
|
* Receive an event from a socket and pass it through to some callback function.
|
||||||
* This is used for both the host -> plugin 'dispatch' events and the plugin ->
|
* This is used for both the host -> plugin 'dispatch' events and the plugin ->
|
||||||
@@ -160,35 +161,30 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket,
|
|||||||
if (logging.has_value()) {
|
if (logging.has_value()) {
|
||||||
auto [logger, is_dispatch] = *logging;
|
auto [logger, is_dispatch] = *logging;
|
||||||
logger.log_event(is_dispatch, event.opcode, event.index, event.value,
|
logger.log_event(is_dispatch, event.opcode, event.index, event.value,
|
||||||
event.data, event.option);
|
event.payload, event.option);
|
||||||
}
|
|
||||||
|
|
||||||
// The void pointer argument for the dispatch function is used for
|
|
||||||
// either:
|
|
||||||
// - Not at all, in which case it will be a null pointer
|
|
||||||
// - For passing strings as input to the event
|
|
||||||
// - For providing a buffer for the event to write results back into
|
|
||||||
char* payload = nullptr;
|
|
||||||
std::array<char, max_string_length> buffer;
|
|
||||||
if (event.data.has_value()) {
|
|
||||||
// If the data parameter was an empty string, then we're going to
|
|
||||||
// pass a larger buffer to the dispatch function instead. Otherwise
|
|
||||||
// we'll pass the data passed by the host.
|
|
||||||
if (!event.data->empty()) {
|
|
||||||
payload = const_cast<char*>(event.data->c_str());
|
|
||||||
} else {
|
|
||||||
payload = buffer.data();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* data = std::visit(
|
||||||
|
overload{[&](std::nullptr_t) -> void* { return nullptr; },
|
||||||
|
[&](std::string s) -> void* {
|
||||||
|
return const_cast<char*>(s.c_str());
|
||||||
|
},
|
||||||
|
[&](std::array<char, max_string_length> buffer) -> void* {
|
||||||
|
return buffer.data();
|
||||||
|
}},
|
||||||
|
event.payload);
|
||||||
const intptr_t return_value = callback(plugin, event.opcode, event.index,
|
const intptr_t return_value = callback(plugin, event.opcode, event.index,
|
||||||
event.value, payload, event.option);
|
event.value, data, event.option);
|
||||||
|
|
||||||
// Only write back the value from `payload` if we were passed an empty
|
// Only write back data for empty buffers
|
||||||
// buffer to write into
|
// XXX: Is it possbile here that we got passed a non empty buffer (i.e.
|
||||||
bool is_updated = event.data.has_value() && event.data->empty();
|
// because it was not zeroed out by the host) for an event that should
|
||||||
|
// report some data back?
|
||||||
const auto response_data =
|
const auto response_data =
|
||||||
is_updated ? std::make_optional(payload) : std::nullopt;
|
std::holds_alternative<std::array<char, max_string_length>>(
|
||||||
|
event.payload)
|
||||||
|
? std::make_optional(static_cast<char*>(data))
|
||||||
|
: std::nullopt;
|
||||||
|
|
||||||
if (logging.has_value()) {
|
if (logging.has_value()) {
|
||||||
auto [logger, is_dispatch] = *logging;
|
auto [logger, is_dispatch] = *logging;
|
||||||
|
|||||||
+9
-10
@@ -121,7 +121,7 @@ void Logger::log_event(bool is_dispatch,
|
|||||||
int opcode,
|
int opcode,
|
||||||
int index,
|
int index,
|
||||||
intptr_t value,
|
intptr_t value,
|
||||||
std::optional<std::string> payload,
|
EventPayload payload,
|
||||||
float option) {
|
float option) {
|
||||||
if (BOOST_UNLIKELY(verbosity >= Verbosity::events)) {
|
if (BOOST_UNLIKELY(verbosity >= Verbosity::events)) {
|
||||||
std::ostringstream message;
|
std::ostringstream message;
|
||||||
@@ -140,15 +140,14 @@ void Logger::log_event(bool is_dispatch,
|
|||||||
|
|
||||||
message << "(index = " << index << ", value = " << value
|
message << "(index = " << index << ", value = " << value
|
||||||
<< ", option = " << option << ", data = ";
|
<< ", option = " << option << ", data = ";
|
||||||
if (!payload.has_value()) {
|
|
||||||
message << "<nullptr>";
|
std::visit(
|
||||||
} else if (payload->empty()) {
|
overload{[&](std::nullptr_t) { message << "<nullptr>"; },
|
||||||
message << "<writable_buffer>";
|
[&](std::string s) { message << "\"" << s << "\""; },
|
||||||
} else {
|
[&](std::array<char, max_string_length>) {
|
||||||
// Might print binary payload, maybe check for this?
|
message << "<writeable_buffer>";
|
||||||
message << "\"" << payload.value() << "\"";
|
}},
|
||||||
}
|
payload);
|
||||||
message << ")";
|
|
||||||
|
|
||||||
log(message.str());
|
log(message.str());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
|
#include "serialization.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Super basic logging facility meant for debugging malfunctioning VST
|
* Super basic logging facility meant for debugging malfunctioning VST
|
||||||
* plugins. This is also used to redirect the output of the Wine process
|
* plugins. This is also used to redirect the output of the Wine process
|
||||||
@@ -93,7 +95,7 @@ class Logger {
|
|||||||
int opcode,
|
int opcode,
|
||||||
int index,
|
int index,
|
||||||
intptr_t value,
|
intptr_t value,
|
||||||
std::optional<std::string> payload,
|
EventPayload payload,
|
||||||
float option);
|
float option);
|
||||||
void log_event_response(bool is_dispatch,
|
void log_event_response(bool is_dispatch,
|
||||||
intptr_t return_value,
|
intptr_t return_value,
|
||||||
|
|||||||
+72
-10
@@ -1,10 +1,15 @@
|
|||||||
#include <bitsery/adapter/buffer.h>
|
#include <bitsery/adapter/buffer.h>
|
||||||
#include <bitsery/ext/std_optional.h>
|
#include <bitsery/ext/std_optional.h>
|
||||||
|
#include <bitsery/ext/std_variant.h>
|
||||||
#include <bitsery/traits/array.h>
|
#include <bitsery/traits/array.h>
|
||||||
#include <bitsery/traits/string.h>
|
#include <bitsery/traits/string.h>
|
||||||
#include <bitsery/traits/vector.h>
|
#include <bitsery/traits/vector.h>
|
||||||
#include <vestige/aeffectx.h>
|
#include <vestige/aeffectx.h>
|
||||||
|
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The maximum number of audio channels supported.
|
* The maximum number of audio channels supported.
|
||||||
*/
|
*/
|
||||||
@@ -15,10 +20,10 @@ constexpr size_t max_audio_channels = 32;
|
|||||||
constexpr size_t max_buffer_size = 16384;
|
constexpr size_t max_buffer_size = 16384;
|
||||||
/**
|
/**
|
||||||
* The maximum size in bytes of a string or buffer passed through a void pointer
|
* The maximum size in bytes of a string or buffer passed through a void pointer
|
||||||
* in one of the dispatch functions. This is used as a buffer size and also as a
|
* in one of the dispatch functions. This is used to create buffers for plugins
|
||||||
* cutoff for checking if c-style strings behind a `char*` have changed.
|
* to write strings to.
|
||||||
*/
|
*/
|
||||||
constexpr size_t max_string_length = 128;
|
constexpr size_t max_string_length = 64;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple constant sized buffer for smaller types that can be allocated on the
|
* A simple constant sized buffer for smaller types that can be allocated on the
|
||||||
@@ -33,12 +38,53 @@ using OutputAdapter = bitsery::OutputBufferAdapter<B>;
|
|||||||
template <typename B>
|
template <typename B>
|
||||||
using InputAdapter = bitsery::InputBufferAdapter<B>;
|
using InputAdapter = bitsery::InputBufferAdapter<B>;
|
||||||
|
|
||||||
|
// The cannonical overloading template for `std::visitor`, not sure why this
|
||||||
|
// isn't part of the standard library
|
||||||
|
template <class... Ts>
|
||||||
|
struct overload : Ts... {
|
||||||
|
using Ts::operator()...;
|
||||||
|
};
|
||||||
|
template <class... Ts>
|
||||||
|
overload(Ts...)->overload<Ts...>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* VST events are passed a void pointer that can contain a variety of different
|
||||||
|
* data types depending on the event's opcode. This is typically either:
|
||||||
|
*
|
||||||
|
* - A null pointer, used for simple events.
|
||||||
|
* - A char pointer to a null terminated string, used for passing strings to the
|
||||||
|
* plugin such as when renaming presets. Bitsery handles the serialization for
|
||||||
|
* us.
|
||||||
|
*
|
||||||
|
* NOTE: Bitsery does not support null terminated C-strings without a known
|
||||||
|
* size. We can replace `std::string` with `char*` once it does for
|
||||||
|
* clarity's sake.
|
||||||
|
*
|
||||||
|
* - Specific data structures from `aeffextx.h`. For instance an event with the
|
||||||
|
* opcode `effProcessEvents` comes with a struct containing a list of midi
|
||||||
|
* events.
|
||||||
|
*
|
||||||
|
* TODO: A lot of these are still missing, beginning with `VstEvents`.
|
||||||
|
*
|
||||||
|
* - 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. We'll
|
||||||
|
* assume that this is the default if none of the above options apply.
|
||||||
|
*
|
||||||
|
* TODO: As a simple optimization we of course wouldn't have to send an entire
|
||||||
|
* empty array here, this should be replaced by some kind of marker
|
||||||
|
* struct. This would require some minor modifications in
|
||||||
|
* `passthrough_event()`.
|
||||||
|
*/
|
||||||
|
using EventPayload = std::
|
||||||
|
variant<std::nullptr_t, std::string, std::array<char, max_string_length>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event as dispatched by the VST host. These events will get forwarded to
|
* An event as dispatched by the VST host. These events will get forwarded to
|
||||||
* the VST host process running under Wine. The fields here mirror those
|
* the VST host process running under Wine. The fields here mirror those
|
||||||
* arguments sent to the `AEffect::dispatch` function.
|
* arguments sent to the `AEffect::dispatch` function.
|
||||||
*/
|
*/
|
||||||
struct Event {
|
struct Event {
|
||||||
|
// TODO: Possibly update to account for VstEvents
|
||||||
using buffer_type = ArrayBuffer<max_string_length + 32>;
|
using buffer_type = ArrayBuffer<max_string_length + 32>;
|
||||||
|
|
||||||
int opcode;
|
int opcode;
|
||||||
@@ -49,12 +95,16 @@ struct Event {
|
|||||||
intptr_t value;
|
intptr_t value;
|
||||||
float option;
|
float option;
|
||||||
/**
|
/**
|
||||||
* The event dispatch function has a void pointer parameter that's used to
|
* The event dispatch function has a void pointer parameter that's often
|
||||||
* either send string messages to the event (e.g. for `effCanDo`) or to
|
* used to either pass additional data for the event or to provide a buffer
|
||||||
* write a string back into. This value will contain an (empty) string if
|
* for the plugin to write a string into.
|
||||||
* the void* parameter for the dispatch function was not a null pointer.
|
*
|
||||||
|
* The `VstEvents` struct passed for the `effProcessEvents` event contains
|
||||||
|
* an array of pointers. This requires some special handling which is why we
|
||||||
|
* have to use an `std::variant` instead of a simple string buffer. Luckily
|
||||||
|
* Bitsery can do all the hard work for us.
|
||||||
*/
|
*/
|
||||||
std::optional<std::string> data;
|
EventPayload payload;
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
void serialize(S& s) {
|
void serialize(S& s) {
|
||||||
@@ -65,8 +115,20 @@ struct Event {
|
|||||||
// bit large pointers.
|
// bit large pointers.
|
||||||
s.value8b(value);
|
s.value8b(value);
|
||||||
s.value4b(option);
|
s.value4b(option);
|
||||||
s.ext(data, bitsery::ext::StdOptional(),
|
|
||||||
[](S& s, auto& v) { s.text1b(v, max_string_length); });
|
// I couldn't get this serializer to work seperately without
|
||||||
|
// `EventPayload` in a struct
|
||||||
|
s.ext(payload,
|
||||||
|
bitsery::ext::StdVariant{
|
||||||
|
// TODO: Some of these oerlaods might not be necessary, check
|
||||||
|
// if this is the case
|
||||||
|
[](S&, std::nullptr_t&) {},
|
||||||
|
[](S& s, std::string& string) {
|
||||||
|
s.text1b(string, max_string_length);
|
||||||
|
},
|
||||||
|
[](S& s, std::array<char, max_string_length>& buffer) {
|
||||||
|
s.container1b(buffer);
|
||||||
|
}});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user