前言
阅读此教程需要一定的C++和JUCE基础。
我的Bilibili频道:香芋派Taro
我的个人博客:taropie0224.github.io
我的公众号:香芋派的烘焙坊
我的音频技术交流群:1136403177
我的个人微信:JazzyTaroPie  
上次我们搭完了合成器的基本框架,这一章我们讲ADSR的搭建。
来到SynthVoice.h中的private,添加如下:
| 12
 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用于判断:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | 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 };
 };
 
 | 
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | ...
 void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
 {
 jassert (isPrepared);
 
 juce::dsp::AudioBlock<float> audioBlock { outputBuffer };
 osc.process(juce::dsp::ProcessContextReplacing<float> (audioBlock));
 gain.process(juce::dsp::ProcessContextReplacing<float> (audioBlock));
 }
 ...
 
 | 
来的PluginProcessor.cpp完成prepareToPlay
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | ...
 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:
| 12
 3
 4
 5
 6
 
 | ...
 private:
 juce::ADSR adsr;
 juce::ADSR::Parameters adsrParams;
 ...
 
 | 
设定adsr的sampleRate:
| 12
 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中的数据:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | ...void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
 {
 jassert (isPrepared);
 
 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:
| 12
 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振荡频率:
| 12
 3
 4
 5
 
 | void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound* sound, int currentPitchWheelPosition){
 osc.setFrequency (juce::MidiMessage::getMidiNoteInHertz(midiNoteNumber));
 adsr.noteOn();
 }
 
 |