你可以在这里找到本章中的相关代码:
TaroStudio/TaroCompressor at main · TaroPie1214/TaroStudio (github.com)
在我们设计各类插件时,不可避免的会面对众多参数的管理问题(比如下图中一个简单的compressor就包含了Threshold,Attack,Release,Ratio等参数),对此JUCE提供了一种方法叫做AudioProcessorValueTreeState(以下简称APVTS):
https://docs.juce.com/master/classAudioProcessorValueTreeState.html
在进入这部分内容的教程之前,请确保你已经完成了上一章的内容,即配置好了开发环境并创建了一个plugin工程。
打开PluginProcessor.h,在public部分补充以下内容:
// PluginProcessor.h
public:
// ...
using APVTS = juce::AudioProcessorValueTreeState;
static APVTS::ParameterLayout createParameterLayout();
APVTS apvts {*this, nullptr, "Parameters", createParameterLayout() };
由于AudioProcessorValueTreeState这一长串字母实在太长,我们通常用using简化成APVTS
打开PluginProcessor.cpp,实现刚刚创建的createParameterLayout()方法,这里以一个compressor的相关实现为例:
juce::AudioProcessorValueTreeState::ParameterLayout TaroCompressorAudioProcessor::createParameterLayout()
{
APVTS::ParameterLayout layout;
using namespace juce;
layout.add(std::make_unique<AudioParameterFloat>(ParameterID { "Threshold", 1 },
"Threshold",
NormalisableRange<float>(-60, 12, 1, 1), 0));
auto attackReleaseRange = NormalisableRange<float>(0, 500, 1, 1);
layout.add(std::make_unique<AudioParameterFloat>(ParameterID { "Attack", 1 }, "Attack", attackReleaseRange, 50));
layout.add(std::make_unique<AudioParameterFloat>(ParameterID { "Release", 1 }, "Release", attackReleaseRange, 250));
auto choices = std::vector<double>{ 1.5, 2, 3, 4, 5, 6, 7, 8, 10, 15, 20, 50, 100 };
juce::StringArray sa;
for ( auto choice : choices )
{
sa.add( juce::String(choice) );
}
layout.add(std::make_unique<AudioParameterChoice>(ParameterID { "Ratio", 1 }, "Ratio", sa, 3));
return layout;
}
接下来我们对以上代码进行拆解
假设我们有一个参数叫做Threshold,它的最小值为-60dB,最大值为12dB,最小步进(参数可调整的最大精度)为1dB,默认值为0dB。
layout.add(std::make_unique<AudioParameterFloat>(ParameterID { "Threshold", 1 },
"Threshold",
NormalisableRange<float>(-60, 12, 1, 1), 0));
我们首先来看一下AudioParameterFloat,在这里它接收了四个参数,分别是:
-
parameterID:参数的ID,必须唯一,我们通过这个ID在别处去修改或获取参数的值
-
parameterName:参数的名称,用于在交互界面上进行展示等等
-
normalisableRange:参数的范围,它也接受了四个参数,分别是
-
defaultValue:默认值
你可能还会发现,在parameterID的位置,我并没有直接给出一个字符串,而是使用了
ParameterID { "Threshold", 1 }
请注意,这样的写法只在编译AU(Audio Unit)格式的插件时需要被使用,其它情况,如Standalone,VST3等则不需要这个步骤,直接给出字符串即可(当然你全都这么写上也不冲突)
layout.add(std::make_unique<AudioParameterFloat>("Threshold",
"Threshold",
NormalisableRange<float>(-60, 12, 1, 1), 0));
我们再来看一个AudioParameterChoice,它跟AudioParameterFloat是最常用的两种APVTS参数类型
它也接收了四个参数,分别是:
- parameterID
- parameterName
- choices:一个StringArray字符串数组,包含所有的选项
- defaultItemIndex:默认选项的Index
auto choices = std::vector<double>{ 1.5, 2, 3, 4, 5, 6, 7, 8, 10, 15, 20, 50, 100 };
juce::StringArray sa;
for ( auto choice : choices )
{
sa.add( juce::String(choice) );
}
layout.add(std::make_unique<AudioParameterChoice>(ParameterID { "Ratio", 1 }, "Ratio", sa, 3));
在这里,我们首先创建了一个juce::StringArray数组,并把所有的Ratio可选值添加到了其中,然后再以此创建了一个AudioParameterChoice并添加到了layout,其中默认选项的index为3,所以在插件初始化时,Ratio的值会默认是4(0->1.5,1->2,2->3,3->4,...)
当我们创建完APVTS之后,此时对代码进行编译,显示的依然还是默认的Hello World,因为我们并没有对PluginEditor进行任何修改。
在之后,我们会讲到如何创建自定义控件并与我们在APVTS中创建的参数进行绑定,并自由设计和摆放我们的控件。但创建控件->绑定参数->摆放控件这一过程还是略显繁琐,如果我们只是在插件的测试阶段(只需要测试它的dsp效果等),其实完全可以跳过这个步骤。
在这里,JUCE提供可一种自动构建界面的方法。
打开PluginProcessor.cpp,找到以下内容:
juce::AudioProcessorEditor* TaroCompressorAudioProcessor::createEditor()
{
return new TaroCompressorAudioProcessorEditor (*this);
}
把它替换成:
juce::AudioProcessorEditor* TaroCompressorAudioProcessor::createEditor()
{
return new juce::GenericAudioProcessorEditor (*this);
}
由此JUCE会根据你当前在APVTS中创建的所有参数自动构建界面,自动生成对应的Slider或ComboBox,如果你按照上述的代码构建了APVTS,那么你此时应该会看到这样的界面:
不过,由于我们并没有设计压缩器的DSP(数字信号处理)部分,所以无论你如何拖动Slider,这个效果器都处于被旁通的状态。但是不用担心,我们将马上进入DSP内容的讲解!
你可以在这里找到本章中的相关代码:
TaroPie1214/TaroStudio: A collection of all my plugins! (github.com)
在本章中,我们首先学习了JUCE中常用的参数管理方式——APVTS,了解了如何向其中添加我们想要的参数,然后根据这些参数自动生成对应的界面。
在下一章中,我们将开始窥探DSP部分的内容,由此创建我们第一个效果器!