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:
Robbert van der Helm
2020-03-09 21:32:49 +01:00
parent adf33e84a8
commit 8dad15b597
6 changed files with 40 additions and 87 deletions
+31 -42
View File
@@ -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;
-34
View File
@@ -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.
*/ */
+3 -4
View File
@@ -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));
+1 -1
View File
@@ -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;
}; };
+3 -4
View File
@@ -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);
} }
}); });
+1 -1
View File
@@ -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;
}; };