diff --git a/src/common/serialization/clap.h b/src/common/serialization/clap.h new file mode 100644 index 00000000..f46db77d --- /dev/null +++ b/src/common/serialization/clap.h @@ -0,0 +1,137 @@ +// yabridge: a Wine plugin bridge +// Copyright (C) 2020-2022 Robbert van der Helm +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#pragma once + +#include + +#include "../bitsery/ext/in-place-variant.h" + +#include "../bitsery/ext/message-reference.h" +#include "../utils.h" +#include "common.h" + +// The CLAP communication strategy is identical to what we do for VST3. + +// The messages are defined in the `clap/` directory following the same +// structure as the CLAP repo. + +/** + * When we send a control message from the plugin to the Wine plugin host, this + * encodes the information we request or the operation we want to perform. A + * request of type `ClapControlRequest(T)` should send back a `T::Response`. + * These messages are for main thread functions. + */ +// TODO: Remove placeholder, add actual types +using ClapMainThreadControlRequest = std::variant; + +template +void serialize(S& s, ClapMainThreadControlRequest& payload) { + // All of the objects in the variant should have their own serialization + // function + s.ext(payload, bitsery::ext::InPlaceVariant{}); +} + +/** + * A message type for audio thread functions the host can call on the plugin. + * These functions are called from a hot loop every processing cycle, so we'll + * have a dedicated socket for these for every plugin instance. + * + * This is wrapped in a struct so we can use some bitsery magic to deserialize + * to a reference. This object is kept around as a thread local, and the + * `process_request_` field stores the last process request received. This + * allows other functions to be called in between process calls without having + * to recreate this object. See `MessageReference` and + * `bitsery::ext::MessageReference` for more information. + */ +struct ClapAudioThreadControlRequest { + ClapAudioThreadControlRequest() {} + + /** + * Initialize the variant with an object. In `ClapSockets::send_message()` + * the object gets implicitly converted to the this variant. + */ + template + ClapAudioThreadControlRequest(T request) : payload(std::move(request)) {} + + // TODO: + using Payload = std::variant; + + Payload payload; + + template + void serialize(S& s) { + s.ext( + payload, + bitsery::ext::InPlaceVariant{ + // TODO: Process data + // [&](S& s, + // MessageReference& request_ref) + // { + // // When serializing this reference we'll read the data + // // directly from the referred to object. During + // // deserializing we'll deserialize into the persistent and + // // thread local `process_request` object (see + // // `ClapSockets::add_audio_processor_and_listen`) and then + // // reassign the reference to point to that boject. + // s.ext(request_ref, + // bitsery::ext::MessageReference(process_request_)); + // }, + [](S& s, auto& request) { s.object(request); }}); + } + + // TODO: Process data, update docstring + // /** + // * Used for deserializing the + // `MessageReference` + // * variant. When we encounter this variant, we'll actually deserialize + // the + // * object into this object, and we'll then reassign the reference to + // point + // * to this object. That way we can keep it around as a thread local + // object + // * to prevent unnecessary allocations. + // */ + // std::optional process_request_; +}; + +/** + * When we do a callback from the Wine plugin host to the plugin, this encodes + * the information we want or the operation we want to perform. A request of + * type `ClapMainThreadCallbackRequest(T)` should send back a `T::Response`. + */ +// TODO: Placeholder +using ClapMainThreadCallbackRequest = std::variant; + +// TODO: Uncomment after changing `ClapMainThreadCallbackRequest` +// template +// void serialize(S& s, ClapMainThreadCallbackRequest& payload) { +// // All of the objects in `ClapMainThreadCallbackRequest` should have their own +// // serialization function. +// s.ext(payload, bitsery::ext::InPlaceVariant{}); +// } + +/** + * Fetch the `std::variant<>` from an audio thread request object. This will + * let us use our regular, simple function call dispatch code, but we can still + * store the process data in a separate field (to reduce allocations). + * + * @overload + */ +inline ClapAudioThreadControlRequest::Payload& get_request_variant( + ClapAudioThreadControlRequest& request) noexcept { + return request.payload; +}