todo rpc框架
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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) # 确保列表非空
|
||||
|
||||
@@ -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)
|
||||
|
||||
1
src/backend/src/engine/src/grpc/engine_grpc.cpp
Normal file
1
src/backend/src/engine/src/grpc/engine_grpc.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "engine_grpc.h"
|
||||
96
src/backend/src/engine/src/grpc/engine_grpc.h
Normal file
96
src/backend/src/engine/src/grpc/engine_grpc.h
Normal file
@@ -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;
|
||||
}
|
||||
};
|
||||
@@ -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 <filesystem>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#include "plugin_host_manager.h"
|
||||
|
||||
#include "plugin_instance.h"
|
||||
|
||||
plugin_host_manager::~plugin_host_manager() {
|
||||
std::vector<uint32_t> 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<plugin_instance>(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<rpc_session> 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;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <ranges>
|
||||
|
||||
#include "lazy_singleton.h"
|
||||
#include "plugin_instance.h"
|
||||
#include "rpc/rpc_session.h"
|
||||
|
||||
class plugin_host_manager : public lazy_singleton<plugin_host_manager> {
|
||||
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<rpc_session> session);
|
||||
|
||||
plugin_instance* get_plugin_instance(uint32_t plugin_id);
|
||||
|
||||
template<typename Func>
|
||||
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<uint32_t, std::unique_ptr<plugin_instance>> plugin_instances_;
|
||||
std::atomic_uint32_t plugin_id_ = 1;
|
||||
boost::asio::io_context io_context_;
|
||||
rpc_server rpc_server_{ io_context_ };
|
||||
};
|
||||
64
src/backend/src/engine/src/plugin_manage/plugin_instance.cpp
Normal file
64
src/backend/src/engine/src/plugin_manage/plugin_instance.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "plugin_instance.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
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<std::string, std::string> 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::managed_shared_memory>(bi::open_or_create, get_shm_in_name(id).c_str(), SHM_SIZE);
|
||||
output_segment = std::make_unique<bi::managed_shared_memory>(bi::open_or_create, get_shm_out_name(id).c_str(), SHM_SIZE);
|
||||
|
||||
// 获取锁无阻塞队列
|
||||
input_queue = input_segment->find_or_construct<lock_free_queue>("input_queue")();
|
||||
output_queue = output_segment->find_or_construct<lock_free_queue>("output_queue")();
|
||||
|
||||
process = std::shared_ptr<boost::process::process>(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());
|
||||
}
|
||||
}
|
||||
32
src/backend/src/engine/src/plugin_manage/plugin_instance.h
Normal file
32
src/backend/src/engine/src/plugin_manage/plugin_instance.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <boost/process.hpp>
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
|
||||
#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<boost::process::process> process; // 沙箱进程句柄
|
||||
|
||||
// 共享内存资源
|
||||
std::unique_ptr<boost::interprocess::managed_shared_memory> input_segment;
|
||||
std::unique_ptr<boost::interprocess::managed_shared_memory> output_segment;
|
||||
lock_free_queue* input_queue = nullptr; // AudioEngine -> Host
|
||||
lock_free_queue* output_queue = nullptr; // Host -> AudioEngine
|
||||
|
||||
// 控制连接
|
||||
// std::shared_ptr<rpc_session> session;
|
||||
|
||||
// 状态标识
|
||||
std::atomic_bool is_processing_done{ true }; // 插件是否处理完毕
|
||||
std::atomic_bool is_registered{ false }; // RPC会话是否注册
|
||||
|
||||
auto is_process_running() const {
|
||||
return process && process->running();
|
||||
}
|
||||
};
|
||||
10
src/backend/src/engine/src/rpc/engine_rpc.cpp
Normal file
10
src/backend/src/engine/src/rpc/engine_rpc.cpp
Normal file
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
8
src/backend/src/engine/src/rpc/engine_rpc.h
Normal file
8
src/backend/src/engine/src/rpc/engine_rpc.h
Normal file
@@ -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)
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
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<std::byte, MAX_PAYLOAD_SIZE> payload;
|
||||
};
|
||||
|
||||
// 根据 plugin_id 生成 IPC 资源名称
|
||||
inline std::string get_uds_path() {
|
||||
#if ALICHO_PLATFORM_WINDOWS
|
||||
return
|
||||
}
|
||||
12
src/backend/src/misc/src/lazy_singleton.h
Normal file
12
src/backend/src/misc/src/lazy_singleton.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
template<typename T>
|
||||
class lazy_singleton {
|
||||
public:
|
||||
static auto get_instance() -> T& {
|
||||
static T instance;
|
||||
return instance;
|
||||
}
|
||||
protected:
|
||||
lazy_singleton() = default;
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "library_handle.h"
|
||||
#include "library_handle/library_handle.h"
|
||||
#include <windows.h>
|
||||
|
||||
library_handle* library_handle::create(const std::filesystem::path& in_path) {
|
||||
34
src/backend/src/misc/src/rpc/common.h
Normal file
34
src/backend/src/misc/src/rpc/common.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <boost/lockfree/spsc_queue.hpp>
|
||||
|
||||
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<audio_block, boost::lockfree::capacity<QUEUE_CAPACITY>>;
|
||||
|
||||
// 根据 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);
|
||||
}
|
||||
0
src/backend/src/misc/src/rpc/rpc_manager.cpp
Normal file
0
src/backend/src/misc/src/rpc/rpc_manager.cpp
Normal file
47
src/backend/src/misc/src/rpc/rpc_manager.h
Normal file
47
src/backend/src/misc/src/rpc/rpc_manager.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "lazy_singleton.h"
|
||||
#include "rpc_type.h"
|
||||
#include "common.h"
|
||||
|
||||
using rpc_callback = std::function<void(uint64_t, const std::vector<std::byte>&)>;
|
||||
|
||||
class rpc_message_handler : public lazy_singleton<rpc_message_handler> {
|
||||
public:
|
||||
void register_rpc_handler(rpc::message_type type, rpc_callback handler) {
|
||||
if (handlers_.contains(type))
|
||||
spdlog::warn("RPC消息类型 {} 已经注册,覆盖旧处理器", static_cast<uint32_t>(type));
|
||||
handlers_[type] = std::move(handler);
|
||||
}
|
||||
|
||||
void handle_message(const rpc::rpc_header& in_header, const std::vector<std::byte>& 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<uint32_t>(in_header.type));
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::unordered_map<rpc::message_type, rpc_callback> handlers_;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct rpc_handler_register {
|
||||
explicit rpc_handler_register(rpc::message_type in_type, const std::function<void(uint64_t, const T&)>& in_func) {
|
||||
auto cast_func = [in_func](uint64_t in_seq_id, const std::vector<std::byte>& msg) {
|
||||
#if ALICHO_DEBUG
|
||||
assert(msg.size() == sizeof(T));
|
||||
#endif
|
||||
auto payload = reinterpret_cast<const T*>(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<body_type> __rpc_handler_register_##type(rpc::message_type::type, __rpc_handler_func_##type);
|
||||
|
||||
53
src/backend/src/misc/src/rpc/rpc_type.h
Normal file
53
src/backend/src/misc/src/rpc/rpc_type.h
Normal file
@@ -0,0 +1,53 @@
|
||||
// RPC调用消息体结构, 由AudioEngine和PluginHost之间传输
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
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<char, MAX_PAYLOAD_SIZE> 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 {
|
||||
|
||||
}
|
||||
41
src/backend/src/misc/src/rpc/session.cpp
Normal file
41
src/backend/src/misc/src/rpc/session.cpp
Normal file
@@ -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() {
|
||||
|
||||
}
|
||||
72
src/backend/src/misc/src/rpc/session.h
Normal file
72
src/backend/src/misc/src/rpc/session.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "common.h"
|
||||
#include "rpc_type.h"
|
||||
|
||||
namespace rpc {
|
||||
class session : public std::enable_shared_from_this<session> {
|
||||
public:
|
||||
explicit session(boost::asio::local::stream_protocol::socket&& in_socket)
|
||||
: socket_(std::move(in_socket)) {
|
||||
}
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
template<typename Msg>
|
||||
auto async_call(Msg&& msg) {
|
||||
// 这里可以实现异步调用逻辑
|
||||
// 比如将消息放入写队列,触发写操作等
|
||||
return boost::asio::post(socket_.get_executor(), [this, msg = std::forward<Msg>(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<std::byte> read_body_;
|
||||
|
||||
std::deque<std::vector<std::byte>> write_queue_; // 写队列, 用于发送消息
|
||||
std::atomic_uint32_t next_id_{ 1 };
|
||||
};
|
||||
|
||||
class server : public std::enable_shared_from_this<server> {
|
||||
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<session>(std::move(socket_));
|
||||
session_->start();
|
||||
}
|
||||
|
||||
template<typename Msg>
|
||||
auto async_call(Msg&& msg) {
|
||||
|
||||
}
|
||||
private:
|
||||
std::shared_ptr<session> session_;
|
||||
};
|
||||
}
|
||||
@@ -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})
|
||||
|
||||
@@ -1,37 +1,69 @@
|
||||
#include "GLFW/glfw3.h"
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "rpc/common.h"
|
||||
#include "vst2host.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "boost/asio.hpp"
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
|
||||
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<lock_free_queue>("input_queue").first;
|
||||
auto output_queue = output_segment.find<lock_free_queue>("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;
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#include "pluginterfaces/vst2.x/aeffectx.h" // Steinberg 官方头
|
||||
#include <span>
|
||||
|
||||
#include "library_handle.h"
|
||||
#include "library_handle/library_handle.h"
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "size_type.h"
|
||||
#include "vec.h"
|
||||
|
||||
|
||||
inline library_handle* lib_ = nullptr;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
} ]
|
||||
}
|
||||
Reference in New Issue
Block a user