我的笔记

灵感、随想与新技术
02 六月 2020

音频开发进阶(三)使用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::makePeakFilter 接口为 state(代理的Coefficients)参数赋值,用以设置滤波器参数。

在prepareToPlay中调用 prepare,releaseResources调用reset。



在Audio Plugin中 输出和输入同源,输入输出都是 getWritePoint 这个数组,所以 process 采用 ProcessContextReplacing 这个参数,而 ProcessContextReplacing 也需要一个AudioBlock参数才能初始化。
ProssContextReplacint(AudioBlock(buffer))



付诸行动


先要在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)));
}
...



这样就完成了

点击这里下载源码

课程进度90%

上海外滩 – StudioEIM // MapleStory
  1. 上海外滩 – StudioEIM // MapleStory
  2. 神木村 – StudioEIM // MapleStory
  3. MapleStory – StudioEIM // MapleStory
  4. Pantheon – StudioEIM // MapleStory
  5. 逐梦飞翔 – StudioEIM // MapleStory
  6. 魔法密林 – StudioEIM // MapleStory