diff --git a/README.md b/README.md index 4fdae3cf..a0287433 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,6 @@ There are a few things that should be done before making this public, including: - Implement missing features: - GUIs. - `AEffect` updates, if that's a thing. - - Fix `processReplacing` forwarding. The forwarding works, but plugins don't - write any audio. - Add missing details if any to the architecture section. - Document what this has been tested on and what does or does not work. - Document wine32 support. diff --git a/src/common/communication.h b/src/common/communication.h index 9bc3243a..34ba7379 100644 --- a/src/common/communication.h +++ b/src/common/communication.h @@ -144,9 +144,14 @@ class DefaultDataConverter { } } - virtual void write(const int /*opcode*/, - void* data, - const EventResult& response) { + /** + * Write the reponse back to the data pointer. It's also possible to + * override the return value, this is used in one place to return a pointer + * to a `VstTime` object that's contantly being updated. + */ + virtual std::optional write(const int /*opcode*/, + void* data, + const EventResult& response) { if (response.data.has_value()) { char* output = static_cast(data); @@ -157,6 +162,8 @@ class DefaultDataConverter { std::copy(response.data->begin(), response.data->end(), output); output[response.data->size()] = 0; } + + return std::nullopt; } }; @@ -214,7 +221,11 @@ intptr_t send_event(boost::asio::local::stream_protocol::socket& socket, response.data); } - data_converter.write(opcode, data, response); + const auto return_value_override = + data_converter.write(opcode, data, response); + if (return_value_override.has_value()) { + return return_value_override.value(); + } return response.return_value; } @@ -259,6 +270,7 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket, return &events.as_c_events(); }, [&](WantsChunkBuffer&) -> void* { return string_buffer.data(); }, + [&](WantsVstTimeInfo&) -> void* { return nullptr; }, [&](WantsString&) -> void* { return string_buffer.data(); }}, event.payload); @@ -278,6 +290,17 @@ void passthrough_event(boost::asio::local::stream_protocol::socket& socket, // plugin has written return std::string(*static_cast(data), return_value); }, + [&](WantsVstTimeInfo&) -> std::optional { + // Not sure why the VST API has twenty different ways of + // returning structs, but in this case the value returned from + // the callback function is actually a pointer to a + // `VstTimeInfo` struct! + // TODO: Maybe add a variant from these return types similar to + // `EventPayload`, even though this is as far as I'm aware + // the only non-string/buffer being returned. + return std::string(reinterpret_cast(return_value), + sizeof(VstTimeInfo)); + }, [&](WantsString&) -> std::optional { return std::string(static_cast(data)); }, diff --git a/src/common/logging.cpp b/src/common/logging.cpp index 43afb4b9..348705d1 100644 --- a/src/common/logging.cpp +++ b/src/common/logging.cpp @@ -173,6 +173,7 @@ void Logger::log_event(bool is_dispatch, [&](const WantsChunkBuffer&) { message << ""; }, + [&](const WantsVstTimeInfo&) { message << ""; }, [&](const WantsString&) { message << ""; }}, payload); diff --git a/src/common/serialization.h b/src/common/serialization.h index 45c24e7c..fcde8d5c 100644 --- a/src/common/serialization.h +++ b/src/common/serialization.h @@ -117,6 +117,12 @@ class alignas(16) DynamicVstEvents { */ struct WantsChunkBuffer {}; +/** + * Marker struct to indicate that the event handler will return a pointer to a + * `VstTiemInfo` struct that should be returned transfered. + */ +struct WantsVstTimeInfo {}; + /** * Marker struct to indicate that that the event requires some buffer to write * a C-string into. @@ -155,6 +161,7 @@ using EventPayload = std::variant; template @@ -173,7 +180,8 @@ void serialize(S& s, EventPayload& payload) { s.container1b(event.dump); }); }, - [](S&, WantsChunkBuffer&) {}, [](S&, WantsString&) {}}); + [](S&, WantsChunkBuffer&) {}, + [](S&, WantsVstTimeInfo&) {}, [](S&, WantsString&) {}}); } /** diff --git a/src/plugin/host-bridge.cpp b/src/plugin/host-bridge.cpp index 810f73fc..cc4ceecd 100644 --- a/src/plugin/host-bridge.cpp +++ b/src/plugin/host-bridge.cpp @@ -182,7 +182,9 @@ class DispatchDataConverter : DefaultDataConverter { } } - void write(const int opcode, void* data, const EventResult& response) { + std::optional write(const int opcode, + void* data, + const EventResult& response) { switch (opcode) { case effGetChunk: // Write the chunk data to some publically accessible place in @@ -192,9 +194,10 @@ class DispatchDataConverter : DefaultDataConverter { chunk.assign(response.data->begin(), response.data->end()); *static_cast(data) = chunk.data(); + return std::nullopt; break; default: - DefaultDataConverter::write(opcode, data, response); + return DefaultDataConverter::write(opcode, data, response); break; } } diff --git a/src/wine-host/plugin-bridge.cpp b/src/wine-host/plugin-bridge.cpp index cd205cab..ce5e0280 100644 --- a/src/wine-host/plugin-bridge.cpp +++ b/src/wine-host/plugin-bridge.cpp @@ -191,6 +191,8 @@ void PluginBridge::wait() { class HostCallbackDataConverter : DefaultDataConverter { public: + HostCallbackDataConverter(VstTimeInfo& time_info) : time(time_info) {} + std::optional read(const int opcode, const intptr_t value, const void* data) { @@ -210,15 +212,37 @@ class HostCallbackDataConverter : DefaultDataConverter { return std::nullopt; break; + case audioMasterGetTime: + return WantsVstTimeInfo{}; + break; default: return DefaultDataConverter::read(opcode, value, data); break; } } - void write(const int opcode, void* data, const EventResult& response) { - return DefaultDataConverter::write(opcode, data, response); + std::optional write(const int opcode, + void* data, + const EventResult& response) { + switch (opcode) { + case audioMasterGetTime: + // Write the returned `VstTimeInfo` struct into a field and make + // the function return a poitner to it + // TODO: Start a time to update this on the host bridge once + // it's been requested. Not sure if this is needed though! + time = *static_cast( + static_cast(response.data->data())); + + return reinterpret_cast(&time); + break; + default: + return DefaultDataConverter::write(opcode, data, response); + break; + } } + + private: + VstTimeInfo& time; }; intptr_t PluginBridge::host_callback(AEffect* /*plugin*/, @@ -227,7 +251,7 @@ intptr_t PluginBridge::host_callback(AEffect* /*plugin*/, intptr_t value, void* data, float option) { - HostCallbackDataConverter converter; + HostCallbackDataConverter converter(time_info); return send_event(vst_host_callback, converter, opcode, index, value, data, option, std::nullopt); } diff --git a/src/wine-host/plugin-bridge.h b/src/wine-host/plugin-bridge.h index 20296a6e..b09da020 100644 --- a/src/wine-host/plugin-bridge.h +++ b/src/wine-host/plugin-bridge.h @@ -61,6 +61,14 @@ class PluginBridge { intptr_t host_callback(AEffect*, int, int, intptr_t, void*, float); + /** + * With the `audioMasterGetTime` host callback the plugin expects the return + * value from the calblack to be a pointer to a VstTimeInfo struct. + * + * TODO: Should this be updated automatically? + */ + VstTimeInfo time_info; + private: /** * The shared library handle of the VST plugin. I sadly could not get