1488 lines
45 KiB
Markdown
1488 lines
45 KiB
Markdown
# 插件开发指南
|
||
|
||
## 目录
|
||
|
||
- [插件开发指南](#插件开发指南)
|
||
- [目录](#目录)
|
||
- [概述](#概述)
|
||
- [插件架构](#插件架构)
|
||
- [沙盒隔离系统](#沙盒隔离系统)
|
||
- [插件接口](#插件接口)
|
||
- [安全通信](#安全通信)
|
||
- [开发插件](#开发插件)
|
||
- [插件生命周期](#插件生命周期)
|
||
- [插件API](#插件api)
|
||
- [音频处理](#音频处理)
|
||
- [参数管理](#参数管理)
|
||
- [状态管理](#状态管理)
|
||
- [预设支持](#预设支持)
|
||
- [GUI集成](#gui集成)
|
||
- [安全沙盒实践](#安全沙盒实践)
|
||
- [资源限制](#资源限制)
|
||
- [安全策略](#安全策略)
|
||
- [平台特定考虑](#平台特定考虑)
|
||
- [调试与优化](#调试与优化)
|
||
- [调试技术](#调试技术)
|
||
- [性能优化](#性能优化)
|
||
- [常见问题](#常见问题)
|
||
- [插件分发](#插件分发)
|
||
- [打包与安装](#打包与安装)
|
||
- [版本控制](#版本控制)
|
||
- [兼容性](#兼容性)
|
||
- [完整示例](#完整示例)
|
||
- [效果器插件](#效果器插件)
|
||
- [乐器插件](#乐器插件)
|
||
- [分析器插件](#分析器插件)
|
||
- [跨平台开发](#跨平台开发)
|
||
|
||
## 概述
|
||
|
||
音频后端系统的插件框架提供了高性能、安全和可靠的插件开发环境,允许第三方开发者创建音频处理插件、乐器插件和分析器插件,同时确保主应用程序的稳定性和安全性。
|
||
|
||
核心特性:
|
||
|
||
- **沙盒隔离**:每个插件在独立的进程中运行,崩溃不影响主应用
|
||
- **资源限制**:精确控制每个插件的CPU、内存和IO使用
|
||
- **安全通信**:加密通信和权限检查
|
||
- **标准化接口**:统一的插件API和生命周期
|
||
- **跨平台支持**:Windows、Linux和macOS平台一致的行为
|
||
|
||
插件架构概览:
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────────────┐
|
||
│ 主应用进程 │
|
||
│ │
|
||
│ ┌────────────────┐ ┌───────────────────────────────────┐ │
|
||
│ │ 音频引擎 │ │ 插件宿主管理器 │ │
|
||
│ │ │◄────►│ (PluginHostManager) │ │
|
||
│ └────────────────┘ └─────────────────┬─────────────────┘ │
|
||
└──────────────────────────────────────────────────────────────────┘
|
||
│
|
||
安全IPC通信 │
|
||
▼
|
||
┌──────────────────────────────────────────────────────────────────┐
|
||
│ 插件沙盒进程 │
|
||
│ │
|
||
│ ┌────────────────┐ ┌───────────────────────────────────┐ │
|
||
│ │ 资源监控 │ │ 插件实例 │ │
|
||
│ │ 安全控制 │◄────►│ (IPlugin) │ │
|
||
│ └────────────────┘ └───────────────────────────────────┘ │
|
||
│ │
|
||
└──────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
## 插件架构
|
||
|
||
### 沙盒隔离系统
|
||
|
||
沙盒隔离是插件系统的核心安全机制,每个插件实例在独立的进程中运行,通过安全通信通道与主应用程序交互:
|
||
|
||
1. **进程隔离**:
|
||
- 插件崩溃不会影响主应用和其他插件
|
||
- 插件资源完全独立,不会导致主应用内存泄漏
|
||
|
||
2. **平台特定实现**:
|
||
- Windows:使用Job Objects和进程完整性级别
|
||
- Linux:使用Namespaces、Seccomp和Cgroups
|
||
- macOS:使用Sandbox容器和XPC通信
|
||
|
||
3. **资源限制**:
|
||
- CPU使用率限制
|
||
- 内存使用上限
|
||
- 文件句柄数量控制
|
||
- 线程数量限制
|
||
|
||
4. **安全限制**:
|
||
- 文件系统访问控制
|
||
- 网络访问控制
|
||
- 系统调用限制
|
||
- 地址空间随机化
|
||
|
||
沙盒类型:
|
||
|
||
```cpp
|
||
enum class SandboxType {
|
||
None, // 无沙盒(不推荐用于生产环境)
|
||
ProcessIsolation, // 仅进程隔离,无资源限制
|
||
LightSandbox, // 轻量级沙盒,基本资源限制
|
||
StandardSandbox, // 标准沙盒,适用于大多数插件
|
||
StrictSandbox // 严格沙盒,用于不受信任的插件
|
||
};
|
||
```
|
||
|
||
### 插件接口
|
||
|
||
所有插件必须实现`IPlugin`接口,该接口定义了插件的生命周期、功能和行为:
|
||
|
||
```cpp
|
||
class IPlugin {
|
||
public:
|
||
virtual ~IPlugin() = default;
|
||
|
||
// 基本信息
|
||
virtual std::unique_ptr<PluginInfo> get_plugin_info() const = 0;
|
||
virtual PluginId get_plugin_id() const = 0;
|
||
virtual std::string get_name() const = 0;
|
||
virtual std::string get_vendor() const = 0;
|
||
virtual Version get_version() const = 0;
|
||
virtual PluginCategory get_category() const = 0;
|
||
virtual PluginType get_type() const = 0;
|
||
virtual PluginCapability get_capabilities() const = 0;
|
||
|
||
// 生命周期管理
|
||
virtual common::ErrorCode initialize(const engine::AudioConfig& config) = 0;
|
||
virtual common::ErrorCode shutdown() = 0;
|
||
virtual common::ErrorCode activate() = 0;
|
||
virtual common::ErrorCode deactivate() = 0;
|
||
virtual common::ErrorCode suspend() = 0;
|
||
virtual common::ErrorCode resume() = 0;
|
||
virtual common::ErrorCode reset() = 0;
|
||
virtual PluginState get_state() const = 0;
|
||
|
||
// 音频处理
|
||
virtual common::ErrorCode prepare_to_play(double sample_rate, uint32_t max_block_size) = 0;
|
||
virtual ProcessingResult process_audio(
|
||
const engine::AudioBuffer& input,
|
||
engine::AudioBuffer& output,
|
||
const std::vector<MidiEvent>& midi_in,
|
||
std::vector<MidiEvent>& midi_out,
|
||
const PluginProcessContext& context) = 0;
|
||
|
||
// 参数管理
|
||
virtual size_t get_parameter_count() const = 0;
|
||
virtual std::unique_ptr<PluginParameter> get_parameter_info(size_t index) const = 0;
|
||
virtual common::ErrorCode set_parameter(const std::string& parameter_id, const std::any& value) = 0;
|
||
virtual std::any get_parameter(const std::string& parameter_id) const = 0;
|
||
|
||
// 更多接口方法...
|
||
};
|
||
```
|
||
|
||
插件类型:
|
||
|
||
```cpp
|
||
enum class PluginType {
|
||
Unknown,
|
||
Effect, // 音频效果器
|
||
Instrument, // 虚拟乐器
|
||
Analyzer, // 分析器
|
||
Utility // 工具类插件
|
||
};
|
||
```
|
||
|
||
### 安全通信
|
||
|
||
插件与主应用之间的通信采用安全设计:
|
||
|
||
1. **消息序列化**:
|
||
- 使用Protobuf序列化消息
|
||
- 严格的消息验证和类型检查
|
||
|
||
2. **安全通道**:
|
||
- 进程间共享内存用于高性能音频数据传输
|
||
- 控制消息通过加密通道传输
|
||
|
||
3. **权限检查**:
|
||
- 消息发送方身份验证
|
||
- 操作权限验证
|
||
- 资源使用审计
|
||
|
||
4. **故障恢复**:
|
||
- 通信超时检测
|
||
- 自动重新连接
|
||
- 状态同步机制
|
||
|
||
## 开发插件
|
||
|
||
### 插件生命周期
|
||
|
||
插件的生命周期由以下状态转换定义:
|
||
|
||
```
|
||
┌──────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
|
||
│ 创建 │─────►│ 初始化 │─────►│ 激活 │─────►│ 处理 │
|
||
└──────────┘ └──────────────┘ └──────────────┘ └──────────────┘
|
||
│ ▲ │
|
||
│ │ │
|
||
│ │ ▼
|
||
│ │ ┌──────────────┐
|
||
│ ┌─────┴─────┐ │ 暂停 │
|
||
▼ │ 恢复 │◄────────┘──────────────┘
|
||
┌──────────────┐ └─────┬─────┘
|
||
│ 停用 │◄─────────── ┘
|
||
└──────────────┘
|
||
│
|
||
▼
|
||
┌──────────────┐
|
||
│ 关闭 │
|
||
└──────────────┘
|
||
```
|
||
|
||
实现插件时需遵循以下生命周期规则:
|
||
|
||
1. **构造函数**:
|
||
- 只应进行最小化初始化
|
||
- 不应分配大量资源
|
||
- 不应访问文件或网络
|
||
|
||
2. **初始化**:
|
||
- 分配基本资源
|
||
- 检查配置有效性
|
||
- 准备插件工作
|
||
|
||
3. **激活**:
|
||
- 分配音频处理资源
|
||
- 初始化DSP状态
|
||
- 启动处理线程(如果有)
|
||
|
||
4. **处理**:
|
||
- 高性能实时音频处理
|
||
- 保持处理线程不受阻塞
|
||
- 不应有阻塞操作
|
||
|
||
5. **停用**:
|
||
- 释放音频处理资源
|
||
- 停止处理线程
|
||
- 保留基本状态
|
||
|
||
6. **关闭**:
|
||
- 释放所有资源
|
||
- 保存必要的状态
|
||
- 准备销毁
|
||
|
||
### 插件API
|
||
|
||
开发插件时,需要实现核心API以支持各种功能:
|
||
|
||
```cpp
|
||
// 示例:基础插件类
|
||
class MyPlugin : public IPlugin {
|
||
private:
|
||
PluginInfo info_;
|
||
PluginState state_ = PluginState::Uninitialized;
|
||
engine::AudioConfig config_;
|
||
std::map<std::string, std::any> parameters_;
|
||
|
||
// 音频处理状态
|
||
std::vector<float> delay_buffer_;
|
||
|
||
public:
|
||
MyPlugin() {
|
||
// 初始化插件信息
|
||
info_.plugin_id = "com.example.my-plugin";
|
||
info_.plugin_name = "我的示例插件";
|
||
info_.vendor_name = "示例公司";
|
||
info_.plugin_version = Version(1, 0, 0);
|
||
info_.category = PluginCategory::Delay;
|
||
info_.type = PluginType::Effect;
|
||
|
||
// 初始化参数
|
||
parameters_["dry"] = 0.5f;
|
||
parameters_["wet"] = 0.5f;
|
||
parameters_["delay"] = 0.5f;
|
||
parameters_["feedback"] = 0.3f;
|
||
}
|
||
|
||
// 基本信息方法实现
|
||
std::unique_ptr<PluginInfo> get_plugin_info() const override {
|
||
return std::make_unique<PluginInfo>(info_);
|
||
}
|
||
|
||
PluginId get_plugin_id() const override { return info_.plugin_id; }
|
||
std::string get_name() const override { return info_.plugin_name; }
|
||
std::string get_vendor() const override { return info_.vendor_name; }
|
||
Version get_version() const override { return info_.plugin_version; }
|
||
PluginCategory get_category() const override { return info_.category; }
|
||
PluginType get_type() const override { return info_.type; }
|
||
|
||
// 生命周期方法实现
|
||
common::ErrorCode initialize(const engine::AudioConfig& config) override {
|
||
if (state_ != PluginState::Uninitialized) {
|
||
return common::ErrorCode::ALREADY_INITIALIZED;
|
||
}
|
||
|
||
config_ = config;
|
||
// 初始化资源...
|
||
|
||
state_ = PluginState::Initialized;
|
||
return common::ErrorCode::SUCCESS;
|
||
}
|
||
|
||
// 其他方法实现...
|
||
};
|
||
|
||
// 插件工厂(用于创建插件实例)
|
||
extern "C" {
|
||
PLUGIN_API IPlugin* create_plugin() {
|
||
return new MyPlugin();
|
||
}
|
||
|
||
PLUGIN_API void delete_plugin(IPlugin* plugin) {
|
||
delete plugin;
|
||
}
|
||
|
||
PLUGIN_API uint32_t get_plugin_api_version() {
|
||
return PLUGIN_API_VERSION;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 音频处理
|
||
|
||
插件的核心功能是音频处理,通过`process_audio`方法实现:
|
||
|
||
```cpp
|
||
ProcessingResult MyPlugin::process_audio(
|
||
const engine::AudioBuffer& input,
|
||
engine::AudioBuffer& output,
|
||
const std::vector<MidiEvent>& midi_in,
|
||
std::vector<MidiEvent>& midi_out,
|
||
const PluginProcessContext& context) {
|
||
|
||
// 确保输出缓冲区正确分配
|
||
if (output.frames() != input.frames() ||
|
||
output.channels() != input.channels()) {
|
||
output.allocate(input.frames(), input.channels(), input.format());
|
||
}
|
||
|
||
// 获取处理参数
|
||
float dry_gain = std::any_cast<float>(parameters_["dry"]);
|
||
float wet_gain = std::any_cast<float>(parameters_["wet"]);
|
||
float delay_time = std::any_cast<float>(parameters_["delay"]);
|
||
float feedback = std::any_cast<float>(parameters_["feedback"]);
|
||
|
||
// 计算延迟样本数
|
||
int delay_samples = static_cast<int>(delay_time * context.sample_rate);
|
||
|
||
// 调整延迟缓冲区大小
|
||
if (delay_buffer_.size() < delay_samples * input.channels()) {
|
||
delay_buffer_.resize(delay_samples * input.channels(), 0.0f);
|
||
}
|
||
|
||
// 对每个声道进行处理
|
||
for (uint16_t ch = 0; ch < input.channels(); ++ch) {
|
||
const float* in_data = input.channel_data<float>(ch);
|
||
float* out_data = output.channel_data<float>(ch);
|
||
|
||
for (uint32_t i = 0; i < input.frames(); ++i) {
|
||
// 获取延迟样本
|
||
int read_index = (delay_write_pos_ - delay_samples + delay_buffer_.size()) %
|
||
delay_buffer_.size();
|
||
float delayed_sample = delay_buffer_[read_index * input.channels() + ch];
|
||
|
||
// 计算输出(干信号 + 湿信号)
|
||
out_data[i] = in_data[i] * dry_gain + delayed_sample * wet_gain;
|
||
|
||
// 更新延迟缓冲区(输入 + 反馈)
|
||
delay_buffer_[delay_write_pos_ * input.channels() + ch] =
|
||
in_data[i] + delayed_sample * feedback;
|
||
}
|
||
}
|
||
|
||
// 更新写入位置
|
||
delay_write_pos_ = (delay_write_pos_ + 1) % delay_buffer_.size();
|
||
|
||
// 返回处理结果
|
||
return ProcessingResult::Success;
|
||
}
|
||
```
|
||
|
||
实时音频处理的关键考虑:
|
||
|
||
1. **性能优化**:
|
||
- 避免动态内存分配
|
||
- 避免锁和同步
|
||
- 使用SIMD指令加速处理
|
||
|
||
2. **线程安全**:
|
||
- 参数更新采用无锁设计
|
||
- 避免共享资源竞争
|
||
- 控制并发访问
|
||
|
||
3. **延迟报告**:
|
||
- 准确报告插件引入的延迟
|
||
- 提供零延迟选项(如果可能)
|
||
|
||
4. **缓冲区管理**:
|
||
- 高效的内存使用
|
||
- 避免不必要的复制
|
||
- 支持不同的缓冲区格式
|
||
|
||
### 参数管理
|
||
|
||
插件参数管理是提供用户控制的关键机制:
|
||
|
||
```cpp
|
||
// 参数类型
|
||
enum class ParameterType {
|
||
Float, // 浮点数
|
||
Integer, // 整数
|
||
Boolean, // 布尔值
|
||
Enum, // 枚举
|
||
String // 字符串
|
||
};
|
||
|
||
// 参数信息
|
||
struct ParameterInfo {
|
||
std::string id; // 唯一标识符
|
||
std::string name; // 显示名称
|
||
std::string unit; // 单位
|
||
ParameterType type; // 类型
|
||
bool automatable; // 是否可自动化
|
||
bool meta_parameter; // 是否为元参数
|
||
|
||
// 数值范围(对数值类型)
|
||
double min_value;
|
||
double max_value;
|
||
double default_value;
|
||
|
||
// 枚举值(对枚举类型)
|
||
std::vector<std::string> enum_values;
|
||
|
||
// 显示格式化
|
||
std::string to_string(const std::any& value) const;
|
||
std::any from_string(const std::string& str) const;
|
||
};
|
||
|
||
// 参数更新方法
|
||
common::ErrorCode MyPlugin::set_parameter(const std::string& parameter_id, const std::any& value) {
|
||
// 查找参数
|
||
if (parameters_.find(parameter_id) == parameters_.end()) {
|
||
return common::ErrorCode::INVALID_ARGUMENT;
|
||
}
|
||
|
||
// 类型检查
|
||
try {
|
||
if (parameter_id == "dry" || parameter_id == "wet" ||
|
||
parameter_id == "delay" || parameter_id == "feedback") {
|
||
// 尝试转换为float
|
||
std::any_cast<float>(value);
|
||
} else if (parameter_id == "bypass") {
|
||
// 尝试转换为bool
|
||
std::any_cast<bool>(value);
|
||
}
|
||
} catch (const std::bad_any_cast&) {
|
||
return common::ErrorCode::INVALID_ARGUMENT;
|
||
}
|
||
|
||
// 值范围检查
|
||
if (parameter_id == "dry" || parameter_id == "wet") {
|
||
float val = std::any_cast<float>(value);
|
||
if (val < 0.0f || val > 1.0f) {
|
||
return common::ErrorCode::INVALID_ARGUMENT;
|
||
}
|
||
} else if (parameter_id == "delay") {
|
||
float val = std::any_cast<float>(value);
|
||
if (val < 0.01f || val > 2.0f) {
|
||
return common::ErrorCode::INVALID_ARGUMENT;
|
||
}
|
||
} else if (parameter_id == "feedback") {
|
||
float val = std::any_cast<float>(value);
|
||
if (val < 0.0f || val > 0.99f) {
|
||
return common::ErrorCode::INVALID_ARGUMENT;
|
||
}
|
||
}
|
||
|
||
// 更新参数值(线程安全方式)
|
||
parameters_[parameter_id] = value;
|
||
|
||
// 如果有监听器,通知参数变化
|
||
if (event_listener_) {
|
||
event_listener_->on_parameter_changed(parameter_id, value);
|
||
}
|
||
|
||
return common::ErrorCode::SUCCESS;
|
||
}
|
||
```
|
||
|
||
参数管理最佳实践:
|
||
|
||
1. **参数平滑**:
|
||
- 使用参数平滑避免音频噪音
|
||
- 实现值插值机制
|
||
|
||
2. **原子更新**:
|
||
- 使用无锁技术更新参数
|
||
- 避免参数更新时的竞态条件
|
||
|
||
3. **参数组织**:
|
||
- 使用参数分组提高用户体验
|
||
- 提供参数依赖关系
|
||
|
||
### 状态管理
|
||
|
||
插件需要保存和恢复其状态,以支持项目保存和加载功能:
|
||
|
||
```cpp
|
||
// 获取插件状态
|
||
std::vector<uint8_t> MyPlugin::get_state_data() const {
|
||
// 使用二进制序列化或JSON等格式保存状态
|
||
nlohmann::json state;
|
||
|
||
// 保存参数
|
||
for (const auto& [key, value] : parameters_) {
|
||
if (std::type_index(value.type()) == std::type_index(typeid(float))) {
|
||
state["parameters"][key] = std::any_cast<float>(value);
|
||
} else if (std::type_index(value.type()) == std::type_index(typeid(bool))) {
|
||
state["parameters"][key] = std::any_cast<bool>(value);
|
||
} else if (std::type_index(value.type()) == std::type_index(typeid(int))) {
|
||
state["parameters"][key] = std::any_cast<int>(value);
|
||
} else if (std::type_index(value.type()) == std::type_index(typeid(std::string))) {
|
||
state["parameters"][key] = std::any_cast<std::string>(value);
|
||
}
|
||
}
|
||
|
||
// 保存其他状态...
|
||
state["current_preset_id"] = current_preset_id_;
|
||
|
||
// 序列化为二进制
|
||
std::string json_str = state.dump();
|
||
return std::vector<uint8_t>(json_str.begin(), json_str.end());
|
||
}
|
||
|
||
// 设置插件状态
|
||
common::ErrorCode MyPlugin::set_state_data(const std::vector<uint8_t>& data) {
|
||
try {
|
||
// 解析JSON
|
||
std::string json_str(data.begin(), data.end());
|
||
auto state = nlohmann::json::parse(json_str);
|
||
|
||
// 恢复参数
|
||
if (state.contains("parameters")) {
|
||
for (auto& [key, value] : state["parameters"].items()) {
|
||
if (parameters_.find(key) != parameters_.end()) {
|
||
if (value.is_number_float()) {
|
||
parameters_[key] = value.get<float>();
|
||
} else if (value.is_boolean()) {
|
||
parameters_[key] = value.get<bool>();
|
||
} else if (value.is_number_integer()) {
|
||
parameters_[key] = value.get<int>();
|
||
} else if (value.is_string()) {
|
||
parameters_[key] = value.get<std::string>();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 恢复其他状态...
|
||
if (state.contains("current_preset_id")) {
|
||
current_preset_id_ = state["current_preset_id"];
|
||
}
|
||
|
||
return common::ErrorCode::SUCCESS;
|
||
} catch (const std::exception& e) {
|
||
return common::ErrorCode::INVALID_ARGUMENT;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 预设支持
|
||
|
||
插件预设管理允许保存和加载常用配置:
|
||
|
||
```cpp
|
||
// 加载预设
|
||
common::ErrorCode MyPlugin::load_preset(const std::string& preset_id) {
|
||
// 查找预设
|
||
auto it = presets_.find(preset_id);
|
||
if (it == presets_.end()) {
|
||
return common::ErrorCode::INVALID_ARGUMENT;
|
||
}
|
||
|
||
// 应用预设参数
|
||
const auto& preset = it->second;
|
||
for (const auto& [key, value] : preset.parameters) {
|
||
parameters_[key] = value;
|
||
}
|
||
|
||
current_preset_id_ = preset_id;
|
||
|
||
// 通知预设加载
|
||
if (event_listener_) {
|
||
event_listener_->on_preset_loaded(preset_id);
|
||
}
|
||
|
||
return common::ErrorCode::SUCCESS;
|
||
}
|
||
|
||
// 保存预设
|
||
common::ErrorCode MyPlugin::save_preset(const std::string& preset_id, const std::string& name) {
|
||
// 创建新预设
|
||
PluginPreset preset;
|
||
preset.id = preset_id;
|
||
preset.name = name;
|
||
preset.parameters = parameters_;
|
||
|
||
// 保存预设
|
||
presets_[preset_id] = preset;
|
||
current_preset_id_ = preset_id;
|
||
|
||
// 通知预设保存
|
||
if (event_listener_) {
|
||
event_listener_->on_preset_saved(preset_id);
|
||
}
|
||
|
||
return common::ErrorCode::SUCCESS;
|
||
}
|
||
```
|
||
|
||
### GUI集成
|
||
|
||
插件可以提供自定义GUI界面进行交互:
|
||
|
||
```cpp
|
||
// 创建GUI
|
||
void* MyPlugin::create_gui(void* parent_window) {
|
||
if (gui_) {
|
||
return gui_->get_native_window();
|
||
}
|
||
|
||
// 创建GUI实例
|
||
gui_ = std::make_unique<MyPluginGUI>(this, parent_window);
|
||
|
||
// 设置回调
|
||
gui_->set_parameter_change_callback([this](const std::string& id, const std::any& value) {
|
||
this->set_parameter(id, value);
|
||
});
|
||
|
||
return gui_->get_native_window();
|
||
}
|
||
|
||
// 销毁GUI
|
||
common::ErrorCode MyPlugin::destroy_gui() {
|
||
if (!gui_) {
|
||
return common::ErrorCode::SUCCESS;
|
||
}
|
||
|
||
// 关闭并销毁GUI
|
||
gui_->close();
|
||
gui_.reset();
|
||
|
||
return common::ErrorCode::SUCCESS;
|
||
}
|
||
```
|
||
|
||
GUI最佳实践:
|
||
|
||
1. **参数绑定**:
|
||
- 使用数据绑定保持UI和参数同步
|
||
- 支持双向更新
|
||
|
||
2. **高性能绘制**:
|
||
- 避免频繁重绘
|
||
- 使用硬件加速
|
||
- 降低资源消耗
|
||
|
||
3. **适应性设计**:
|
||
- 支持大小调整
|
||
- 考虑高DPI显示
|
||
- 支持不同主题
|
||
|
||
## 安全沙盒实践
|
||
|
||
### 资源限制
|
||
|
||
合理的资源限制对于插件性能和主应用稳定性至关重要:
|
||
|
||
```cpp
|
||
// 音频效果器的推荐资源限制
|
||
SandboxConfig create_audio_effect_sandbox_config() {
|
||
SandboxConfig config;
|
||
|
||
// 资源限制
|
||
config.limits.max_memory_bytes = 256 * 1024 * 1024; // 256MB
|
||
config.limits.max_cpu_percent = 15.0; // 15% CPU
|
||
config.limits.max_threads = 4; // 4个线程
|
||
config.limits.max_file_handles = 32; // 32个文件句柄
|
||
config.limits.max_network_connections = 0; // 禁止网络
|
||
config.limits.max_processing_time_ms = 5; // 5ms处理时间
|
||
|
||
// 安全设置
|
||
config.security.allow_file_system_access = true; // 允许文件访问
|
||
config.security.allowed_paths = { // 允许的路径
|
||
"${PLUGIN_DIR}/resources",
|
||
"${PLUGIN_DIR}/presets",
|
||
"${USER_DATA_DIR}/${PLUGIN_ID}"
|
||
};
|
||
config.security.allow_network_access = false; // 禁止网络访问
|
||
config.security.enable_address_randomization = true; // 地址随机化
|
||
|
||
return config;
|
||
}
|
||
|
||
// 乐器插件的推荐资源限制
|
||
SandboxConfig create_instrument_sandbox_config() {
|
||
SandboxConfig config;
|
||
|
||
// 乐器需要更多资源
|
||
config.limits.max_memory_bytes = 1024 * 1024 * 1024; // 1GB
|
||
config.limits.max_cpu_percent = 25.0; // 25% CPU
|
||
config.limits.max_threads = 8; // 8个线程
|
||
config.limits.max_file_handles = 64; // 64个文件句柄
|
||
config.limits.max_processing_time_ms = 10; // 10ms处理时间
|
||
|
||
// 其他配置...
|
||
|
||
return config;
|
||
}
|
||
|
||
// 分析器插件的推荐资源限制
|
||
SandboxConfig create_analyzer_sandbox_config() {
|
||
SandboxConfig config;
|
||
|
||
// 分析器通常需要更少资源
|
||
config.limits.max_memory_bytes = 128 * 1024 * 1024; // 128MB
|
||
config.limits.max_cpu_percent = 10.0; // 10% CPU
|
||
config.limits.max_threads = 2; // 2个线程
|
||
|
||
// 其他配置...
|
||
|
||
return config;
|
||
}
|
||
```
|
||
|
||
资源监控与管理建议:
|
||
|
||
1. **性能自我监控**:
|
||
- 监控自身CPU和内存使用
|
||
- 在达到限制之前优化
|
||
|
||
2. **资源自适应**:
|
||
- 根据可用资源调整质量
|
||
- 提供不同的性能模式
|
||
|
||
### 安全策略
|
||
|
||
插件开发需遵循安全最佳实践:
|
||
|
||
1. **最小权限原则**:
|
||
- 只请求必要的资源和权限
|
||
- 明确声明资源需求
|
||
|
||
2. **安全文件访问**:
|
||
- 只在允许目录中读写文件
|
||
- 验证所有文件路径
|
||
- 使用安全的文件访问API
|
||
|
||
3. **输入验证**:
|
||
- 验证所有来自宿主的输入
|
||
- 检查缓冲区边界
|
||
- 防止恶意输入
|
||
|
||
4. **错误处理**:
|
||
- 优雅处理所有错误
|
||
- 避免崩溃和泄露
|
||
- 提供有用的错误信息
|
||
|
||
安全策略配置示例:
|
||
|
||
```cpp
|
||
// 严格安全配置
|
||
SecuritySettings strict_security;
|
||
strict_security.allow_file_system_access = false;
|
||
strict_security.allow_network_access = false;
|
||
strict_security.enable_address_randomization = true;
|
||
strict_security.enable_data_execution_prevention = true;
|
||
strict_security.enable_control_flow_guard = true;
|
||
|
||
// 中等安全配置(仅限资源访问)
|
||
SecuritySettings moderate_security;
|
||
moderate_security.allow_file_system_access = true;
|
||
moderate_security.allowed_paths = {
|
||
"${PLUGIN_DIR}/resources",
|
||
"${USER_DATA_DIR}/${PLUGIN_ID}"
|
||
};
|
||
moderate_security.allow_network_access = false;
|
||
moderate_security.enable_address_randomization = true;
|
||
|
||
// 最低安全配置(开发模式)
|
||
SecuritySettings dev_security;
|
||
dev_security.allow_file_system_access = true;
|
||
dev_security.allow_network_access = true;
|
||
dev_security.enable_address_randomization = false;
|
||
dev_security.enable_debugging = true;
|
||
```
|
||
|
||
### 平台特定考虑
|
||
|
||
不同平台上的沙盒实现有所差异:
|
||
|
||
**Windows平台**:
|
||
```cpp
|
||
#ifdef _WIN32
|
||
// 检查Job对象支持
|
||
if (WindowsSandboxFactory::supports_job_objects()) {
|
||
// 使用Job对象实现资源限制
|
||
}
|
||
|
||
// 使用Windows安全描述符
|
||
SecurityAttributes security_attrs;
|
||
security_attrs.integrity_level = IntegrityLevel::Low;
|
||
security_attrs.capabilities = { L"createFileReadData" };
|
||
#endif
|
||
```
|
||
|
||
**Linux平台**:
|
||
```cpp
|
||
#ifdef __linux__
|
||
// 使用Namespaces隔离
|
||
if (LinuxSandboxFactory::supports_namespaces()) {
|
||
// 配置namespace隔离
|
||
LinuxSandboxConfig linux_config;
|
||
linux_config.use_mount_ns = true;
|
||
linux_config.use_pid_ns = true;
|
||
linux_config.use_net_ns = true;
|
||
}
|
||
|
||
// 使用Cgroups限制资源
|
||
if (LinuxSandboxFactory::supports_cgroups()) {
|
||
// 配置cgroup限制
|
||
}
|
||
|
||
// 使用Seccomp限制系统调用
|
||
if (LinuxSandboxFactory::supports_seccomp()) {
|
||
// 配置seccomp过滤器
|
||
}
|
||
#endif
|
||
```
|
||
|
||
**macOS平台**:
|
||
```cpp
|
||
#ifdef __APPLE__
|
||
// 使用macOS沙盒
|
||
if (MacOSSandboxFactory::supports_sandbox()) {
|
||
MacOSSandboxProfile profile;
|
||
profile.add_rule("(allow file-read* (subpath \"${PLUGIN_DIR}/Resources\"))");
|
||
profile.add_rule("(deny network*)");
|
||
}
|
||
|
||
// 使用XPC通信
|
||
if (MacOSSandboxFactory::supports_xpc()) {
|
||
// 配置XPC通信
|
||
}
|
||
#endif
|
||
```
|
||
|
||
## 调试与优化
|
||
|
||
### 调试技术
|
||
|
||
调试沙盒化插件需要特殊技术:
|
||
|
||
1. **日志调试**:
|
||
- 使用宿主提供的日志系统
|
||
- 在开发模式下启用详细日志
|
||
- 为不同模块使用不同日志级别
|
||
|
||
```cpp
|
||
// 使用宿主日志系统
|
||
common::log_debug("MyPlugin: 初始化参数,延迟时间={}", delay_time);
|
||
common::log_info("MyPlugin: 完成初始化");
|
||
```
|
||
|
||
2. **调试版本**:
|
||
- 创建特殊调试版本的插件
|
||
- 在调试版本中添加调试辅助功能
|
||
|
||
```cpp
|
||
#ifdef DEBUG_BUILD
|
||
// 调试辅助方法
|
||
void MyPlugin::dump_state() {
|
||
common::log_debug("=== 插件状态转储 ===");
|
||
for (const auto& [key, value] : parameters_) {
|
||
common::log_debug("参数: {} = {}", key, parameter_to_string(key, value));
|
||
}
|
||
common::log_debug("当前预设: {}", current_preset_id_);
|
||
common::log_debug("音频缓冲区: {}帧, {}声道", config_.frames_per_buffer, config_.channels);
|
||
}
|
||
#endif
|
||
```
|
||
|
||
3. **沙盒外调试**:
|
||
- 提供在沙盒外运行的选项
|
||
- 便于调试器连接
|
||
|
||
```cpp
|
||
#ifdef SANDBOX_DEBUG
|
||
// 在沙盒外运行
|
||
SandboxConfig debug_config;
|
||
debug_config.sandbox_type = SandboxType::None;
|
||
debug_config.security.enable_debugging = true;
|
||
#endif
|
||
```
|
||
|
||
### 性能优化
|
||
|
||
插件性能优化关键点:
|
||
|
||
1. **高效内存管理**:
|
||
- 避免音频处理中分配内存
|
||
- 使用内存池和预分配
|
||
- 注意数据局部性
|
||
|
||
```cpp
|
||
// 使用预分配的内存池
|
||
class MemoryPool {
|
||
public:
|
||
MemoryPool(size_t block_size, size_t num_blocks)
|
||
: block_size_(block_size), num_blocks_(num_blocks) {
|
||
pool_memory_.resize(block_size * num_blocks);
|
||
for (size_t i = 0; i < num_blocks; ++i) {
|
||
free_blocks_.push_back(&pool_memory_[i * block_size]);
|
||
}
|
||
}
|
||
|
||
void* allocate() {
|
||
if (free_blocks_.empty()) {
|
||
return nullptr;
|
||
}
|
||
|
||
void* block = free_blocks_.back();
|
||
free_blocks_.pop_back();
|
||
return block;
|
||
}
|
||
|
||
void deallocate(void* ptr) {
|
||
free_blocks_.push_back(ptr);
|
||
}
|
||
|
||
private:
|
||
size_t block_size_;
|
||
size_t num_blocks_;
|
||
std::vector<uint8_t> pool_memory_;
|
||
std::vector<void*> free_blocks_;
|
||
};
|
||
|
||
// 在插件中使用
|
||
MemoryPool memory_pool(1024, 128); // 128个1KB的块
|
||
```
|
||
|
||
2. **SIMD优化**:
|
||
- 利用SIMD指令加速音频处理
|
||
- 注意跨平台SIMD差异
|
||
|
||
```cpp
|
||
// 使用SIMD优化处理
|
||
void MyPlugin::apply_gain_simd(float* buffer, size_t size, float gain) {
|
||
// 调用SIMD优化的增益函数
|
||
simd::CALL_SIMD_AUDIO_FUNCTION(apply_gain_f32, buffer, gain, buffer, size);
|
||
}
|
||
```
|
||
|
||
3. **并行处理**:
|
||
- 在适当情况下使用多线程
|
||
- 避免线程同步开销
|
||
|
||
```cpp
|
||
// 多声道并行处理
|
||
void MyPlugin::process_multi_threaded(const engine::AudioBuffer& input, engine::AudioBuffer& output) {
|
||
// 为每个声道启动一个线程
|
||
std::vector<std::thread> threads;
|
||
for (uint16_t ch = 0; ch < input.channels(); ++ch) {
|
||
threads.emplace_back([this, &input, &output, ch]() {
|
||
this->process_channel(input, output, ch);
|
||
});
|
||
}
|
||
|
||
// 等待所有线程完成
|
||
for (auto& thread : threads) {
|
||
thread.join();
|
||
}
|
||
}
|
||
```
|
||
|
||
### 常见问题
|
||
|
||
开发插件时的常见问题及解决方法:
|
||
|
||
1. **加载失败**:
|
||
- 确保符合API版本要求
|
||
- 检查所有依赖项
|
||
- 验证权限和资源访问
|
||
|
||
2. **音频噪声**:
|
||
- 检查参数平滑
|
||
- 防止缓冲区溢出
|
||
- 避免不连续的信号处理
|
||
|
||
3. **性能问题**:
|
||
- 使用性能分析工具
|
||
- 减少处理线程中的分配
|
||
- 优化算法复杂度
|
||
|
||
4. **崩溃恢复**:
|
||
- 实现正确的异常处理
|
||
- 提供崩溃后自恢复机制
|
||
- 保存中间状态
|
||
|
||
## 插件分发
|
||
|
||
### 打包与安装
|
||
|
||
插件打包最佳实践:
|
||
|
||
1. **文件结构**:
|
||
```
|
||
MyPlugin/
|
||
├── MyPlugin.dll # 主插件库
|
||
├── MyPlugin.pdb # 调试符号(仅开发版)
|
||
├── resources/ # 资源文件目录
|
||
│ ├── presets/ # 预设目录
|
||
│ ├── samples/ # 样本目录(如适用)
|
||
│ └── images/ # 图片资源
|
||
└── README.md # 使用说明
|
||
```
|
||
|
||
2. **资源管理**:
|
||
- 使用相对路径访问资源
|
||
- 验证资源完整性
|
||
- 提供资源缺失的错误处理
|
||
|
||
3. **版本兼容性**:
|
||
- 明确声明支持的宿主版本
|
||
- 提供向后兼容性
|
||
- 正确处理版本升级
|
||
|
||
### 版本控制
|
||
|
||
插件版本控制建议:
|
||
|
||
1. **语义化版本**:
|
||
- 主版本:不兼容的API变更
|
||
- 次版本:向后兼容的功能新增
|
||
- 修订版本:向后兼容的问题修复
|
||
|
||
2. **版本检查**:
|
||
- 插件启动时检查API兼容性
|
||
- 提供版本不匹配的错误信息
|
||
|
||
3. **升级路径**:
|
||
- 提供状态迁移机制
|
||
- 支持配置向前兼容
|
||
|
||
### 兼容性
|
||
|
||
确保插件在不同环境中的兼容性:
|
||
|
||
1. **跨平台兼容**:
|
||
- 使用跨平台库和API
|
||
- 避免平台特定代码
|
||
- 进行全平台测试
|
||
|
||
2. **宿主兼容**:
|
||
- 遵循标准插件接口
|
||
- 不依赖宿主特定行为
|
||
- 优雅处理宿主差异
|
||
|
||
3. **资源兼容**:
|
||
- 支持不同资源格式
|
||
- 提供资源转换机制
|
||
- 处理资源缺失情况
|
||
|
||
## 完整示例
|
||
|
||
### 效果器插件
|
||
|
||
下面是一个简单延迟效果器插件的完整示例:
|
||
|
||
```cpp
|
||
// DelayPlugin.h
|
||
#pragma once
|
||
|
||
#include "plugin_interface.h"
|
||
#include <vector>
|
||
#include <map>
|
||
#include <memory>
|
||
#include <atomic>
|
||
|
||
using namespace audio_backend::plugin_host;
|
||
|
||
class DelayPlugin : public IPlugin {
|
||
public:
|
||
DelayPlugin();
|
||
~DelayPlugin() override;
|
||
|
||
// 基本信息
|
||
std::unique_ptr<PluginInfo> get_plugin_info() const override;
|
||
PluginId get_plugin_id() const override { return "com.example.delay-plugin"; }
|
||
std::string get_name() const override { return "示例延迟效果器"; }
|
||
std::string get_vendor() const override { return "示例公司"; }
|
||
Version get_version() const override { return Version(1, 0, 0); }
|
||
PluginCategory get_category() const override { return PluginCategory::Delay; }
|
||
PluginType get_type() const override { return PluginType::Effect; }
|
||
PluginCapability get_capabilities() const override { return PluginCapability::AudioEffect; }
|
||
|
||
// 生命周期管理
|
||
common::ErrorCode initialize(const engine::AudioConfig& config) override;
|
||
common::ErrorCode shutdown() override;
|
||
common::ErrorCode activate() override;
|
||
common::ErrorCode deactivate() override;
|
||
common::ErrorCode suspend() override { return common::ErrorCode::SUCCESS; }
|
||
common::ErrorCode resume() override { return common::ErrorCode::SUCCESS; }
|
||
common::ErrorCode reset() override;
|
||
PluginState get_state() const override { return state_; }
|
||
|
||
// 音频处理
|
||
common::ErrorCode prepare_to_play(double sample_rate, uint32_t max_block_size) override;
|
||
ProcessingResult process_audio(
|
||
const engine::AudioBuffer& input,
|
||
engine::AudioBuffer& output,
|
||
const std::vector<MidiEvent>& midi_in,
|
||
std::vector<MidiEvent>& midi_out,
|
||
const PluginProcessContext& context) override;
|
||
ProcessingResult process_midi(
|
||
const std::vector<MidiEvent>& midi_in,
|
||
std::vector<MidiEvent>& midi_out,
|
||
const PluginProcessContext& context) override { return ProcessingResult::Success; }
|
||
|
||
// 延迟信息
|
||
uint32_t get_latency_samples() const override { return 0; }
|
||
uint32_t get_tail_length_samples() const override { return max_delay_samples_; }
|
||
|
||
// 旁路控制
|
||
common::ErrorCode set_bypass(bool bypass) override {
|
||
bypass_ = bypass;
|
||
return common::ErrorCode::SUCCESS;
|
||
}
|
||
bool is_bypassed() const override { return bypass_; }
|
||
|
||
// 音频配置
|
||
std::vector<uint16_t> get_supported_input_channels() const override {
|
||
return {1, 2, 4, 8};
|
||
}
|
||
std::vector<uint16_t> get_supported_output_channels() const override {
|
||
return {1, 2, 4, 8};
|
||
}
|
||
common::ErrorCode set_channel_configuration(uint16_t input_channels, uint16_t output_channels) override;
|
||
std::pair<uint16_t, uint16_t> get_channel_configuration() const override {
|
||
return {input_channels_, output_channels_};
|
||
}
|
||
|
||
// 参数管理
|
||
size_t get_parameter_count() const override { return parameters_.size(); }
|
||
std::unique_ptr<PluginParameter> get_parameter_info(size_t index) const override;
|
||
std::unique_ptr<PluginParameter> get_parameter_by_id(const std::string& parameter_id) const override;
|
||
common::ErrorCode set_parameter(const std::string& parameter_id, const std::any& value) override;
|
||
std::any get_parameter(const std::string& parameter_id) const override;
|
||
common::ErrorCode reset_parameter(const std::string& parameter_id) override;
|
||
common::ErrorCode reset_all_parameters() override;
|
||
|
||
// 预设管理
|
||
size_t get_preset_count() const override { return presets_.size(); }
|
||
std::unique_ptr<PluginPreset> get_preset_info(size_t index) const override;
|
||
common::ErrorCode load_preset(const std::string& preset_id) override;
|
||
common::ErrorCode save_preset(const std::string& preset_id, const std::string& name) override;
|
||
std::string get_current_preset_id() const override { return current_preset_id_; }
|
||
|
||
// 状态管理
|
||
std::vector<uint8_t> get_state_data() const override;
|
||
common::ErrorCode set_state_data(const std::vector<uint8_t>& data) override;
|
||
size_t get_state_data_size() const override;
|
||
|
||
// GUI支持
|
||
bool has_gui() const override { return true; }
|
||
void* create_gui(void* parent_window) override;
|
||
common::ErrorCode destroy_gui() override;
|
||
common::ErrorCode set_gui_visible(bool visible) override;
|
||
bool is_gui_visible() const override { return gui_visible_; }
|
||
std::pair<uint32_t, uint32_t> get_gui_size() const override { return {600, 400}; }
|
||
common::ErrorCode set_gui_size(uint32_t width, uint32_t height) override;
|
||
|
||
// 事件处理
|
||
void set_event_listener(std::shared_ptr<IPluginEventListener> listener) override {
|
||
event_listener_ = listener;
|
||
}
|
||
void remove_event_listener() override { event_listener_.reset(); }
|
||
|
||
// 性能和诊断
|
||
double get_cpu_usage() const override { return cpu_usage_; }
|
||
size_t get_memory_usage() const override { return memory_usage_; }
|
||
std::chrono::nanoseconds get_average_processing_time() const override { return avg_processing_time_; }
|
||
std::chrono::nanoseconds get_max_processing_time() const override { return max_processing_time_; }
|
||
void reset_performance_statistics() override;
|
||
|
||
private:
|
||
// 参数数据结构
|
||
struct ParameterData {
|
||
std::string id;
|
||
std::string name;
|
||
std::string unit;
|
||
float min_value;
|
||
float max_value;
|
||
float default_value;
|
||
std::function<void(float)> update_callback;
|
||
};
|
||
|
||
// 初始化参数
|
||
void init_parameters();
|
||
|
||
// 更新延迟设置
|
||
void update_delay_settings();
|
||
|
||
// 处理计时更新
|
||
void update_processing_times(std::chrono::nanoseconds processing_time);
|
||
|
||
private:
|
||
// 基本状态
|
||
PluginState state_ = PluginState::Uninitialized;
|
||
engine::AudioConfig config_;
|
||
uint16_t input_channels_ = 2;
|
||
uint16_t output_channels_ = 2;
|
||
bool bypass_ = false;
|
||
bool gui_visible_ = false;
|
||
|
||
// 参数值和定义
|
||
std::map<std::string, std::any> parameters_;
|
||
std::vector<ParameterData> parameter_definitions_;
|
||
|
||
// 预设管理
|
||
std::map<std::string, PluginPreset> presets_;
|
||
std::string current_preset_id_;
|
||
|
||
// 延迟处理
|
||
std::vector<std::vector<float>> delay_buffers_;
|
||
std::vector<size_t> delay_positions_;
|
||
size_t max_delay_samples_ = 0;
|
||
float delay_time_ = 0.5f;
|
||
|
||
// 性能统计
|
||
std::atomic<double> cpu_usage_{0.0};
|
||
std::atomic<size_t> memory_usage_{0};
|
||
std::chrono::nanoseconds avg_processing_time_{0};
|
||
std::chrono::nanoseconds max_processing_time_{0};
|
||
|
||
// GUI
|
||
std::unique_ptr<class DelayPluginGUI> gui_;
|
||
|
||
// 事件监听器
|
||
std::shared_ptr<IPluginEventListener> event_listener_;
|
||
};
|
||
|
||
// 插件工厂函数(导出)
|
||
extern "C" {
|
||
PLUGIN_API IPlugin* create_plugin() {
|
||
return new DelayPlugin();
|
||
}
|
||
|
||
PLUGIN_API void delete_plugin(IPlugin* plugin) {
|
||
delete plugin;
|
||
}
|
||
|
||
PLUGIN_API uint32_t get_plugin_api_version() {
|
||
return PLUGIN_API_VERSION;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 乐器插件
|
||
|
||
以下是一个基础合成器插件的示例框架:
|
||
|
||
```cpp
|
||
// SynthPlugin.h
|
||
class SynthPlugin : public IPlugin {
|
||
public:
|
||
SynthPlugin();
|
||
~SynthPlugin() override;
|
||
|
||
// 实现IPlugin接口...
|
||
|
||
// 乐器特有方法
|
||
void note_on(int note, int velocity);
|
||
void note_off(int note);
|
||
void all_notes_off();
|
||
|
||
private:
|
||
// 音符跟踪
|
||
struct Voice {
|
||
int note;
|
||
float frequency;
|
||
float phase;
|
||
float amplitude;
|
||
bool active;
|
||
};
|
||
|
||
// 振荡器类型
|
||
enum class OscType {
|
||
Sine,
|
||
Saw,
|
||
Square,
|
||
Triangle
|
||
};
|
||
|
||
// 生成采样
|
||
float generate_sample(Voice& voice);
|
||
|
||
// 查找空闲声部
|
||
Voice* find_free_voice();
|
||
|
||
private:
|
||
// 声部管理
|
||
std::vector<Voice> voices_;
|
||
std::mutex voices_mutex_;
|
||
|
||
// 合成器参数
|
||
OscType oscillator_type_;
|
||
float attack_time_;
|
||
float decay_time_;
|
||
float sustain_level_;
|
||
float release_time_;
|
||
|
||
// 性能优化
|
||
bool use_simd_;
|
||
};
|
||
|
||
// 实现示例
|
||
ProcessingResult SynthPlugin::process_audio(
|
||
const engine::AudioBuffer& input,
|
||
engine::AudioBuffer& output,
|
||
const std::vector<MidiEvent>& midi_in,
|
||
std::vector<MidiEvent>& midi_out,
|
||
const PluginProcessContext& context) {
|
||
|
||
// 确保输出缓冲区正确分配
|
||
if (output.frames() != input.frames() ||
|
||
output.channels() != output_channels_) {
|
||
output.allocate(input.frames(), output_channels_, engine::AudioFormat::FLOAT32);
|
||
}
|
||
|
||
// 清空输出缓冲区
|
||
output.clear();
|
||
|
||
// 处理MIDI事件
|
||
for (const auto& event : midi_in) {
|
||
if (event.is_note_on()) {
|
||
note_on(event.get_note(), event.get_velocity());
|
||
} else if (event.is_note_off()) {
|
||
note_off(event.get_note());
|
||
}
|
||
}
|
||
|
||
// 声音生成
|
||
std::lock_guard<std::mutex> lock(voices_mutex_);
|
||
|
||
for (uint32_t i = 0; i < output.frames(); ++i) {
|
||
float sample = 0.0f;
|
||
|
||
// 混合所有活动声部
|
||
for (auto& voice : voices_) {
|
||
if (voice.active) {
|
||
sample += generate_sample(voice);
|
||
}
|
||
}
|
||
|
||
// 应用主音量
|
||
float master_volume = std::any_cast<float>(parameters_["volume"]);
|
||
sample *= master_volume;
|
||
|
||
// 写入所有输出声道
|
||
for (uint16_t ch = 0; ch < output_channels_; ++ch) {
|
||
output.channel_data<float>(ch)[i] = sample;
|
||
}
|
||
}
|
||
|
||
return ProcessingResult::Success;
|
||
}
|
||
```
|
||
|
||
### 分析器插件
|
||
|
||
以下是一个频谱分析器插件的示例框架:
|
||
|
||
```cpp
|
||
// SpectrumAnalyzerPlugin.h
|
||
class SpectrumAnalyzerPlugin : public IPlugin {
|
||
public:
|
||
SpectrumAnalyzerPlugin();
|
||
~SpectrumAnalyzerPlugin() override;
|
||
|
||
// 实现IPlugin接口...
|
||
|
||
// 分析器特有方法
|
||
const std::vector<float>& get_spectrum_data() const { return spectrum_data_; }
|
||
int get_fft_size() const { return fft_size_; }
|
||
|
||
private:
|
||
// FFT分析
|
||
void perform_fft_analysis(const float* data, size_t size);
|
||
|
||
// 应用窗口函数
|
||
void apply_window(float* data, size_t size);
|
||
|
||
// 计算频谱幅度
|
||
void calculate_magnitude(const std::complex<float>* fft_data, float* magnitude, size_t size);
|
||
|
||
private:
|
||
// FFT相关
|
||
int fft_size_ = 1024;
|
||
std::vector<float> window_function_;
|
||
std::vector<std::complex<float>> fft_buffer_;
|
||
std::vector<float> spectrum_data_;
|
||
|
||
// FFT实现
|
||
std::unique_ptr<FFTImplementation> fft_;
|
||
|
||
// 分析设置
|
||
float smoothing_ = 0.8f;
|
||
bool use_log_scale_ = true;
|
||
int frequency_resolution_ = 32;
|
||
};
|
||
|
||
// 实现示例
|
||
ProcessingResult SpectrumAnalyzerPlugin::process_audio(
|
||
const engine::AudioBuffer& input,
|
||
engine::AudioBuffer& output,
|
||
const std::vector<MidiEvent>& midi_in,
|
||
std::vector<MidiEvent>& midi_out,
|
||
const PluginProcessContext& context) {
|
||
|
||
// 复制输入到输出(透明分析器)
|
||
if (output.frames() != input.frames() ||
|
||
output.channels() != input.channels()) {
|
||
output.allocate(input.frames(), input.channels(), input.format());
|
||
}
|
||
input.copy_to(output);
|
||
|
||
// 执行FFT分析(仅分析左声道)
|
||
if (input.channels() > 0) {
|
||
const float* channel_data = input.channel_data<float>(0);
|
||
|
||
// 如果输入数据足够,执行FFT
|
||
if (input.frames() >= fft_size_) {
|
||
perform_fft_analysis(channel_data, fft_size_);
|
||
}
|
||
}
|
||
|
||
return ProcessingResult::Success;
|
||
}
|
||
```
|
||
|
||
## 跨平台开发
|
||
|
||
跨平台插件开发的关键注意事项:
|
||
|
||
1. **编译器和工具链**:
|
||
- 使用跨平台构建系统(如CMake)
|
||
- 注意不同编译器的兼容性
|
||
- 使用条件编译处理平台差异
|
||
|
||
2. **依赖管理**:
|
||
- 使用跨平台库和接口
|
||
- 避免平台特定API
|
||
- 处理库不可用情况
|
||
|
||
3. **文件系统访问**:
|
||
- 使用平台无关的路径表示
|
||
- 处理不同目录结构
|
||
- 兼容不同的文件命名限制
|
||
|
||
4. **GUI开发**:
|
||
- 使用跨平台GUI框架
|
||
- 支持不同主题和样式
|
||
- 适应不同输入设备
|
||
|
||
5. **调试和测试**:
|
||
- 在所有目标平台测试
|
||
- 自动化跨平台测试
|
||
- 为每个平台提供调试支持 |