关于 juce-plugin
面向使用 JUCE 构建音频插件的开发者,提供从 CMake 项目结构、DSP 与参数管理到界面、状态保存和测试的开发指导。内容涵盖插件生命周期、实时线程安全及 JUCE 9 WebView 界面。
主要功能
- 01规划 AudioProcessor 与编辑器结构
- 02使用 APVTS 管理参数和状态
- 03提供 DSP 与实时线程安全指导
- 04配置多种插件格式的 CMake 构建
- 05介绍 JUCE 9 WebView 界面
适用场景
- 01从头创建 JUCE 音频插件
- 02实现合成器、效果器或 MIDI 插件
- 03排查音频线程安全与性能问题
- 04为插件添加 WebView 界面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ shortwavlabs/ agent-skills --skill juce-plugin安装后在 Claude Code 中输入/juce-plugin确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/audio-thread-safety.mdcmake-reference.mddsp-patterns.mdparameter-management.mdperformance-investigation.mdplugin-lifecycle.mdproduction-plugin-practices.mdthreejs-webview-ui.mdui-patterns.mdwebview-ui.mdscripts/webview_gesture_check.cpp详细介绍
JUCE 音频插件开发
你正在使用 JUCE 构建音频插件。JUCE 是一个用于音频应用和插件的跨平台 C++ 框架。插件可通过单一代码库编译为 VST3、AU、AAX、LV2 和 Standalone 格式。
JUCE 插件的工作方式
JUCE 插件包含两个始终配对的核心类:
- AudioProcessor(引擎)——DSP 核心。负责管理参数、在
processBlock()中处理音频,以及处理状态的保存和加载。运行在实时音频线程上。 - AudioProcessorEditor(GUI)——可视化界面。通过 APVTS 附件连接参数。运行在消息线程上。
AudioProcessorValueTreeState(APVTS)连接这两者:为音频线程提供线程安全的参数访问,为编辑器提供 UI 附件,并使用 XML 序列化预设和状态。
项目结构
MyPlugin/
CMakeLists.txt # Build configuration with juce_add_plugin()
Source/
PluginProcessor.h # AudioProcessor subclass declaration
PluginProcessor.cpp # AudioProcessor implementation
PluginEditor.h # AudioProcessorEditor subclass declaration
PluginEditor.cpp # Editor GUI implementation
dsp/ # Custom DSP classes (filters, oscillators, etc.)
components/ # Custom UI components (spectrum displays, etc.)
resources/ # Assets (images, impulse responses, fonts)
工作流:创建新插件
1. 设置项目
确定如何引入 JUCE:
# Option A: JUCE as subdirectory (most common for plugins)
add_subdirectory(JUCE)
# Option B: System-installed JUCE
find_package(JUCE CONFIG REQUIRED)
2. 编写 CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(MyPlugin VERSION 1.0.0)
add_subdirectory(JUCE)
juce_add_plugin(MyPlugin
COMPANY_NAME "YourCompany"
PLUGIN_MANUFACTURER_CODE Yco1 # 4 chars, at least 1 uppercase
PLUGIN_CODE Mp01 # 4 chars, exactly 1 uppercase
FORMATS VST3 AU Standalone # Build targets
PRODUCT_NAME "My Plugin"
IS_SYNTH FALSE
NEEDS_MIDI_INPUT FALSE
NEEDS_MIDI_OUTPUT FALSE
COPY_PLUGIN_AFTER_BUILD TRUE) # Auto-install on build
target_sources(MyPlugin PRIVATE
Source/PluginProcessor.cpp
Source/PluginEditor.cpp)
target_compile_definitions(MyPlugin PUBLIC
JUCE_WEB_BROWSER=0
JUCE_USE_CURL=0
JUCE_VST3_CAN_REPLACE_VST2=0)
target_link_libraries(MyPlugin
PRIVATE juce::juce_audio_utils
PUBLIC
juce::juce_recommended_config_flags
juce::juce_recommended_lto_flags
juce::juce_recommended_warning_flags)
完整的 CMake API(SDK 路径、二进制数据、捆绑资源、iOS/macOS 特定配置)请参阅 references/cmake-reference.md。
3. 创建处理器
AudioProcessor 子类必须重写特定方法。完整契约请参阅 references/plugin-lifecycle.md。
最简处理器骨架:
// PluginProcessor.h
#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
class MyPluginProcessor : public juce::AudioProcessor
{
public:
MyPluginProcessor();
~MyPluginProcessor() override;
// Audio processing
void prepareToPlay (double sampleRate, int samplesPerBlock) override;
void releaseResources() override;
void processBlock (juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
// Editor
juce::AudioProcessorEditor* createEditor() override;
bool hasEditor() const override;
// Info
const juce::String getName() const override;
bool acceptsMidi() const override;
bool producesMidi() const override;
double getTailLengthSeconds() const override;
int getNumPrograms() override;
int getCurrentProgram() override;
void setCurrentProgram (int index) override;
const juce::String getProgramName (int index) override;
void changeProgramName (int index, const juce::String& newName) override;
// State
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
// Bus layout
bool isBusesLayoutSupported (const BusesLayout& layouts) const override;
// APVTS — the parameter manager
juce::AudioProcessorValueTreeState apvts;
private:
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyPluginProcessor)
};
4. 使用 APVTS 设置参数
在自由函数中静态定义所有参数,然后在构造函数中初始化 APVTS。完整模式(附件、近似动态参数、非参数状态)请参阅 references/parameter-management.md。
// In PluginProcessor.cpp
juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout()
{
return {
std::make_unique<juce::AudioParameterFloat>(
juce::ParameterID { "gain", 1 },
"Gain",
juce::NormalisableRange<float> (0.0f, 1.0f, 0.001f),
0.5f),
std::make_unique<juce::AudioParameterChoice>(
juce::ParameterID { "filterType", 1 },
"Filter Type",
juce::StringArray { "Low Pass", "High Pass", "Band Pass" },
0),
std::make_unique<juce::AudioParameterBool>(
juce::ParameterID { "bypass", 1 },
"Bypass",
false)
};
}
MyPluginProcessor::MyPluginProcessor()
: AudioProcessor (BusesProperties()
.withInput ("Input", juce::AudioChannelSet::stereo())
.withOutput ("Output", juce::AudioChannelSet::stereo())),
apvts (*this, nullptr, "Parameters", createParameterLayout())
{
}
5. 实现 processBlock
音频处理在这里进行。它运行在实时线程上,因此必须遵守严格规则。完整的安全指南请参阅 references/audio-thread-safety.md。
void MyPluginProcessor::processBlock (juce::AudioBuffer<float>& buffer,
juce::MidiBuffer& midiMessages)
{
juce::ScopedNoDenormals noDenormals;
// Read parameter values (thread-safe via atomics)
auto gainValue = apvts.getRawParameterValue("gain")->load();
for (int channel = 0; channel < buffer.getNumChannels(); ++channel)
{
auto* channelData = buffer.getWritePointer (channel);
for (int sample = 0; sample < buffer.getNumSamples(); ++sample)
{
channelData[sample] *= gainValue;
}
}
}
DSP 模块模式(滤波器、振荡器、效果链、卷积)请参阅 references/dsp-patterns.md。
6. 实现状态的保存和加载
void MyPluginProcessor::getStateInformation (juce::MemoryBlock& destData)
{
auto state = apvts.copyState();
if (auto xml = state.createXml())
copyXmlToBinary (*xml, destData);
}
void MyPluginProcessor::setStateInformation (const void* data, int sizeInBytes)
{
if (auto xml = getXmlFromBinary (data, sizeInBytes))
if (xml->hasTagName (apvts.state.getType()))
apvts.replaceState (juce::ValueTree::fromXml (*xml));
}
7. 创建编辑器
编辑器通过附件将 UI 控件连接到 APVTS 参数。自定义组件、布局策略和 LookAndFeel 定制请参阅 references/ui-patterns.md。
// PluginEditor.h
#pragma once
#include <juce_audio_processors/juce_audio_processors.h>
#include "PluginProcessor.h"
class MyPluginEditor : public juce::AudioProcessorEditor
{
public:
MyPluginEditor (MyPluginProcessor&);
~MyPluginEditor() override;
void paint (juce::Graphics&) override;
void resized() override;
private:
MyPluginProcessor& processorRef;
juce::Slider gainSlider;
juce::AudioProcessorValueTreeState::SliderAttachment gainAttachment;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyPluginEditor)
};
// PluginEditor.cpp
MyPluginEditor::MyPluginEditor (MyPluginProcessor& p)
: AudioProcessorEditor (&p),
processorRef (p),
gainAttachment (p.apvts, "gain", gainSlider)
{
addAndMakeVisible (gainSlider);
gainSlider.setSliderStyle (juce::Slider::RotaryHorizontalVerticalDrag);
gainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, false, 80, 20);
setResizeLimits (200, 150, 800, 600);
setResizable (true, true);
setSize (400, 300);
}
void MyPluginEditor::resized()
{
auto bounds = getLocalBounds().reduced (10);
gainSlider.setBounds (bounds.removeFromTop (100));
}
8. 构建和测试
# Configure
cmake -B build -DCMAKE_BUILD_TYPE=Release
# Build all formats
cmake --build build --config Release
# On macOS, plugins are copied to:
# ~/Library/Audio/Plug-Ins/VST3/
# ~/Library/Audio/Plug-Ins/Components/ (AU)
# On Linux:
# ~/.vst3/
# ~/.lv2/ (if LV2 format enabled)
# On Windows:
# %APPDATA%/VST3/
跨平台构建、使用 GitHub Actions 进行 CI/CD 以及发布,请参阅 references/cmake-reference.md。
插件类型
JUCE 支持多种插件类别,可通过 CMake 属性进行配置:
| 类型 | CMake 标志 | 说明 |
|---|---|---|
| 音频效果 | 默认值 | 将音频输入处理为音频输出 |
| 合成器 | IS_SYNTH TRUE、NEEDS_MIDI_INPUT TRUE | 根据 MIDI 生成音频 |
| MIDI 效果 | IS_MIDI_EFFECT TRUE | 仅处理 MIDI(不处理音频) |
| 合成器 + MIDI 输出 | 添加 NEEDS_MIDI_OUTPUT TRUE | 同时发送 MIDI 的合成器 |
JUCE 模块
模块是构建的基本单元。链接顶层模块后,其传递依赖会自动解析:
| 模块 | 用途 | 链接 |
|---|---|---|
juce_audio_utils | 音频工具、MIDI 键盘 | juce::juce_audio_utils |
juce_dsp | DSP:滤波器、振荡器、卷积、ProcessorChain | juce::juce_dsp |
juce_audio_processors | AudioProcessor、APVTS、插件格式托管 | (自动包含) |
juce_graphics | 图形、Colour、Path、Image | (自动包含) |
juce_gui_basics | Component、Slider、Button、Label | (自动包含) |
juce_gui_extra | CodeEditorComponent、WebBrowserComponent | juce::juce_gui_extra |
juce_opengl | 插件中的 OpenGL 渲染 | juce::juce_opengl |
juce_osc | 开放声音控制(OSC) | juce::juce_osc |
24 个 JUCE 模块分别为:juce_analytics、juce_animation、juce_audio_basics、juce_audio_devices、juce_audio_formats、juce_audio_plugin_client、juce_audio_processors、juce_audio_processors_headless、juce_audio_utils、juce_box2d、juce_core、juce_cryptography、juce_data_structures、juce_dsp、juce_events、juce_graphics、juce_gui_basics、juce_gui_extra、juce_javascript、juce_midi_ci、juce_opengl、juce_osc、juce_product_unlocking、juce_video。
WebView 界面(JUCE 9)
对于基于 Web 的编辑器,请启用浏览器模块,并使用最新的 WebView 集成参考,了解 CMake/WebView2、中继器生命周期、前端程序包兼容性、离线资源和手势。原生编辑器示例中的 JUCE_WEB_BROWSER=0 必须改为 1。请根据已安装的 JUCE 版本核对 API 签名。
对于使用 Blender 制作、可交互的音箱、效果踏板或机架设备,请阅读 Three.js/WebView 界面。其中介绍了分阶段的 Blender → 语义化 GLB → 模拟 Three.js → JUCE WebView → APVTS 工作流,以及绝对控制和拉动/旋转控制、开关、输入插孔、联动的可见状态、仅限插件的实用界面元素、按需渲染和宿主验收。以 APVTS 为准,在浏览器之前构建中继器,在浏览器之后构建附件,并确保所有 WebView 操作都不在音频线程上执行。
参考文件
根据实现内容按需阅读:
references/plugin-lifecycle.md— 完整的 AudioProcessor 契约:所有重写项、总线配置、双精度,以及合成器的 Synthesiser 框架references/parameter-management.md— APVTS 模式:参数布局、附件、原始指针、非参数状态、参数组references/dsp-patterns.md— DSP 手册:ProcessorChain、滤波器、振荡器、波形整形器、卷积混响、延迟线、波表合成、控制速率 LFO、双层级链架构、逐样本处理与分块处理references/ui-patterns.md— 编辑器模式:组件布局、自定义控件、LookAndFeel、电平表、FFT 频谱分析器、自适应调整大小、用于资源的二进制数据references/webview-ui.md— JUCE 9 WebBrowserComponent、WebView2、本地资源、原生函数、参数绑定、前端框架、显式开发模式,以及流式视觉数据(电平表和重绘开销、空闲状态支持)references/threejs-webview-ui.md— 交互式吉他设备 GLB、模拟/JUCE 绑定、实体控件、派生视觉状态、按需渲染、阴影贴图失效、重绘频率、像素比、绘制调用和分阶段宿主验证references/audio-thread-safety.md— 实时安全规则:在 processBlock 中可以和不可以做的操作、无锁模式、原子操作、音频故障调试references/cmake-reference.md— 完整的 CMake API:所有 juce_add_plugin 属性、SDK 路径、二进制数据、CI/CD、平台细节references/performance-investigation.md— 插件“占用 CPU 过多”:优化前分别评估音频线程、消息线程和 WebView 开销,比较无头与实时回调测量、测试矩阵、阶段细分、测试工具和实时宿主工具、空值测试、多实例扩展、性能门槛及报告references/production-plugin-practices.md— 产品级插件实践:CMake 预设、测试/测量目标、资源/模型加载、状态恢复、延迟/尾音更新、过采样电路建模插件(采样率策略、整数延迟、旁通参数、模型修订版)、宿主验证和发布门槛
常见模式
合成器插件
使用 Synthesiser 框架。重写 SynthesiserVoice::renderNextBlock() 以实现逐声部 DSP,使用 SynthesiserSound 处理音符/通道路由。完整模式请参阅 references/plugin-lifecycle.md。
侧链效果器
在构造函数中添加侧链总线:
BusesProperties()
.withInput ("Input", juce::AudioChannelSet::stereo())
.withInput ("Sidechain", juce::AudioChannelSet::stereo())
.withOutput ("Output", juce::AudioChannelSet::stereo())
在 processBlock 中访问:auto sidechain = getBusBuffer (buffer, true, 1);
多频段处理
使用 juce::dsp::LinkwitzRileyFilter<> 分频滤波器将信号拆分为多个频段,分别处理各个频段,然后重新合并。
自定义预设系统
将预设保存为 XML 文件,通过 juce::File 加载,或嵌入为二进制数据。使用 apvts.replaceState() 加载。
提示
- 使用
GenericAudioProcessorEditor快速制作原型——它会根据参数自动生成 UI。在createEditor()中直接返回new GenericAudioProcessorEditor(*this)即可。 - 使用
pluginval在发布前验证插件——它可以捕获线程、状态和参数方面的错误。 - 将
getRawParameterValue()指针缓存为成员——切勿在processBlock()中调用它。 - 在
prepareToPlay()中预分配所有缓冲区,切勿在processBlock()中分配。 - 在
processBlock()开头使用juce::ScopedNoDenormals,避免非规格化浮点数带来的性能损失。 - 优化前先分析 CPU 占用——宿主性能表的测量方式因宿主而异。先对比编辑器关闭、打开和旁通时的音频回调,再判断问题出在 DSP 还是 UI(
references/performance-investigation.md)。 - 在 CMake 中设置
COPY_PLUGIN_AFTER_BUILD TRUE以加快迭代——每次构建后都会自动安装插件。 - 重建前关闭 DAW——DAW 加载插件时会锁定插件文件。