音频开发进阶(三)使用DSP模块(附源码·EQ效果器)
JUCE 在 5.1 版本之后加入了 dsp 模块,从此可以通过调用接口的方式实现各种音频效果,降低了对开发者算法水平的要求。
确定目标
一个 EQ 效果器
收集信息
在开发文档中的 dsp 分区里,发现 Filter 是我想要的。
在查阅相关资料后,得知 Filter 对象 有三个接口需要调用 :
1.prepare(ProcessSpec&)- 在处理信号前调用,设定采样率等规格参数,必须调用
2.reset – 重置通道
3.process(ProcessContextReplacing或ProcessContextNonReplacing) – 处理信号
查到 ProcessorDuplicator 把一个处理器变成双声道立体声两个处理器,同时它为被代理的处理器(比如这里的 dsp::IIR:Filter ) 实现一种适配器的模式。这种模式中对 ProcessorDuplicator 的操作和对原处理器(比如这里的 Filter )的的操作极为类似。(在这里就是 ProcessorDuplicator 对应 Filter, ProcessorDuplicator 的参数字段 state 对应 Coefficients )
整理思路
用 ProcessorDuplicator 代理 Filter,即 Filter 的 prepare、reset、process 都换成 ProcessorDuplicator 的。此外 ProcessorDuplicator 的 state 对象等同于 Filter 的 Coefficients的作用。
用 dsp::IIR::Coefficients
在prepareToPlay中调用 prepare,releaseResources调用reset。
在Audio Plugin中 输出和输入同源,输入输出都是 getWritePoint 这个数组,所以 process 采用 ProcessContextReplacing 这个参数,而 ProcessContextReplacing 也需要一个AudioBlock参数才能初始化。
(ProssContextReplacint
付诸行动
先要在JUCE工程中勾选DSP模块,才能打开DSP功能使用 dsp 命名空间。
声明推子
1 2 3 4 5 6 | // PluginEditor.h ... MyEqAudioProcessor& processor; Slider HF_Vol; // 声明一个推子 <- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MyEqAudioProcessorEditor) ... |
让推子影响变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | // PluginEditor.cpp ... MyEqAudioProcessorEditor::MyEqAudioProcessorEditor (MyEqAudioProcessor& p) : AudioProcessorEditor (&p), processor (p) { HF_Vol.setRange(0.01, 1, 0.01); HF_Vol.setBounds(0, 0, 100, 50); HF_Vol.setSliderStyle(Slider::SliderStyle::LinearHorizontal); HF_Vol.setTextBoxStyle(Slider::TextBoxBelow, true, 100, 30);; addAndMakeVisible(HF_Vol); HF_Vol.onValueChange = [this] { *(processor.myFilter.state) = *(dsp::IIR::Coefficients<float>::makePeakFilter(processor.getSampleRate(),2400.0f,0.7f,HF_Vol.getValue())); }; setSize (400, 300); *(processor.myFilter.state) = *(dsp::IIR::Coefficients<float>::makePeakFilter(processor.getSampleRate(), 2400.0f, 0.7f, HF_Vol.getValue())); } ... |
声明处理器
1 2 3 4 5 6 | // PluginProcessor.h ... void setStateInformation (const void* data, int sizeInBytes) override; dsp::ProcessorDuplicator<dsp::IIR::Filter<float>, dsp::IIR::Coefficients<float>> myFilter; // 声明一个处理器 <- private: ... |
prepare 与 reset
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | // PluginProcessor.cpp ... void MyEqAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock) { dsp::ProcessSpec ps; ps.maximumBlockSize = samplesPerBlock; ps.numChannels = getTotalNumOutputChannels(); ps.sampleRate = sampleRate; myFilter.prepare(ps); } void MyEqAudioProcessor::releaseResources() { myFilter.reset(); } ... |
处理器处理信号
1 2 3 4 5 6 7 8 9 | // PluginProcessor.cpp ... void MyEqAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages) { ScopedNoDenormals noDenormals; myFilter.process(dsp::ProcessContextReplacing<float>(dsp::AudioBlock<float>(buffer))); } ... |
这样就完成了