mcpField
J
juce-plugin

juce-plugin

github.com/shortwavlabs开发699 次安装3 周前更新
代码仓库

关于 juce-plugin

面向使用 JUCE 构建音频插件的开发者,提供从 CMake 项目结构、DSP 与参数管理到界面、状态保存和测试的开发指导。内容涵盖插件生命周期、实时线程安全及 JUCE 9 WebView 界面。

主要功能

  1. 01规划 AudioProcessor 与编辑器结构
  2. 02使用 APVTS 管理参数和状态
  3. 03提供 DSP 与实时线程安全指导
  4. 04配置多种插件格式的 CMake 构建
  5. 05介绍 JUCE 9 WebView 界面

适用场景

  1. 01从头创建 JUCE 音频插件
  2. 02实现合成器、效果器或 MIDI 插件
  3. 03排查音频线程安全与性能问题
  4. 04为插件添加 WebView 界面

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/shortwavlabs/agent-skills --skill juce-plugin

安装后在 Claude Code 中输入/juce-plugin确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

agents/
openai.yaml
references/
audio-thread-safety.md
cmake-reference.md
dsp-patterns.md
parameter-management.md
performance-investigation.md
plugin-lifecycle.md
production-plugin-practices.md
threejs-webview-ui.md
ui-patterns.md
webview-ui.md
scripts/
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_dspDSP:滤波器、振荡器、卷积、ProcessorChainjuce::juce_dsp
juce_audio_processorsAudioProcessor、APVTS、插件格式托管(自动包含)
juce_graphics图形、Colour、Path、Image(自动包含)
juce_gui_basicsComponent、Slider、Button、Label(自动包含)
juce_gui_extraCodeEditorComponent、WebBrowserComponentjuce::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 加载插件时会锁定插件文件。

评论

支持 Markdown