前言

阅读此教程需要一定的C++和JUCE基础。
我的Bilibili频道:香芋派Taro
我的个人博客:taropie0224.github.io
我的公众号:香芋派的烘焙坊
我的音频技术交流群:1136403177
我的个人微信:JazzyTaroPie

上次我们搭完了合成器的基本框架,这一章我们讲ADSR的搭建。

来到SynthVoice.h中的private,添加如下:

1
2
3
4
5
6
...
private:
juce::dsp::Oscillator<float> osc { [](float x) {return std::sin(x);} };
juce::dsp::Gain<float> gain;
};
...

在SynthVoice的renderNextBlock前补充prepareToPlay函数同时补充isPrepared用于判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//SynthVoice.h
class SynthVoice : public juce::SynthesiserVoice
{
public:
bool canPlaySound (juce::SynthesiserSound* sound) override;
void startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound* sound, int currentPitchWheelPosition) override;
void stopNote (float velocity, bool allowTailOff) override;
void controllerMoved (int controllerNumber, int newControllerValue) override;
void pitchWheelMoved (int newPitchWheelValue) override;
void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
void renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples) override;
private:
juce::dsp::Oscillator<float> osc { [](float x) {return std::sin(x);} };
juce::dsp::Gain<float> gain;
bool isPrepared { false }; //判断是否prepare
};
1
2
3
4
5
6
7
8
9
10
11
//SynthVoice.cpp
...
void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
{
jassert (isPrepared); //如果没有prepared会抛出问题

juce::dsp::AudioBlock<float> audioBlock { outputBuffer };
osc.process(juce::dsp::ProcessContextReplacing<float> (audioBlock));
gain.process(juce::dsp::ProcessContextReplacing<float> (audioBlock));
}
...

来的PluginProcessor.cpp完成prepareToPlay

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//PluginProcessor.cpp
...
void SynthTutorialAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
synth.setCurrentPlaybackSampleRate (sampleRate);

for (int i = 0; i < synth.getNumVoices(); i++)
{
if (auto voice = dynamic_cast<SynthVoice*>(synth.getVoice(i)))
{
voice->prepareToPlay (sampleRate, samplesPerBlock, getTotalNumInputChannels());
}
}
}
...

创建ADSR及其对应Params:

1
2
3
4
5
6
//SynthVoice.h
...
private:
juce::ADSR adsr;
juce::ADSR::Parameters adsrParams;
...

设定adsr的sampleRate:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels)
{
adsr.setSampleRate (sampleRate);

juce::dsp::ProcessSpec spec;
spec.maximumBlockSize = samplesPerBlock;
spec.sampleRate = sampleRate;
spec.numChannels = outputChannels;

osc.prepare (spec);
gain.prepare ( spec );

gain.setGainLinear (0.01f);

isPrepared = true;
...
}

把adsr应用到buffer中的数据:

1
2
3
4
5
6
7
8
9
10
11
12
...
void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
{
jassert (isPrepared); //如果没有prepared会抛出问题

juce::dsp::AudioBlock<float> audioBlock { outputBuffer };
osc.process(juce::dsp::ProcessContextReplacing<float> (audioBlock));
gain.process(juce::dsp::ProcessContextReplacing<float> (audioBlock));

adsr.applyEnvelopeToBuffer(outputBuffer, startSample, numSamples);
}
...

补充noteOn/noteOff:

1
2
3
4
5
6
7
8
9
void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound* sound, int currentPitchWheelPosition)
{
adsr.noteOn();
}

void SynthVoice::stopNote (float velocity, bool allowTailOff)
{
adsr.noteOff();
}

使用传入的midiNoteNumber来控制Osc振荡频率:

1
2
3
4
5
void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound* sound, int currentPitchWheelPosition)
{
osc.setFrequency (juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber));
adsr.noteOn();
}