前言
阅读此教程需要一定的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
| 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 }; };
|
1 2 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
1 2 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:
1 2 3 4 5 6
| ... 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); 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(); }
|