From b96404e317f8071ae61ab6ba3e63ae60106f3ca7 Mon Sep 17 00:00:00 2001 From: daiqingshuang Date: Tue, 19 Aug 2025 19:13:40 +0800 Subject: [PATCH] =?UTF-8?q?todo=20rpc=E6=A1=86=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/backend/CMakeLists.txt | 4 +- src/backend/cmake_script/detect_os.cmake | 7 ++ src/backend/src/engine/CMakeLists.txt | 11 +- .../src/engine/src/grpc/engine_grpc.cpp | 1 + src/backend/src/engine/src/grpc/engine_grpc.h | 96 +++++++++++++++ src/backend/src/engine/src/main.cpp | 112 +++--------------- .../src/plugin_manage/plugin_host_manager.cpp | 53 +++++++++ .../src/plugin_manage/plugin_host_manager.h | 37 ++++++ .../src/plugin_manage/plugin_instance.cpp | 64 ++++++++++ .../src/plugin_manage/plugin_instance.h | 32 +++++ src/backend/src/engine/src/rpc/engine_rpc.cpp | 10 ++ src/backend/src/engine/src/rpc/engine_rpc.h | 8 ++ src/backend/src/misc/src/host_common.h | 40 ------- src/backend/src/misc/src/lazy_singleton.h | 12 ++ .../src/{ => library_handle}/library_handle.h | 0 .../windows/library_handle.cpp | 2 +- src/backend/src/misc/src/rpc/common.h | 34 ++++++ src/backend/src/misc/src/rpc/rpc_manager.cpp | 0 src/backend/src/misc/src/rpc/rpc_manager.h | 47 ++++++++ src/backend/src/misc/src/rpc/rpc_type.h | 53 +++++++++ src/backend/src/misc/src/rpc/session.cpp | 41 +++++++ src/backend/src/misc/src/rpc/session.h | 72 +++++++++++ .../src/misc/src/{size_type.h => vec.h} | 0 src/backend/src/vst2_host/CMakeLists.txt | 5 +- src/backend/src/vst2_host/src/main.cpp | 58 +++++++-- src/backend/src/vst2_host/src/vst2host.h | 4 +- src/backend/src/vst3_host/CMakeLists.txt | 2 +- src/backend/vcpkg.json | 27 +++-- 28 files changed, 663 insertions(+), 169 deletions(-) create mode 100644 src/backend/src/engine/src/grpc/engine_grpc.cpp create mode 100644 src/backend/src/engine/src/grpc/engine_grpc.h create mode 100644 src/backend/src/engine/src/plugin_manage/plugin_host_manager.cpp create mode 100644 src/backend/src/engine/src/plugin_manage/plugin_host_manager.h create mode 100644 src/backend/src/engine/src/plugin_manage/plugin_instance.cpp create mode 100644 src/backend/src/engine/src/plugin_manage/plugin_instance.h create mode 100644 src/backend/src/engine/src/rpc/engine_rpc.cpp create mode 100644 src/backend/src/engine/src/rpc/engine_rpc.h delete mode 100644 src/backend/src/misc/src/host_common.h create mode 100644 src/backend/src/misc/src/lazy_singleton.h rename src/backend/src/misc/src/{ => library_handle}/library_handle.h (100%) rename src/backend/src/misc/src/{ => library_handle}/windows/library_handle.cpp (93%) create mode 100644 src/backend/src/misc/src/rpc/common.h create mode 100644 src/backend/src/misc/src/rpc/rpc_manager.cpp create mode 100644 src/backend/src/misc/src/rpc/rpc_manager.h create mode 100644 src/backend/src/misc/src/rpc/rpc_type.h create mode 100644 src/backend/src/misc/src/rpc/session.cpp create mode 100644 src/backend/src/misc/src/rpc/session.h rename src/backend/src/misc/src/{size_type.h => vec.h} (100%) diff --git a/src/backend/CMakeLists.txt b/src/backend/CMakeLists.txt index 5fc49c4..069eba7 100644 --- a/src/backend/CMakeLists.txt +++ b/src/backend/CMakeLists.txt @@ -50,7 +50,7 @@ find_package(gRPC CONFIG REQUIRED) find_package(Protobuf CONFIG REQUIRED) find_package(ZeroMQ CONFIG REQUIRED) find_package(spdlog CONFIG REQUIRED) -find_package(Boost CONFIG REQUIRED COMPONENTS system thread interprocess lockfree asio) +find_package(Boost CONFIG REQUIRED COMPONENTS system interprocess lockfree asio process) # --- Protocol Buffers 编译 (Protocol Buffers Compilation) --- # 使用自定义函数编译 .proto 文件,自动生成 C++ 源代码和 gRPC 服务代码。 @@ -60,7 +60,7 @@ find_package(Boost CONFIG REQUIRED COMPONENTS system thread interprocess lockfre # @param OUTPUT_PATH: 生成的 .pb.h, .pb.cc, .grpc.pb.h, .grpc.pb.cc 文件的输出目录。 # @param GRPC_ENABLED: 设置为 TRUE,表示同时生成 gRPC 服务相关的代码。 compile_proto_files( - TARGET_NAME alicho_proto + TARGET_NAME AlichoProto PROTO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../proto OUTPUT_PATH ${CMAKE_CURRENT_SOURCE_DIR}/proto GRPC_ENABLED TRUE diff --git a/src/backend/cmake_script/detect_os.cmake b/src/backend/cmake_script/detect_os.cmake index 2fbae8d..ff3971a 100644 --- a/src/backend/cmake_script/detect_os.cmake +++ b/src/backend/cmake_script/detect_os.cmake @@ -23,6 +23,11 @@ function(add_os_definitions target) set(alicho_def_arch_64bit 0) set(alicho_def_arch_32bit 0) set(alicho_def_apple 0) # 用于 iOS 和 macOS 的通用定义 + set(alicho_def_debug 0) # 当前是否处于调试模式 + + if (CMAKE_BUILD_TYPE STREQUAL "Debug") + set(alicho_def_debug 1) + endif () # -- 操作系统检测与赋值 -- # 注意检测顺序:优先检测更具体的平台 @@ -113,6 +118,8 @@ function(add_os_definitions target) list(APPEND definitions_list "ALICHO_PLATFORM_POSIX=${alicho_def_posix}") list(APPEND definitions_list "ALICHO_PLATFORM_IS_MOBILE=${alicho_def_mobile}") + list(APPEND definitions_list "ALICHO_DEBUG=${alicho_def_debug}") # 当前是否处于调试模式 + # --- 阶段 3: 应用所有定义 --- # **关键:使用一次调用将所有定义添加到目标** if(definitions_list) # 确保列表非空 diff --git a/src/backend/src/engine/CMakeLists.txt b/src/backend/src/engine/CMakeLists.txt index 13e8aa6..5222a98 100644 --- a/src/backend/src/engine/CMakeLists.txt +++ b/src/backend/src/engine/CMakeLists.txt @@ -30,5 +30,14 @@ target_link_libraries(${PROJECT_NAME} PRIVATE gRPC::grpc++ protobuf::libprotobuf libzmq - alicho_proto + Boost::process + Boost::interprocess + Boost::system + Boost::lockfree + Boost::asio + AlichoProto + AlichoMisc + ws2_32 ) +target_compile_definitions(${PROJECT_NAME} PRIVATE _WIN32_WINNT=0x0A00 WIN32_LEAN_AND_MEAN) +target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src) diff --git a/src/backend/src/engine/src/grpc/engine_grpc.cpp b/src/backend/src/engine/src/grpc/engine_grpc.cpp new file mode 100644 index 0000000..8f1684c --- /dev/null +++ b/src/backend/src/engine/src/grpc/engine_grpc.cpp @@ -0,0 +1 @@ +#include "engine_grpc.h" diff --git a/src/backend/src/engine/src/grpc/engine_grpc.h b/src/backend/src/engine/src/grpc/engine_grpc.h new file mode 100644 index 0000000..5372e48 --- /dev/null +++ b/src/backend/src/engine/src/grpc/engine_grpc.h @@ -0,0 +1,96 @@ +#pragma once + +#include "ctrl/frontend_to_engine.grpc.pb.h" + +class daw_api_project_service : public daw::api::ProjectService::Service { +public: + grpc::Status NewProject(grpc::ServerContext* context, + const google::protobuf::Empty* request, + daw::api::ProjectState* response) override { + + return grpc::Status::OK; + } + + grpc::Status LoadProject(grpc::ServerContext* context, + const daw::api::LoadProjectRequest* request, + daw::api::ProjectState* response) override { + + return grpc::Status::OK; + } + + grpc::Status SaveProject(grpc::ServerContext* context, + const daw::api::SaveProjectRequest* request, + daw::api::StatusResponse* response) override { + + return grpc::Status::OK; + } +}; + +class daw_api_transport_service : public daw::api::TransportService::Service { +public: + virtual ~daw_api_transport_service() override; + + virtual grpc::Status Play(grpc::ServerContext* context, + const google::protobuf::Empty* request, + daw::api::StatusResponse* response) override; + + virtual grpc::Status Pause(grpc::ServerContext* context, + const google::protobuf::Empty* request, + daw::api::StatusResponse* response) override; + + virtual grpc::Status Stop(grpc::ServerContext* context, + const google::protobuf::Empty* request, + daw::api::StatusResponse* response) override; + + virtual grpc::Status SetTempo(grpc::ServerContext* context, + const daw::api::SetTempoRequest* request, + daw::api::StatusResponse* response) override; +}; + +class daw_api_track_service : public daw::api::TrackService::Service { +public: + grpc::Status AddTrack(grpc::ServerContext* context, + const daw::api::AddTrackRequest* request, + daw::api::TrackInfo* response) override { + + return grpc::Status::OK; + } + + grpc::Status RemoveTrack(grpc::ServerContext* context, + const daw::api::TrackIdRequest* request, + daw::api::StatusResponse* response) override { + + return grpc::Status::OK; + } + + grpc::Status SetTrackVolume(grpc::ServerContext* context, + const daw::api::SetTrackVolumeRequest* request, + daw::api::StatusResponse* response) override { + + return grpc::Status::OK; + } + + grpc::Status SetTrackPan(grpc::ServerContext* context, + const daw::api::SetTrackPanRequest* request, + daw::api::StatusResponse* response) override { + + return grpc::Status::OK; + } +}; + +class daw_api_plugin_service : public daw::api::PluginService::Service { +public: + grpc::Status LoadPlugin(grpc::ServerContext* context, + const daw::api::LoadPluginRequest* request, + daw::api::PluginInfo* response) override { + + return grpc::Status::OK; + } + + grpc::Status SetPluginParameter(grpc::ServerContext* context, + const daw::api::SetPluginParameterRequest* request, + daw::api::StatusResponse* response) override { + + return grpc::Status::OK; + } +}; diff --git a/src/backend/src/engine/src/main.cpp b/src/backend/src/engine/src/main.cpp index 0287345..8948958 100644 --- a/src/backend/src/engine/src/main.cpp +++ b/src/backend/src/engine/src/main.cpp @@ -1,104 +1,22 @@ -// -// Created by A on 2025/8/12. -// -#include "zmq.h" -#include "ctrl/frontend_to_engine.grpc.pb.h" -class daw_api_project_service : public daw::api::ProjectService::Service { -public: - grpc::Status NewProject(grpc::ServerContext* context, - const google::protobuf::Empty* request, - daw::api::ProjectState* response) override { - - return grpc::Status::OK; - } - - grpc::Status LoadProject(grpc::ServerContext* context, - const daw::api::LoadProjectRequest* request, - daw::api::ProjectState* response) override { - - return grpc::Status::OK; - } - - grpc::Status SaveProject(grpc::ServerContext* context, - const daw::api::SaveProjectRequest* request, - daw::api::StatusResponse* response) override { - - return grpc::Status::OK; - } -}; - -class daw_api_transport_service : public daw::api::TransportService::Service { -public: - virtual ~daw_api_transport_service() override; - - virtual grpc::Status Play(grpc::ServerContext* context, - const google::protobuf::Empty* request, - daw::api::StatusResponse* response) override; - - virtual grpc::Status Pause(grpc::ServerContext* context, - const google::protobuf::Empty* request, - daw::api::StatusResponse* response) override; - - virtual grpc::Status Stop(grpc::ServerContext* context, - const google::protobuf::Empty* request, - daw::api::StatusResponse* response) override; - - virtual grpc::Status SetTempo(grpc::ServerContext* context, - const daw::api::SetTempoRequest* request, - daw::api::StatusResponse* response) override; -}; - -class daw_api_track_service : public daw::api::TrackService::Service { -public: - grpc::Status AddTrack(grpc::ServerContext* context, - const daw::api::AddTrackRequest* request, - daw::api::TrackInfo* response) override { - - return grpc::Status::OK; - } - - grpc::Status RemoveTrack(grpc::ServerContext* context, - const daw::api::TrackIdRequest* request, - daw::api::StatusResponse* response) override { - - return grpc::Status::OK; - } - - grpc::Status SetTrackVolume(grpc::ServerContext* context, - const daw::api::SetTrackVolumeRequest* request, - daw::api::StatusResponse* response) override { - - return grpc::Status::OK; - } - - grpc::Status SetTrackPan(grpc::ServerContext* context, - const daw::api::SetTrackPanRequest* request, - daw::api::StatusResponse* response) override { - - return grpc::Status::OK; - } -}; - -class daw_api_plugin_service : public daw::api::PluginService::Service { -public: - grpc::Status LoadPlugin(grpc::ServerContext* context, - const daw::api::LoadPluginRequest* request, - daw::api::PluginInfo* response) override { - - return grpc::Status::OK; - } - - grpc::Status SetPluginParameter(grpc::ServerContext* context, - const daw::api::SetPluginParameterRequest* request, - daw::api::StatusResponse* response) override { - - return grpc::Status::OK; - } -}; +#include "plugin_manage/plugin_host_manager.h" +#include "rpc/rpc_session.h" +#include int main(int argc, char *argv[]) { + std::filesystem::path path(R"(D:\Projects\Alicho\src\backend\src\vst2_host\test\4Front Piano x64.dll)"); + plugin_host_manager::get_instance().load_plugin(path); + + uint32_t times = 0; + while (true) { + // 模拟主循环 + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + if (++times > 10) { + break; // 运行10次后退出 + } + } + return 0; } diff --git a/src/backend/src/engine/src/plugin_manage/plugin_host_manager.cpp b/src/backend/src/engine/src/plugin_manage/plugin_host_manager.cpp new file mode 100644 index 0000000..721c893 --- /dev/null +++ b/src/backend/src/engine/src/plugin_manage/plugin_host_manager.cpp @@ -0,0 +1,53 @@ +#include "plugin_host_manager.h" + +#include "plugin_instance.h" + +plugin_host_manager::~plugin_host_manager() { + std::vector ids_to_unload; + { + std::lock_guard lock(mutex_); + for (const auto& id: plugin_instances_ | std::views::keys) { + ids_to_unload.push_back(id); + } + } + for (const auto& id: ids_to_unload) { + unload_plugin(id); + } +} + +plugin_instance* plugin_host_manager::load_plugin(const std::filesystem::path& plugin_binary_path) { + auto plugin_id = get_next_plugin_id(); + + plugin_instance* ptr = nullptr; + try { + auto instance = std::make_unique(io_context_, plugin_id, plugin_binary_path); + ptr = instance.get(); + + std::lock_guard lock(mutex_); + plugin_instances_.emplace(plugin_id, std::move(instance)); + } + catch (const std::exception& e) { + // TODO: 如果创建失败,清理已分配的资源 + spdlog::error("Failed to load plugin {}: {}", plugin_binary_path.string(), e.what()); + } + + return ptr; +} + +void plugin_host_manager::unload_plugin(uint32_t plugin_id) { + +} + +void plugin_host_manager::register_session(uint32_t plugin_id, std::shared_ptr session) { + +} + +plugin_instance* plugin_host_manager::get_plugin_instance(uint32_t plugin_id) { + std::lock_guard lock(mutex_); + const auto& it = plugin_instances_.find(plugin_id); + if (it != plugin_instances_.end()) { + return it->second.get(); + } + return nullptr; +} + diff --git a/src/backend/src/engine/src/plugin_manage/plugin_host_manager.h b/src/backend/src/engine/src/plugin_manage/plugin_host_manager.h new file mode 100644 index 0000000..6eb05d5 --- /dev/null +++ b/src/backend/src/engine/src/plugin_manage/plugin_host_manager.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include + +#include "lazy_singleton.h" +#include "plugin_instance.h" +#include "rpc/rpc_session.h" + +class plugin_host_manager : public lazy_singleton { +public: + ~plugin_host_manager(); + + plugin_instance* load_plugin(const std::filesystem::path& plugin_binary_path); + void unload_plugin(uint32_t plugin_id); + + void register_session(uint32_t plugin_id, std::shared_ptr session); + + plugin_instance* get_plugin_instance(uint32_t plugin_id); + + template + void for_each_plugin_instance(Func&& func) { + std::lock_guard lock(mutex_); + for (const auto& instance: plugin_instances_ | std::views::values) { + func(instance.get()); + } + } +private: + auto get_next_plugin_id() { + return plugin_id_.fetch_add(1); + } + + std::mutex mutex_; + std::unordered_map> plugin_instances_; + std::atomic_uint32_t plugin_id_ = 1; + boost::asio::io_context io_context_; + rpc_server rpc_server_{ io_context_ }; +}; diff --git a/src/backend/src/engine/src/plugin_manage/plugin_instance.cpp b/src/backend/src/engine/src/plugin_manage/plugin_instance.cpp new file mode 100644 index 0000000..8df97b4 --- /dev/null +++ b/src/backend/src/engine/src/plugin_manage/plugin_instance.cpp @@ -0,0 +1,64 @@ +#include "plugin_instance.h" + +#include + +namespace bi = boost::interprocess; +namespace bp = boost::process; + +auto execute_plugin_process(uint32_t id, boost::asio::io_context& ctx, const std::filesystem::path& plugin_path) { + if (!std::filesystem::exists(plugin_path)) { + throw std::runtime_error("Plugin path does not exist: " + plugin_path.string()); + } + if (!plugin_path.has_extension()) { + throw std::runtime_error("Plugin path must have an extension: " + plugin_path.string()); + } + // 启动沙箱进程 + const auto& ext = plugin_path.extension(); + const static std::unordered_map supported_exts = { + {".so", "AlichoPluginHostVst2.exe"}, + {".dll", "AlichoPluginHostVst2.exe"}, + {".dylib", "AlichoPluginHostVst2.exe"}, + {".vst2", "AlichoPluginHostVst2.exe"}, + {".vst3", "AlichoPluginHostVst3.exe"}, + {".clap", "AlichoPluginHostClap.exe"}, + }; + if (!supported_exts.contains(ext.string())) { + throw std::runtime_error("Unsupported plugin file type: " + ext.string()); + } + const auto& host_executable = supported_exts.at(ext.string()); + const auto& host_path = std::filesystem::current_path() / host_executable; + if (!std::filesystem::exists(host_path)) { + throw std::runtime_error("Host executable not found: " + host_path.string()); + } + + return new bp::process(ctx, host_path.string(), { get_shm_in_name(id), get_shm_out_name(id) }); +} + +plugin_instance::plugin_instance(boost::asio::io_context& ctx, + uint32_t in_id, + const std::filesystem::path& in_plugin_path) { + + id = in_id; + + // 创建共享内存段 + input_segment = std::make_unique(bi::open_or_create, get_shm_in_name(id).c_str(), SHM_SIZE); + output_segment = std::make_unique(bi::open_or_create, get_shm_out_name(id).c_str(), SHM_SIZE); + + // 获取锁无阻塞队列 + input_queue = input_segment->find_or_construct("input_queue")(); + output_queue = output_segment->find_or_construct("output_queue")(); + + process = std::shared_ptr(execute_plugin_process(id, ctx, in_plugin_path), [](boost::process::process* p) { + delete p; + }); +} + +plugin_instance::~plugin_instance() { + output_queue->push({}); + if (input_segment) { + bi::shared_memory_object::remove(get_shm_in_name(id).c_str()); + } + if (output_segment) { + bi::shared_memory_object::remove(get_shm_out_name(id).c_str()); + } +} diff --git a/src/backend/src/engine/src/plugin_manage/plugin_instance.h b/src/backend/src/engine/src/plugin_manage/plugin_instance.h new file mode 100644 index 0000000..858fdba --- /dev/null +++ b/src/backend/src/engine/src/plugin_manage/plugin_instance.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include +#include + +#include "rpc/common.h" + +class rpc_session; + +struct plugin_instance { + plugin_instance(boost::asio::io_context& ctx, uint32_t in_id, const std::filesystem::path& in_plugin_path); + ~plugin_instance(); + uint32_t id = 0; + std::shared_ptr process; // 沙箱进程句柄 + + // 共享内存资源 + std::unique_ptr input_segment; + std::unique_ptr output_segment; + lock_free_queue* input_queue = nullptr; // AudioEngine -> Host + lock_free_queue* output_queue = nullptr; // Host -> AudioEngine + + // 控制连接 + // std::shared_ptr session; + + // 状态标识 + std::atomic_bool is_processing_done{ true }; // 插件是否处理完毕 + std::atomic_bool is_registered{ false }; // RPC会话是否注册 + + auto is_process_running() const { + return process && process->running(); + } +}; diff --git a/src/backend/src/engine/src/rpc/engine_rpc.cpp b/src/backend/src/engine/src/rpc/engine_rpc.cpp new file mode 100644 index 0000000..ddf2a9a --- /dev/null +++ b/src/backend/src/engine/src/rpc/engine_rpc.cpp @@ -0,0 +1,10 @@ +#include "engine_rpc.h" + +void __rpc_handler_func_PROCESS_DONE(uint64_t seq_id, const rpc::process_done& body) { + +} + +void __rpc_handler_func_REGISTER_HOST(uint64_t seq_id, const rpc::register_host& body) { + +} + diff --git a/src/backend/src/engine/src/rpc/engine_rpc.h b/src/backend/src/engine/src/rpc/engine_rpc.h new file mode 100644 index 0000000..7e9dda3 --- /dev/null +++ b/src/backend/src/engine/src/rpc/engine_rpc.h @@ -0,0 +1,8 @@ +#pragma once +#include "rpc/common.h" +#include "rpc/rpc_type.h" +#include "rpc/rpc_manager.h" + +REGISTER_RPC_HANDLER(LOG, rpc::log) +REGISTER_RPC_HANDLER(REGISTER_HOST, rpc::register_host) +REGISTER_RPC_HANDLER(PROCESS_DONE, rpc::process_done) diff --git a/src/backend/src/misc/src/host_common.h b/src/backend/src/misc/src/host_common.h deleted file mode 100644 index 04c8c3b..0000000 --- a/src/backend/src/misc/src/host_common.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include -#include - -constexpr size_t AUDIO_BLOCK_SIZE = 4096; // 最大音频块大小 -constexpr size_t QUEUE_CAPACITY = 4; // 音频队列容量 -constexpr size_t MAX_PAYLOAD_SIZE = 246; // 最大有效载荷大小 - -struct audio_block { - float data[AUDIO_BLOCK_SIZE]; // 音频数据 - size_t size; // 有效数据大小 -}; - -enum class message_type : uint32_t { - // Host -> AudioEngine - REGISTER = 1, // 注册插件 - PROCESS_DONE, // 处理完成 - PARAMETER_VALUE_CHANGED, // 参数值改变 - - // AudioEngine -> Host - PROCESS_AUDIO_BLOCK = 10001, // 处理音频块 - SET_PARAMETER, // 设置参数 - SHUTDOWN // 关闭插件 -}; - -// RPC 消息帧结构 -struct rpc_message { - message_type type; - uint64_t seq_id; // 消息序列号, 用于跟踪请求和响应 - uint32_t payload_size; // 有效载荷大小 - std::array payload; -}; - -// 根据 plugin_id 生成 IPC 资源名称 -inline std::string get_uds_path() { -#if ALICHO_PLATFORM_WINDOWS - return -} diff --git a/src/backend/src/misc/src/lazy_singleton.h b/src/backend/src/misc/src/lazy_singleton.h new file mode 100644 index 0000000..fbf68c5 --- /dev/null +++ b/src/backend/src/misc/src/lazy_singleton.h @@ -0,0 +1,12 @@ +#pragma once + +template +class lazy_singleton { +public: + static auto get_instance() -> T& { + static T instance; + return instance; + } +protected: + lazy_singleton() = default; +}; diff --git a/src/backend/src/misc/src/library_handle.h b/src/backend/src/misc/src/library_handle/library_handle.h similarity index 100% rename from src/backend/src/misc/src/library_handle.h rename to src/backend/src/misc/src/library_handle/library_handle.h diff --git a/src/backend/src/misc/src/windows/library_handle.cpp b/src/backend/src/misc/src/library_handle/windows/library_handle.cpp similarity index 93% rename from src/backend/src/misc/src/windows/library_handle.cpp rename to src/backend/src/misc/src/library_handle/windows/library_handle.cpp index b191033..34b9786 100644 --- a/src/backend/src/misc/src/windows/library_handle.cpp +++ b/src/backend/src/misc/src/library_handle/windows/library_handle.cpp @@ -1,4 +1,4 @@ -#include "library_handle.h" +#include "library_handle/library_handle.h" #include library_handle* library_handle::create(const std::filesystem::path& in_path) { diff --git a/src/backend/src/misc/src/rpc/common.h b/src/backend/src/misc/src/rpc/common.h new file mode 100644 index 0000000..0504634 --- /dev/null +++ b/src/backend/src/misc/src/rpc/common.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +constexpr size_t AUDIO_BLOCK_SIZE = 4096; // 最大音频块大小 +constexpr size_t QUEUE_CAPACITY = 4; // 音频队列容量 +constexpr size_t MAX_PAYLOAD_SIZE = 1024; // 最大有效载荷大小 +constexpr size_t SHM_SIZE = 1024 * 1024; // 共享内存段大小 + +struct audio_block { + float data[AUDIO_BLOCK_SIZE * 2]; // 音频数据, 双声道 + size_t size; // 有效数据大小 +}; + +// 单生产者单消费者无锁队列 +using lock_free_queue = boost::lockfree::spsc_queue>; + +// 根据 plugin_id 生成 IPC 资源名称 +inline std::string get_uds_path() { +#if ALICHO_PLATFORM_WINDOWS + return "alicho_host.sock"; +#else + return "/tmp/alicho_host"; // Linux 使用 Unix 域套接字 +#endif +} + +inline std::string get_shm_in_name(const uint32_t plugin_id) { + return "alicho_host_in_" + std::to_string(plugin_id); +} + +inline std::string get_shm_out_name(const uint32_t plugin_id) { + return "alicho_host_out_" + std::to_string(plugin_id); +} diff --git a/src/backend/src/misc/src/rpc/rpc_manager.cpp b/src/backend/src/misc/src/rpc/rpc_manager.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/backend/src/misc/src/rpc/rpc_manager.h b/src/backend/src/misc/src/rpc/rpc_manager.h new file mode 100644 index 0000000..b234db4 --- /dev/null +++ b/src/backend/src/misc/src/rpc/rpc_manager.h @@ -0,0 +1,47 @@ +#pragma once +#include + +#include "lazy_singleton.h" +#include "rpc_type.h" +#include "common.h" + +using rpc_callback = std::function&)>; + +class rpc_message_handler : public lazy_singleton { +public: + void register_rpc_handler(rpc::message_type type, rpc_callback handler) { + if (handlers_.contains(type)) + spdlog::warn("RPC消息类型 {} 已经注册,覆盖旧处理器", static_cast(type)); + handlers_[type] = std::move(handler); + } + + void handle_message(const rpc::rpc_header& in_header, const std::vector& in_payload) { + const auto& it = handlers_.find(in_header.type); + if (it != handlers_.end()) { + it->second(in_header.seq_id, in_payload); + } else { + spdlog::warn("未处理的RPC消息类型: {}", static_cast(in_header.type)); + } + } +private: + std::unordered_map handlers_; +}; + +template +struct rpc_handler_register { + explicit rpc_handler_register(rpc::message_type in_type, const std::function& in_func) { + auto cast_func = [in_func](uint64_t in_seq_id, const std::vector& msg) { +#if ALICHO_DEBUG + assert(msg.size() == sizeof(T)); +#endif + auto payload = reinterpret_cast(msg.data()); + in_func(in_seq_id, *payload); + }; + rpc_message_handler::get_instance().register_rpc_handler(in_type, cast_func); + } +}; + +#define REGISTER_RPC_HANDLER(type, body_type) \ + void __rpc_handler_func_##type(uint64_t seq_id, const body_type& body); \ + static rpc_handler_register __rpc_handler_register_##type(rpc::message_type::type, __rpc_handler_func_##type); + diff --git a/src/backend/src/misc/src/rpc/rpc_type.h b/src/backend/src/misc/src/rpc/rpc_type.h new file mode 100644 index 0000000..40cb5af --- /dev/null +++ b/src/backend/src/misc/src/rpc/rpc_type.h @@ -0,0 +1,53 @@ +// RPC调用消息体结构, 由AudioEngine和PluginHost之间传输 +#pragma once +#include +#include + +namespace rpc { + enum class message_type : uint32_t { + WTF = 0, // 未知消息类型, 用于调试 + + // Host -> AudioEngine + LOG = 1, // 日志消息 + REGISTER_HOST, // 注册插件 + PROCESS_DONE, // 处理完成 + PARAMETER_VALUE_CHANGED, // 参数值改变 + + // AudioEngine -> Host + PROCESS_AUDIO_BLOCK = 10001, // 处理音频块 + SET_PARAMETER, // 设置参数 + SHUTDOWN // 关闭插件 + }; + + struct rpc_header { + message_type type; // 消息类型 + uint64_t seq_id; // 消息序列号 + uint32_t payload_size; // 有效载荷大小 + }; +} + +namespace rpc { + struct log { + uint32_t id; // 插件ID + spdlog::level::level_enum level; + std::array str; + }; + + struct register_host { + uint32_t id; + }; + + struct process_done { + uint32_t id; // 插件ID + }; + + struct parameter_value_changed { + uint32_t id; // 插件ID + uint32_t parameter_id; // 参数ID + float value; // 新值 + }; +} + +namespace rpc { + +} diff --git a/src/backend/src/misc/src/rpc/session.cpp b/src/backend/src/misc/src/rpc/session.cpp new file mode 100644 index 0000000..faa2293 --- /dev/null +++ b/src/backend/src/misc/src/rpc/session.cpp @@ -0,0 +1,41 @@ +#include "session.h" + +#include "rpc_manager.h" + +void rpc::session::start() { + do_read_header(); +} + +void rpc::session::stop() { + +} + +void rpc::session::do_read_header() { + auto self = shared_from_this(); + // 读取消息到 read_queue_ 中 + auto func = [self](boost::system::error_code ec, std::size_t in_size) { + if (ec) + return; + self->do_read_body(); + }; + boost::asio::async_read(socket_, boost::asio::buffer{read_header_, 4}, func); +} + +void rpc::session::do_read_body() { + auto self = shared_from_this(); + auto func = [self](boost::system::error_code ec, std::size_t in_size) { + if (ec) + return; + // 处理读取到的消息 + rpc_message_handler::get_instance().handle_message(self->read_header_, self->read_body_); + }; + read_body_.resize(read_header_.payload_size); + boost::asio::async_read(socket_, boost::asio::buffer(read_body_, read_header_.payload_size), func); +} + +void rpc::session::do_write() { +} + +void rpc::server::do_accept() { + +} diff --git a/src/backend/src/misc/src/rpc/session.h b/src/backend/src/misc/src/rpc/session.h new file mode 100644 index 0000000..b11cf30 --- /dev/null +++ b/src/backend/src/misc/src/rpc/session.h @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include + +#include "common.h" +#include "rpc_type.h" + +namespace rpc { + class session : public std::enable_shared_from_this { + public: + explicit session(boost::asio::local::stream_protocol::socket&& in_socket) + : socket_(std::move(in_socket)) { + } + + void start(); + void stop(); + + template + auto async_call(Msg&& msg) { + // 这里可以实现异步调用逻辑 + // 比如将消息放入写队列,触发写操作等 + return boost::asio::post(socket_.get_executor(), [this, msg = std::forward(msg)]() mutable { + write_queue_.emplace_back(std::move(msg)); + if (write_queue_.size() == 1) { + do_write(); + } + }); + } + private: + void do_read_header(); + void do_read_body(); + void do_write(); + + boost::asio::local::stream_protocol::socket socket_; + + rpc_header read_header_; + std::vector read_body_; + + std::deque> write_queue_; // 写队列, 用于发送消息 + std::atomic_uint32_t next_id_{ 1 }; + }; + + class server : public std::enable_shared_from_this { + public: + explicit server(boost::asio::io_context& io_context) : acceptor_(io_context, + boost::asio::local::stream_protocol::endpoint(get_uds_path()), false) { + } + + private: + void do_accept(); + + boost::asio::local::stream_protocol::acceptor acceptor_;; + }; + + class client { + public: + explicit client(boost::asio::io_context& io_context) { + boost::asio::local::stream_protocol::socket socket_(io_context); + socket_.connect(get_uds_path()); + session_ = std::make_shared(std::move(socket_)); + session_->start(); + } + + template + auto async_call(Msg&& msg) { + + } + private: + std::shared_ptr session_; + }; +} diff --git a/src/backend/src/misc/src/size_type.h b/src/backend/src/misc/src/vec.h similarity index 100% rename from src/backend/src/misc/src/size_type.h rename to src/backend/src/misc/src/vec.h diff --git a/src/backend/src/vst2_host/CMakeLists.txt b/src/backend/src/vst2_host/CMakeLists.txt index e4bab86..24421de 100644 --- a/src/backend/src/vst2_host/CMakeLists.txt +++ b/src/backend/src/vst2_host/CMakeLists.txt @@ -15,9 +15,12 @@ retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES) add_executable(${PROJECT_NAME} ${SRC_FILES}) target_link_libraries(${PROJECT_NAME} PRIVATE config_target - alicho_proto glfw AlichoMisc + Boost::interprocess + Boost::lockfree + Boost::asio + Boost::system ) target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) register_plugin_host(${PROJECT_NAME}) diff --git a/src/backend/src/vst2_host/src/main.cpp b/src/backend/src/vst2_host/src/main.cpp index 53722c3..dc83c3b 100644 --- a/src/backend/src/vst2_host/src/main.cpp +++ b/src/backend/src/vst2_host/src/main.cpp @@ -1,37 +1,69 @@ #include "GLFW/glfw3.h" #include +#include +#include "rpc/common.h" #include "vst2host.h" #include "spdlog/spdlog.h" -#include "boost/asio.hpp" +#include + +namespace bi = boost::interprocess; int main(int argc, char *argv[]) { - if (argc != 3) { + if (argc != 3) + return 1; + + std::string shm_input_path = argv[1]; + std::string shm_output_path = argv[2]; + + boost::system::error_code ec; + boost::asio::io_context io_context; + boost::asio::local::stream_protocol::socket socket(io_context); + socket.connect(get_uds_path(), ec); + if (ec) { + spdlog::error("无法连接到插件主机: {}", ec.message()); + return 1; + } + // 连接共享内存 + bi::managed_shared_memory input_segment(bi::open_only, shm_input_path.c_str()); + bi::managed_shared_memory output_segment(bi::open_only, shm_output_path.c_str()); + // 获取锁无阻塞队列 + auto input_queue = input_segment.find("input_queue").first; + auto output_queue = output_segment.find("output_queue").first; + if (!input_queue || !output_queue) { + spdlog::error("无法找到共享内存队列"); return 1; } glfwSetErrorCallback([](int error, const char* description) { - spdlog::error("GLFW Error {}: {}", error, description); + spdlog::error("GLFW 错误 {}: {}", error, description); }); glfwInit(); // 初始化 GLFW glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); // 仅使用 GLFW 的窗口功能,不使用 OpenGL - open_editor(); - while (true) { - if (is_editor_open()) { - glfwPollEvents(); - idle_editor(); - constexpr std::chrono::nanoseconds delta_time(std::nano::den / 320); // 240fps刷新率,但是留有额外的抖动余量(防止CPU时间片分配不均) - std::this_thread::sleep_for(delta_time); - } - else { - break; + // if (is_editor_open()) { + // glfwPollEvents(); + // idle_editor(); + // } + // else { + // break; + // } + // 处理音频块 + audio_block block; + if (input_queue->pop(block)) { + break; // 这里临时测试, 直接退出 } + + constexpr std::chrono::nanoseconds delta_time(std::nano::den / 320); // 240fps刷新率,但是留有额外的抖动余量(防止CPU时间片分配不均) + std::this_thread::sleep_for(delta_time); } unload_plugin(); glfwTerminate(); + + bi::shared_memory_object::remove(shm_input_path.c_str()); + bi::shared_memory_object::remove(shm_output_path.c_str()); return 0; } diff --git a/src/backend/src/vst2_host/src/vst2host.h b/src/backend/src/vst2_host/src/vst2host.h index 84a68a3..1d6bb29 100644 --- a/src/backend/src/vst2_host/src/vst2host.h +++ b/src/backend/src/vst2_host/src/vst2host.h @@ -2,10 +2,10 @@ #include "pluginterfaces/vst2.x/aeffectx.h" // Steinberg 官方头 #include -#include "library_handle.h" +#include "library_handle/library_handle.h" #include -#include "size_type.h" +#include "vec.h" inline library_handle* lib_ = nullptr; diff --git a/src/backend/src/vst3_host/CMakeLists.txt b/src/backend/src/vst3_host/CMakeLists.txt index 69204b6..e045af3 100644 --- a/src/backend/src/vst3_host/CMakeLists.txt +++ b/src/backend/src/vst3_host/CMakeLists.txt @@ -13,7 +13,7 @@ retrieve_files(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_FILES) add_executable(${PROJECT_NAME} ${SRC_FILES}) target_link_libraries(${PROJECT_NAME} PRIVATE config_target - alicho_proto + AlichoProto gRPC::grpc++ protobuf::libprotobuf libzmq diff --git a/src/backend/vcpkg.json b/src/backend/vcpkg.json index 7dfd3a8..8ef420e 100644 --- a/src/backend/vcpkg.json +++ b/src/backend/vcpkg.json @@ -1,13 +1,18 @@ { - "dependencies": [ - "grpc", - "protobuf", - "cppzmq", - "glfw3", - "spdlog", - "boost", - "gtest" - ], - "version": "0.0.1", - "name": "ninaengine" + "name" : "alicho", + "version" : "0.0.1", + "builtin-baseline" : "f33cc491c85a7d643c5ab6da1667c1458e6d7abf", + "dependencies" : [ "grpc", "protobuf", "cppzmq", "glfw3", "spdlog", "gtest", { + "name" : "boost-asio", + "version>=" : "1.88.0" + }, { + "name" : "boost-process", + "version>=" : "1.88.0" + }, { + "name" : "boost-interprocess", + "version>=" : "1.88.0" + }, { + "name" : "boost-lockfree", + "version>=" : "1.88.0" + } ] } \ No newline at end of file