mirror of
https://github.com/robbert-vdh/yabridge.git
synced 2026-05-10 04:30:12 +02:00
Always use resizable buffers
It was a slight problem for audio buffers, but events can apparently also have an arbitrary size because of chunks.
This commit is contained in:
+31
-42
@@ -30,46 +30,35 @@
|
|||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "serialization.h"
|
#include "serialization.h"
|
||||||
|
|
||||||
// I don't want to editor 'include/vestige/aeffectx.h`. That's why this type
|
template <typename B>
|
||||||
// trait and the above serialization function are here.` Clang complains that
|
using OutputAdapter = bitsery::OutputBufferAdapter<B>;
|
||||||
// `buffer` should be qualified (and only in some cases), so `buffer_t` it is.
|
|
||||||
template <typename T>
|
|
||||||
struct buffer_t {
|
|
||||||
using type = typename T::buffer_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
template <typename B>
|
||||||
struct buffer_t<AEffect> {
|
using InputAdapter = bitsery::InputBufferAdapter<B>;
|
||||||
using type = ArrayBuffer<128>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serialize an object using bitsery and write it to a socket.
|
* Serialize an object using bitsery and write it to a socket.
|
||||||
*
|
*
|
||||||
* @param socket The Boost.Asio socket to write to.
|
* @param socket The Boost.Asio socket to write to.
|
||||||
* @param object The object to write to the stream.
|
* @param object The object to write to the stream.
|
||||||
* @param buffer The buffer to write to. Only needed for when sending audio
|
* @param buffer The buffer to write to. This is useful for sending audio and
|
||||||
* because their buffers might be quite large.
|
* chunk data since that can vary in size by a lot.
|
||||||
*
|
*
|
||||||
* @relates read_object
|
* @relates read_object
|
||||||
*/
|
*/
|
||||||
template <typename T, typename Socket>
|
template <typename T, typename Socket>
|
||||||
inline void write_object(Socket& socket,
|
inline void write_object(
|
||||||
|
Socket& socket,
|
||||||
const T& object,
|
const T& object,
|
||||||
typename buffer_t<T>::type& buffer) {
|
std::vector<uint8_t> buffer = std::vector<uint8_t>(64)) {
|
||||||
bitsery::ext::PointerLinkingContext serializer_context{};
|
const size_t size =
|
||||||
auto length =
|
bitsery::quickSerialization<OutputAdapter<std::vector<uint8_t>>>(
|
||||||
bitsery::quickSerialization<bitsery::ext::PointerLinkingContext,
|
buffer, object);
|
||||||
OutputAdapter<typename buffer_t<T>::type>>(
|
|
||||||
serializer_context, buffer, object);
|
|
||||||
|
|
||||||
socket.send(boost::asio::buffer(buffer, length));
|
// Tell the other side how large the object is so it can prepare a buffer
|
||||||
}
|
// large enough before sending the data
|
||||||
|
socket.send(boost::asio::buffer(std::array<size_t, 1>{size}));
|
||||||
template <typename T, typename Socket>
|
socket.send(boost::asio::buffer(buffer, size));
|
||||||
inline void write_object(Socket& socket, const T& object) {
|
|
||||||
typename buffer_t<T>::type buffer;
|
|
||||||
write_object(socket, object, buffer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -80,8 +69,8 @@ inline void write_object(Socket& socket, const T& object) {
|
|||||||
* @param object The object to deserialize to, if given. This can be used to
|
* @param object The object to deserialize to, if given. This can be used to
|
||||||
* update an existing `AEffect` struct without losing the pointers set by the
|
* update an existing `AEffect` struct without losing the pointers set by the
|
||||||
* host and the bridge.
|
* host and the bridge.
|
||||||
* @param buffer The buffer to write to. Only needed for when sending audio
|
* @param buffer The buffer to read into. This is useful for sending audio and
|
||||||
* because their buffers might be quite large.
|
* chunk data since that can vary in size by a lot.
|
||||||
*
|
*
|
||||||
* @throw std::runtime_error If the conversion to an object was not successful.
|
* @throw std::runtime_error If the conversion to an object was not successful.
|
||||||
*
|
*
|
||||||
@@ -90,16 +79,22 @@ inline void write_object(Socket& socket, const T& object) {
|
|||||||
template <typename T, typename Socket>
|
template <typename T, typename Socket>
|
||||||
inline T& read_object(Socket& socket,
|
inline T& read_object(Socket& socket,
|
||||||
T& object,
|
T& object,
|
||||||
typename buffer_t<T>::type& buffer) {
|
std::vector<uint8_t> buffer = std::vector<uint8_t>(64)) {
|
||||||
auto message_length = socket.receive(boost::asio::buffer(buffer));
|
std::array<size_t, 1> message_length;
|
||||||
|
socket.receive(boost::asio::buffer(message_length));
|
||||||
|
|
||||||
|
// Make sure the buffer is large enough
|
||||||
|
const size_t size = message_length[0];
|
||||||
|
buffer.resize(size);
|
||||||
|
|
||||||
|
const auto actual_size = socket.receive(boost::asio::buffer(buffer));
|
||||||
|
assert(size == actual_size);
|
||||||
|
|
||||||
bitsery::ext::PointerLinkingContext serializer_context{};
|
|
||||||
auto [_, success] =
|
auto [_, success] =
|
||||||
bitsery::quickDeserialization<bitsery::ext::PointerLinkingContext,
|
bitsery::quickDeserialization<InputAdapter<std::vector<uint8_t>>>(
|
||||||
InputAdapter<typename buffer_t<T>::type>>(
|
{buffer.begin(), size}, object);
|
||||||
serializer_context, {buffer.begin(), message_length}, object);
|
|
||||||
|
|
||||||
if (!success) {
|
if (BOOST_UNLIKELY(!success)) {
|
||||||
throw std::runtime_error("Deserialization failure in call:" +
|
throw std::runtime_error("Deserialization failure in call:" +
|
||||||
std::string(__PRETTY_FUNCTION__));
|
std::string(__PRETTY_FUNCTION__));
|
||||||
}
|
}
|
||||||
@@ -107,12 +102,6 @@ inline T& read_object(Socket& socket,
|
|||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Socket>
|
|
||||||
inline T& read_object(Socket& socket, T& object) {
|
|
||||||
typename buffer_t<T>::type buffer;
|
|
||||||
return read_object(socket, object, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, typename Socket>
|
template <typename T, typename Socket>
|
||||||
inline T read_object(Socket& socket) {
|
inline T read_object(Socket& socket) {
|
||||||
T object;
|
T object;
|
||||||
|
|||||||
@@ -50,19 +50,6 @@ constexpr size_t max_midi_events = 32;
|
|||||||
*/
|
*/
|
||||||
constexpr size_t max_string_length = 64;
|
constexpr size_t max_string_length = 64;
|
||||||
|
|
||||||
/**
|
|
||||||
* A simple constant sized buffer for smaller types that can be allocated on the
|
|
||||||
* stack.
|
|
||||||
*/
|
|
||||||
template <std::size_t N>
|
|
||||||
using ArrayBuffer = std::array<uint8_t, N>;
|
|
||||||
|
|
||||||
template <typename B>
|
|
||||||
using OutputAdapter = bitsery::OutputBufferAdapter<B>;
|
|
||||||
|
|
||||||
template <typename B>
|
|
||||||
using InputAdapter = bitsery::InputBufferAdapter<B>;
|
|
||||||
|
|
||||||
// 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
|
||||||
template <class... Ts>
|
template <class... Ts>
|
||||||
@@ -157,15 +144,6 @@ using EventPayload =
|
|||||||
* arguments sent to the `AEffect::dispatch` function.
|
* arguments sent to the `AEffect::dispatch` function.
|
||||||
*/
|
*/
|
||||||
struct Event {
|
struct Event {
|
||||||
// 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 opcode;
|
||||||
int index;
|
int index;
|
||||||
// TODO: This is an intptr_t, if we want to support 32 bit Wine plugins all
|
// TODO: This is an intptr_t, if we want to support 32 bit Wine plugins all
|
||||||
@@ -216,8 +194,6 @@ struct Event {
|
|||||||
* AN instance of this should be sent back as a response to an incoming event.
|
* AN instance of this should be sent back as a response to an incoming event.
|
||||||
*/
|
*/
|
||||||
struct EventResult {
|
struct EventResult {
|
||||||
using buffer_type = ArrayBuffer<max_string_length + 32>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The result that should be returned from the dispatch function.
|
* The result that should be returned from the dispatch function.
|
||||||
*/
|
*/
|
||||||
@@ -241,8 +217,6 @@ struct EventResult {
|
|||||||
* whether `value` contains a value or not.
|
* whether `value` contains a value or not.
|
||||||
*/
|
*/
|
||||||
struct Parameter {
|
struct Parameter {
|
||||||
using buffer_type = ArrayBuffer<16>;
|
|
||||||
|
|
||||||
int index;
|
int index;
|
||||||
std::optional<float> value;
|
std::optional<float> value;
|
||||||
|
|
||||||
@@ -260,8 +234,6 @@ struct Parameter {
|
|||||||
* from the Wine VST host.
|
* from the Wine VST host.
|
||||||
*/
|
*/
|
||||||
struct ParameterResult {
|
struct ParameterResult {
|
||||||
using buffer_type = ArrayBuffer<16>;
|
|
||||||
|
|
||||||
std::optional<float> value;
|
std::optional<float> value;
|
||||||
|
|
||||||
template <typename S>
|
template <typename S>
|
||||||
@@ -276,12 +248,6 @@ struct ParameterResult {
|
|||||||
* processing. The number of samples is encoded in each audio buffer's length.
|
* processing. The number of samples is encoded in each audio buffer's length.
|
||||||
*/
|
*/
|
||||||
struct AudioBuffers {
|
struct AudioBuffers {
|
||||||
// When sending data we could use a vector of the right size, but when
|
|
||||||
// receiving data we don't know how large this vector should be in advance
|
|
||||||
// (or without sending the message length first)
|
|
||||||
using buffer_type =
|
|
||||||
ArrayBuffer<max_audio_channels * max_buffer_size * sizeof(float) + 16>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An audio buffer for each of the plugin's audio channels.
|
* An audio buffer for each of the plugin's audio channels.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -90,8 +90,7 @@ HostBridge::HostBridge(audioMasterCallback host_callback)
|
|||||||
socket_endpoint.path(),
|
socket_endpoint.path(),
|
||||||
bp::env = set_wineprefix(),
|
bp::env = set_wineprefix(),
|
||||||
bp::std_out = wine_stdout,
|
bp::std_out = wine_stdout,
|
||||||
bp::std_err = wine_stderr),
|
bp::std_err = wine_stderr) {
|
||||||
process_buffer(std::make_unique<AudioBuffers::buffer_type>()) {
|
|
||||||
logger.log("Initializing yabridge using '" + vst_host_path.string() + "'");
|
logger.log("Initializing yabridge using '" + vst_host_path.string() + "'");
|
||||||
logger.log("plugin: '" + vst_plugin_path.string() + "'");
|
logger.log("plugin: '" + vst_plugin_path.string() + "'");
|
||||||
logger.log("wineprefix: '" +
|
logger.log("wineprefix: '" +
|
||||||
@@ -202,12 +201,12 @@ void HostBridge::process_replacing(AEffect* /*plugin*/,
|
|||||||
}
|
}
|
||||||
|
|
||||||
const AudioBuffers request{input_buffers, sample_frames};
|
const AudioBuffers request{input_buffers, sample_frames};
|
||||||
write_object(host_vst_process_replacing, request, *process_buffer);
|
write_object(host_vst_process_replacing, request, process_buffer);
|
||||||
|
|
||||||
// /Write the results back to the `outputs` arrays
|
// /Write the results back to the `outputs` arrays
|
||||||
AudioBuffers response;
|
AudioBuffers response;
|
||||||
response =
|
response =
|
||||||
read_object(host_vst_process_replacing, response, *process_buffer);
|
read_object(host_vst_process_replacing, response, process_buffer);
|
||||||
|
|
||||||
// TODO: Doesn't quite work yet, not sure which side is causing problems
|
// TODO: Doesn't quite work yet, not sure which side is causing problems
|
||||||
assert(response.buffers.size() == static_cast<size_t>(plugin.numOutputs));
|
assert(response.buffers.size() == static_cast<size_t>(plugin.numOutputs));
|
||||||
|
|||||||
@@ -163,5 +163,5 @@ class HostBridge {
|
|||||||
* A scratch buffer for sending and receiving data during `process` and
|
* A scratch buffer for sending and receiving data during `process` and
|
||||||
* `processReplacing` calls.
|
* `processReplacing` calls.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<AudioBuffers::buffer_type> process_buffer;
|
std::vector<uint8_t> process_buffer;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,8 +61,7 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
|||||||
vst_host_callback(io_context),
|
vst_host_callback(io_context),
|
||||||
host_vst_parameters(io_context),
|
host_vst_parameters(io_context),
|
||||||
host_vst_process_replacing(io_context),
|
host_vst_process_replacing(io_context),
|
||||||
vst_host_aeffect(io_context),
|
vst_host_aeffect(io_context) {
|
||||||
process_buffer(std::make_unique<AudioBuffers::buffer_type>()) {
|
|
||||||
// Got to love these C APIs
|
// Got to love these C APIs
|
||||||
if (plugin_handle == nullptr) {
|
if (plugin_handle == nullptr) {
|
||||||
throw std::runtime_error("Could not load a shared library at '" +
|
throw std::runtime_error("Could not load a shared library at '" +
|
||||||
@@ -154,7 +153,7 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
|||||||
while (true) {
|
while (true) {
|
||||||
AudioBuffers request;
|
AudioBuffers request;
|
||||||
request = read_object(host_vst_process_replacing, request,
|
request = read_object(host_vst_process_replacing, request,
|
||||||
*process_buffer);
|
process_buffer);
|
||||||
|
|
||||||
// TODO: Check if the plugin doesn't support `processReplacing` and
|
// TODO: Check if the plugin doesn't support `processReplacing` and
|
||||||
// call the legacy `process` function instead
|
// call the legacy `process` function instead
|
||||||
@@ -176,7 +175,7 @@ PluginBridge::PluginBridge(std::string plugin_dll_path,
|
|||||||
request.sample_frames);
|
request.sample_frames);
|
||||||
|
|
||||||
AudioBuffers response{output_buffers, request.sample_frames};
|
AudioBuffers response{output_buffers, request.sample_frames};
|
||||||
write_object(host_vst_process_replacing, response, *process_buffer);
|
write_object(host_vst_process_replacing, response, process_buffer);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -115,5 +115,5 @@ class PluginBridge {
|
|||||||
* A scratch buffer for sending and receiving data during `process` and
|
* A scratch buffer for sending and receiving data during `process` and
|
||||||
* `processReplacing` calls.
|
* `processReplacing` calls.
|
||||||
*/
|
*/
|
||||||
std::unique_ptr<AudioBuffers::buffer_type> process_buffer;
|
std::vector<uint8_t> process_buffer;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user