This is from [tapSynth](https://github.com/TheAudioProgrammer/tapSynth) by [The Audio Programmer](https://www.youtube.com/@TheAudioProgrammer)
Diff 0: e7a154de0b83dab2332d8dc06a3bace88794bb46 to 97731b4dae33477bc2711462cd53a16576b6f890
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index acffd7f..d69180f 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -97,6 +97,14 @@ void TapSynthAudioProcessor::changeProgramName (int index, const juce::String& n
void TapSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
synth.setCurrentPlaybackSampleRate (sampleRate);
+
+ for (int i = 0; i < synth.getNumVoices(); i++)
+ {
+ if (auto voice = dynamic_cast(synth.getVoice(i)))
+ {
+ voice->prepareToPlay (sampleRate, samplesPerBlock, getTotalNumOutputChannels());
+ }
+ }
}
void TapSynthAudioProcessor::releaseResources()
```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp
index 72b6997..9be4d2f 100644
```diff
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -18,12 +18,13 @@ bool SynthVoice::canPlaySound (juce::SynthesiserSound* sound)
void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound *sound, int currentPitchWheelPosition)
{
-
+ osc.setFrequency (juce::MidiMessage::getMidiNoteInHertz (midiNoteNumber));
+ adsr.noteOn();
}
void SynthVoice::stopNote (float velocity, bool allowTailOff)
{
-
+ adsr.noteOff();
}
void SynthVoice::controllerMoved (int controllerNumber, int newControllerValue)
@@ -36,7 +37,30 @@ void SynthVoice::pitchWheelMoved (int newPitchWheelValue)
}
+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;
+}
+
void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
{
+ jassert (isPrepared);
+
+ juce::dsp::AudioBlock audioBlock { outputBuffer };
+ osc.process (juce::dsp::ProcessContextReplacing (audioBlock));
+ gain.process (juce::dsp::ProcessContextReplacing (audioBlock));
+ adsr.applyEnvelopeToBuffer (outputBuffer, startSample, numSamples);
}
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index a606b19..1048347 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -21,8 +21,18 @@ public:
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::ADSR adsr;
+ juce::ADSR::Parameters adsrParams;
+ juce::dsp::Oscillator osc { [](float x) { return std::sin (x); }};
+ juce::dsp::Gain gain;
+ bool isPrepared { false };
+
+ // return std::sin (x); //Sine Wave
+ // return x / MathConstants::pi; // Saw Wave
+ // return x < 0.0f ? -1.0f : 1.0f; // Square Wave
};
```
diff --git a/tapSynth.jucer b/tapSynth.jucer
index b6d02ce..8ea6bb5 100644
```diff
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -1,7 +1,7 @@
+ addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn">
+
@@ -49,6 +50,7 @@
+
```
Diff 1: 97731b4dae33477bc2711462cd53a16576b6f890 to b2530760f0a925df7c3098ba9151e8c657723fed
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index 889bd56..3214a71 100644
```diff
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -13,9 +13,16 @@
TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcessor& p)
: AudioProcessorEditor (&p), audioProcessor (p)
{
- // Make sure that before the constructor has finished, you've set the
- // editor's size to whatever you need it to be.
setSize (400, 300);
+
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ attackAttachment = std::make_unique(audioProcessor.apvts, "ATTACK", attackSlider);
+ decayAttachment = std::make_unique(audioProcessor.apvts, "DECAY", decaySlider);
+ sustainAttachment = std::make_unique(audioProcessor.apvts, "SUSTAIN", sustainSlider);
+ releaseAttachment = std::make_unique(audioProcessor.apvts, "RELEASE", releaseSlider);
+
+ oscSelAttachment = std::make_unique(audioProcessor.apvts, "OSC", oscSelector);
}
TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h
index 9f34431..82b7c57 100644
```diff
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -25,8 +25,20 @@ public:
void resized() override;
private:
- // This reference is provided as a quick way for your editor to
- // access the processor object that created it.
+ juce::Slider attackSlider;
+ juce::Slider decaySlider;
+ juce::Slider sustainSlider;
+ juce::Slider releaseSlider;
+ juce::ComboBox oscSelector;
+
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ std::unique_ptr attackAttachment;
+ std::unique_ptr decayAttachment;
+ std::unique_ptr sustainAttachment;
+ std::unique_ptr releaseAttachment;
+ std::unique_ptr oscSelAttachment;
+
TapSynthAudioProcessor& audioProcessor;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index d69180f..9bc772f 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -19,7 +19,7 @@ TapSynthAudioProcessor::TapSynthAudioProcessor()
#endif
.withOutput ("Output", juce::AudioChannelSet::stereo(), true)
#endif
- )
+ ), apvts (*this, nullptr, "Parameters", createParams())
#endif
{
synth.addSound (new SynthSound());
@@ -192,4 +192,18 @@ juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
return new TapSynthAudioProcessor();
}
-// Value Tree
+juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::createParams()
+{
+ std::vector> params;
+
+ // OSC select
+ params.push_back (std::make_unique ("OSC", "Oscillator", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+
+ // ADSR
+ params.push_back (std::make_unique("ATTACK", "Attack", juce::NormalisableRange { 0.1f, 1.0f, }, 0.1f));
+ params.push_back (std::make_unique("DECAY", "Decay", juce::NormalisableRange { 0.1f, 1.0f, }, 0.1f));
+ params.push_back (std::make_unique("SUSTAIN", "Sustain", juce::NormalisableRange { 0.1f, 1.0f, }, 1.0f));
+ params.push_back (std::make_unique("RELEASE", "Release", juce::NormalisableRange { 0.1f, 3.0f, }, 0.4f));
+
+ return { params.begin(), params.end() };
+}
```
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h
index 9c4296a..68c34b4 100644
```diff
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -54,9 +54,12 @@ public:
//==============================================================================
void getStateInformation (juce::MemoryBlock& destData) override;
void setStateInformation (const void* data, int sizeInBytes) override;
+
+ juce::AudioProcessorValueTreeState apvts;
private:
juce::Synthesiser synth;
+ juce::AudioProcessorValueTreeState::ParameterLayout createParams();
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessor)
```
Diff 2: b2530760f0a925df7c3098ba9151e8c657723fed to 38ff9b2a20ffed9481f264b549499c9591a12a62
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 9bc772f..e8eb9dc 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -157,6 +157,10 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
}
}
+ for (const juce::MidiMessageMetadata metadata : midiMessages)
+ if (metadata.numBytes == 3)
+ juce::Logger::writeToLog ("TimeStamp: " + juce::String (metadata.getMessage().getTimeStamp()));
+
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
}
```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp
index 9be4d2f..169aa3a 100644
```diff
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -25,6 +25,9 @@ void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::Synthesise
void SynthVoice::stopNote (float velocity, bool allowTailOff)
{
adsr.noteOff();
+
+ if (! allowTailOff || ! adsr.isActive())
+ clearCurrentNote();
}
void SynthVoice::controllerMoved (int controllerNumber, int newControllerValue)
@@ -51,6 +54,13 @@ void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outp
gain.setGainLinear (0.01f);
+ adsrParams.attack = 0.8f;
+ adsrParams.decay = 0.8f;
+ adsrParams.sustain = 1.0f;
+ adsrParams.release = 1.5f;
+
+ adsr.setParameters (adsrParams);
+
isPrepared = true;
}
@@ -58,9 +68,26 @@ void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int
{
jassert (isPrepared);
- juce::dsp::AudioBlock audioBlock { outputBuffer };
+ if (! isVoiceActive())
+ return;
+
+ synthBuffer.setSize (outputBuffer.getNumChannels(), numSamples, false, false, true);
+ synthBuffer.clear();
+
+ juce::dsp::AudioBlock audioBlock { synthBuffer };
osc.process (juce::dsp::ProcessContextReplacing (audioBlock));
gain.process (juce::dsp::ProcessContextReplacing (audioBlock));
- adsr.applyEnvelopeToBuffer (outputBuffer, startSample, numSamples);
+ adsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples());
+
+ if (startSample != 0)
+ jassertfalse;
+
+ for (int channel = 0; channel < outputBuffer.getNumChannels(); ++channel)
+ {
+ outputBuffer.addFrom (channel, startSample, synthBuffer, channel, 0, numSamples);
+
+ if (! adsr.isActive())
+ clearCurrentNote();
+ }
}
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index 1048347..1ee7b23 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -27,6 +27,7 @@ public:
private:
juce::ADSR adsr;
juce::ADSR::Parameters adsrParams;
+ juce::AudioBuffer synthBuffer;
juce::dsp::Oscillator osc { [](float x) { return std::sin (x); }};
juce::dsp::Gain gain;
```
Diff 3: 38ff9b2a20ffed9481f264b549499c9591a12a62 to be86a6aca2a09395ab10c3c820a5424a263862b8
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index 3214a71..f535bbf 100644
```diff
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -23,6 +23,11 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess
releaseAttachment = std::make_unique(audioProcessor.apvts, "RELEASE", releaseSlider);
oscSelAttachment = std::make_unique(audioProcessor.apvts, "OSC", oscSelector);
+
+ setSliderParams (attackSlider);
+ setSliderParams (decaySlider);
+ setSliderParams (sustainSlider);
+ setSliderParams (releaseSlider);
}
TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
@@ -32,16 +37,27 @@ TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
//==============================================================================
void TapSynthAudioProcessorEditor::paint (juce::Graphics& g)
{
- // (Our component is opaque, so we must completely fill the background with a solid colour)
- g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
-
- g.setColour (juce::Colours::white);
- g.setFont (15.0f);
- g.drawFittedText ("Hello World!", getLocalBounds(), juce::Justification::centred, 1);
+ g.fillAll (juce::Colours::black);
}
void TapSynthAudioProcessorEditor::resized()
{
- // This is generally where you'll want to lay out the positions of any
- // subcomponents in your editor..
+ const auto bounds = getLocalBounds().reduced (10);
+ const auto padding = 10;
+ const auto sliderWidth = bounds.getWidth() / 4 - padding;
+ const auto sliderHeight = bounds.getWidth() / 4 - padding;
+ const auto sliderStartX = 0;
+ const auto sliderStartY = bounds.getHeight() / 2 - (sliderHeight / 2);
+
+ attackSlider.setBounds (sliderStartX, sliderStartY, sliderWidth, sliderHeight);
+ decaySlider.setBounds (attackSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+ sustainSlider.setBounds (decaySlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+ releaseSlider.setBounds (sustainSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+}
+
+void TapSynthAudioProcessorEditor::setSliderParams (juce::Slider& slider)
+{
+ slider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
+ slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
+ addAndMakeVisible (slider);
}
```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h
index 82b7c57..cfe4fca 100644
```diff
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -25,6 +25,8 @@ public:
void resized() override;
private:
+ void setSliderParams (juce::Slider& slider);
+
juce::Slider attackSlider;
juce::Slider decaySlider;
juce::Slider sustainSlider;
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index e8eb9dc..538500b 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -146,21 +146,23 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
-
for (int i = 0; i < synth.getNumVoices(); ++i)
{
- if (auto voice = dynamic_cast(synth.getVoice(i)))
+ if (auto voice = dynamic_cast(synth.getVoice(i)))
{
// Osc controls
// ADSR
// LFO
+
+ auto& attack = *apvts.getRawParameterValue ("ATTACK");
+ auto& decay = *apvts.getRawParameterValue ("DECAY");
+ auto& sustain = *apvts.getRawParameterValue ("SUSTAIN");
+ auto& release = *apvts.getRawParameterValue ("RELEASE");
+
+ voice->updateADSR (attack.load(), decay.load(), sustain.load(), release.load());
}
}
- for (const juce::MidiMessageMetadata metadata : midiMessages)
- if (metadata.numBytes == 3)
- juce::Logger::writeToLog ("TimeStamp: " + juce::String (metadata.getMessage().getTimeStamp()));
-
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
}
```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp
index 169aa3a..a1a2e74 100644
```diff
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -52,16 +52,19 @@ void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outp
osc.prepare (spec);
gain.prepare (spec);
- gain.setGainLinear (0.01f);
+ gain.setGainLinear (0.3f);
- adsrParams.attack = 0.8f;
- adsrParams.decay = 0.8f;
- adsrParams.sustain = 1.0f;
- adsrParams.release = 1.5f;
+ isPrepared = true;
+}
+
+void SynthVoice::updateADSR (const float attack, const float decay, const float sustain, const float release)
+{
+ adsrParams.attack = attack;
+ adsrParams.decay = decay;
+ adsrParams.sustain = sustain;
+ adsrParams.release = release;
adsr.setParameters (adsrParams);
-
- isPrepared = true;
}
void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
@@ -80,9 +83,6 @@ void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int
adsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples());
- if (startSample != 0)
- jassertfalse;
-
for (int channel = 0; channel < outputBuffer.getNumChannels(); ++channel)
{
outputBuffer.addFrom (channel, startSample, synthBuffer, channel, 0, numSamples);
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index 1ee7b23..6f43fad 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -24,12 +24,14 @@ public:
void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
void renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples) override;
+ void updateADSR (const float attack, const float decay, const float sustain, const float release);
+
private:
juce::ADSR adsr;
juce::ADSR::Parameters adsrParams;
juce::AudioBuffer synthBuffer;
- juce::dsp::Oscillator osc { [](float x) { return std::sin (x); }};
+ juce::dsp::Oscillator osc { [](float x) { return x / juce::MathConstants::pi; }};
juce::dsp::Gain gain;
bool isPrepared { false };
```
Diff 4: be86a6aca2a09395ab10c3c820a5424a263862b8 to fed9a4238122b2ce7b3ddb5e05819c90a42ca965
diff --git a/Source/Data/AdsrData.cpp b/Source/Data/AdsrData.cpp
new file mode 100644
```diff
index 0000000..d0ef3c4
--- /dev/null
+++ b/Source/Data/AdsrData.cpp
@@ -0,0 +1,21 @@
+/*
+ ==============================================================================
+
+ AdsrData.cpp
+ Created: 7 Feb 2021 2:29:21pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include "AdsrData.h"
+
+void AdsrData::updateADSR (const float attack, const float decay, const float sustain, const float release)
+{
+ adsrParams.attack = attack;
+ adsrParams.decay = decay;
+ adsrParams.sustain = sustain;
+ adsrParams.release = release;
+
+ setParameters (adsrParams);
+}
```
diff --git a/Source/Data/AdsrData.h b/Source/Data/AdsrData.h
new file mode 100644
```diff
index 0000000..e4b9605
--- /dev/null
+++ b/Source/Data/AdsrData.h
@@ -0,0 +1,22 @@
+/*
+ ==============================================================================
+
+ AdsrData.h
+ Created: 7 Feb 2021 2:29:21pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+class AdsrData : public juce::ADSR
+{
+public:
+ void updateADSR (const float attack, const float decay, const float sustain, const float release);
+
+private:
+ juce::ADSR::Parameters adsrParams;
+};
```
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index f535bbf..e4ba37c 100644
```diff
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -11,23 +11,13 @@
//==============================================================================
TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcessor& p)
- : AudioProcessorEditor (&p), audioProcessor (p)
+ : AudioProcessorEditor (&p), audioProcessor (p), adsr (audioProcessor.apvts)
{
setSize (400, 300);
- using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
-
- attackAttachment = std::make_unique(audioProcessor.apvts, "ATTACK", attackSlider);
- decayAttachment = std::make_unique(audioProcessor.apvts, "DECAY", decaySlider);
- sustainAttachment = std::make_unique(audioProcessor.apvts, "SUSTAIN", sustainSlider);
- releaseAttachment = std::make_unique(audioProcessor.apvts, "RELEASE", releaseSlider);
-
oscSelAttachment = std::make_unique(audioProcessor.apvts, "OSC", oscSelector);
- setSliderParams (attackSlider);
- setSliderParams (decaySlider);
- setSliderParams (sustainSlider);
- setSliderParams (releaseSlider);
+ addAndMakeVisible (adsr);
}
TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
@@ -42,22 +32,7 @@ void TapSynthAudioProcessorEditor::paint (juce::Graphics& g)
void TapSynthAudioProcessorEditor::resized()
{
- const auto bounds = getLocalBounds().reduced (10);
- const auto padding = 10;
- const auto sliderWidth = bounds.getWidth() / 4 - padding;
- const auto sliderHeight = bounds.getWidth() / 4 - padding;
- const auto sliderStartX = 0;
- const auto sliderStartY = bounds.getHeight() / 2 - (sliderHeight / 2);
-
- attackSlider.setBounds (sliderStartX, sliderStartY, sliderWidth, sliderHeight);
- decaySlider.setBounds (attackSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
- sustainSlider.setBounds (decaySlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
- releaseSlider.setBounds (sustainSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+ adsr.setBounds (getWidth() / 2, 0, getWidth() / 2, getHeight());
}
-void TapSynthAudioProcessorEditor::setSliderParams (juce::Slider& slider)
-{
- slider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
- slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
- addAndMakeVisible (slider);
-}
+
```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h
index cfe4fca..9f78696 100644
```diff
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -10,6 +10,7 @@
#include
#include "PluginProcessor.h"
+#include "UI/AdsrComponent.h"
//==============================================================================
/**
@@ -25,23 +26,10 @@ public:
void resized() override;
private:
- void setSliderParams (juce::Slider& slider);
-
- juce::Slider attackSlider;
- juce::Slider decaySlider;
- juce::Slider sustainSlider;
- juce::Slider releaseSlider;
juce::ComboBox oscSelector;
-
- using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
-
- std::unique_ptr attackAttachment;
- std::unique_ptr decayAttachment;
- std::unique_ptr sustainAttachment;
- std::unique_ptr releaseAttachment;
std::unique_ptr oscSelAttachment;
-
TapSynthAudioProcessor& audioProcessor;
+ AdsrComponent adsr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
};
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 538500b..9163db2 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -159,7 +159,7 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
auto& sustain = *apvts.getRawParameterValue ("SUSTAIN");
auto& release = *apvts.getRawParameterValue ("RELEASE");
- voice->updateADSR (attack.load(), decay.load(), sustain.load(), release.load());
+ voice->update (attack.load(), decay.load(), sustain.load(), release.load());
}
}
```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp
index a1a2e74..90a8185 100644
```diff
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -57,14 +57,9 @@ void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outp
isPrepared = true;
}
-void SynthVoice::updateADSR (const float attack, const float decay, const float sustain, const float release)
+void SynthVoice::update (const float attack, const float decay, const float sustain, const float release)
{
- adsrParams.attack = attack;
- adsrParams.decay = decay;
- adsrParams.sustain = sustain;
- adsrParams.release = release;
-
- adsr.setParameters (adsrParams);
+ adsr.updateADSR (attack, decay, sustain, release);
}
void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index 6f43fad..972ec6e 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -12,6 +12,7 @@
#include
#include "SynthSound.h"
+#include "Data/AdsrData.h"
class SynthVoice : public juce::SynthesiserVoice
{
@@ -24,11 +25,10 @@ public:
void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
void renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples) override;
- void updateADSR (const float attack, const float decay, const float sustain, const float release);
+ void update (const float attack, const float decay, const float sustain, const float release);
private:
- juce::ADSR adsr;
- juce::ADSR::Parameters adsrParams;
+ AdsrData adsr;
juce::AudioBuffer synthBuffer;
juce::dsp::Oscillator osc { [](float x) { return x / juce::MathConstants::pi; }};
```
diff --git a/Source/UI/AdsrComponent.cpp b/Source/UI/AdsrComponent.cpp
new file mode 100644
```diff
index 0000000..3147550
--- /dev/null
+++ b/Source/UI/AdsrComponent.cpp
@@ -0,0 +1,59 @@
+/*
+ ==============================================================================
+
+ AdsrComponent.cpp
+ Created: 7 Feb 2021 2:28:49pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include
+#include "AdsrComponent.h"
+
+//==============================================================================
+AdsrComponent::AdsrComponent (juce::AudioProcessorValueTreeState& apvts)
+{
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ attackAttachment = std::make_unique(apvts, "ATTACK", attackSlider);
+ decayAttachment = std::make_unique(apvts, "DECAY", decaySlider);
+ sustainAttachment = std::make_unique(apvts, "SUSTAIN", sustainSlider);
+ releaseAttachment = std::make_unique(apvts, "RELEASE", releaseSlider);
+
+ setSliderParams (attackSlider);
+ setSliderParams (decaySlider);
+ setSliderParams (sustainSlider);
+ setSliderParams (releaseSlider);
+}
+
+AdsrComponent::~AdsrComponent()
+{
+}
+
+void AdsrComponent::paint (juce::Graphics& g)
+{
+ g.fillAll (juce::Colours::black);
+}
+
+void AdsrComponent::resized()
+{
+ const auto bounds = getLocalBounds().reduced (10);
+ const auto padding = 10;
+ const auto sliderWidth = bounds.getWidth() / 4 - padding;
+ const auto sliderHeight = bounds.getHeight();
+ const auto sliderStartX = 0;
+ const auto sliderStartY = 0;
+
+ attackSlider.setBounds (sliderStartX, sliderStartY, sliderWidth, sliderHeight);
+ decaySlider.setBounds (attackSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+ sustainSlider.setBounds (decaySlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+ releaseSlider.setBounds (sustainSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+}
+
+void AdsrComponent::setSliderParams (juce::Slider& slider)
+{
+ slider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
+ slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
+ addAndMakeVisible (slider);
+}
```
diff --git a/Source/UI/AdsrComponent.h b/Source/UI/AdsrComponent.h
new file mode 100644
```diff
index 0000000..703a366
--- /dev/null
+++ b/Source/UI/AdsrComponent.h
@@ -0,0 +1,43 @@
+/*
+ ==============================================================================
+
+ AdsrComponent.h
+ Created: 7 Feb 2021 2:28:49pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+//==============================================================================
+/*
+*/
+class AdsrComponent : public juce::Component
+{
+public:
+ AdsrComponent (juce::AudioProcessorValueTreeState& apvts);
+ ~AdsrComponent() override;
+
+ void paint (juce::Graphics&) override;
+ void resized() override;
+
+private:
+ void setSliderParams (juce::Slider& slider);
+
+ juce::Slider attackSlider;
+ juce::Slider decaySlider;
+ juce::Slider sustainSlider;
+ juce::Slider releaseSlider;
+
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ std::unique_ptr attackAttachment;
+ std::unique_ptr decayAttachment;
+ std::unique_ptr sustainAttachment;
+ std::unique_ptr releaseAttachment;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AdsrComponent)
+};
```
diff --git a/tapSynth.jucer b/tapSynth.jucer
index 8ea6bb5..a8f0f5c 100644
```diff
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -14,6 +14,15 @@
+
+
+
+
+
+
+
+
```
Diff 5: fed9a4238122b2ce7b3ddb5e05819c90a42ca965 to fa658b7c0faee65d2f4b191dd8089a5c602c6a40
diff --git a/Source/Data/AdsrData.cpp b/Source/Data/AdsrData.cpp
index d0ef3c4..0d89597 100644
```diff
--- a/Source/Data/AdsrData.cpp
+++ b/Source/Data/AdsrData.cpp
@@ -10,7 +10,7 @@
#include "AdsrData.h"
-void AdsrData::updateADSR (const float attack, const float decay, const float sustain, const float release)
+void AdsrData::update (const float attack, const float decay, const float sustain, const float release)
{
adsrParams.attack = attack;
adsrParams.decay = decay;
```
diff --git a/Source/Data/AdsrData.h b/Source/Data/AdsrData.h
index e4b9605..d7087b8 100644
```diff
--- a/Source/Data/AdsrData.h
+++ b/Source/Data/AdsrData.h
@@ -15,7 +15,7 @@
class AdsrData : public juce::ADSR
{
public:
- void updateADSR (const float attack, const float decay, const float sustain, const float release);
+ void update (const float attack, const float decay, const float sustain, const float release);
private:
juce::ADSR::Parameters adsrParams;
```
diff --git a/Source/Data/OscData.cpp b/Source/Data/OscData.cpp
new file mode 100644
```diff
index 0000000..ad99bd3
--- /dev/null
+++ b/Source/Data/OscData.cpp
@@ -0,0 +1,72 @@
+/*
+ ==============================================================================
+
+ OscData.cpp
+ Created: 14 Feb 2021 6:51:17pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include "OscData.h"
+
+void OscData::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels)
+{
+ juce::dsp::ProcessSpec spec;
+ spec.maximumBlockSize = samplesPerBlock;
+ spec.sampleRate = sampleRate;
+ spec.numChannels = outputChannels;
+
+ prepare (spec);
+ gain.prepare (spec);
+}
+
+void OscData::setType (const int oscSelection)
+{
+ switch (oscSelection)
+ {
+ // Sine
+ case 0:
+ initialise ([](float x) { return std::sin (x); });
+ break;
+
+ // Saw
+ case 1:
+ initialise ([] (float x) { return x / juce::MathConstants::pi; });
+ break;
+
+ // Square
+ case 2:
+ initialise ([] (float x) { return x < 0.0f ? -1.0f : 1.0f; });
+ break;
+
+ default:
+ // You shouldn't be here!
+ jassertfalse;
+ break;
+ }
+}
+
+void OscData::setGain (const float levelInDecibels)
+{
+ gain.setGainDecibels(levelInDecibels);
+}
+
+void OscData::setPitchVal (const int pitch)
+{
+ pitchVal = pitch;
+ setFrequency (juce::MidiMessage::getMidiNoteInHertz (lastMidiNote + pitchVal));
+}
+
+void OscData::setFreq (const int midiNoteNumber)
+{
+ setFrequency (juce::MidiMessage::getMidiNoteInHertz (midiNoteNumber + pitchVal));
+ lastMidiNote = midiNoteNumber;
+}
+
+void OscData::renderNextBlock (juce::dsp::AudioBlock& audioBlock)
+{
+ jassert (audioBlock.getNumSamples() > 0);
+ process (juce::dsp::ProcessContextReplacing (audioBlock));
+ gain.process (juce::dsp::ProcessContextReplacing (audioBlock));
+}
```
diff --git a/Source/Data/OscData.h b/Source/Data/OscData.h
new file mode 100644
```diff
index 0000000..83a6c52
--- /dev/null
+++ b/Source/Data/OscData.h
@@ -0,0 +1,33 @@
+/*
+ ==============================================================================
+
+ OscData.h
+ Created: 14 Feb 2021 6:51:17pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+class OscData : public juce::dsp::Oscillator
+{
+public:
+ void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
+ void setType (const int oscSelection);
+ void setGain (const float levelInDecibels);
+ void setPitchVal (const int pitch);
+ void setFreq (const int midiNoteNumber);
+ void renderNextBlock (juce::dsp::AudioBlock& audioBlock);
+
+private:
+ juce::dsp::Gain gain;
+ int pitchVal { 0 };
+ int lastMidiNote { 0 };
+};
+
+// return std::sin (x); //Sine Wave
+// return x / MathConstants::pi; // Saw Wave
+// return x < 0.0f ? -1.0f : 1.0f; // Square Wave
```
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index e4ba37c..07f155c 100644
```diff
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -11,12 +11,11 @@
//==============================================================================
TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcessor& p)
- : AudioProcessorEditor (&p), audioProcessor (p), adsr (audioProcessor.apvts)
+ : AudioProcessorEditor (&p), audioProcessor (p), osc1 (audioProcessor.apvts, "OSC1", "OSC1GAIN", "OSC1PITCH"), osc2 (audioProcessor.apvts, "OSC2", "OSC2GAIN", "OSC2PITCH"), adsr (audioProcessor.apvts)
{
setSize (400, 300);
-
- oscSelAttachment = std::make_unique(audioProcessor.apvts, "OSC", oscSelector);
-
+ addAndMakeVisible (osc1);
+ addAndMakeVisible (osc2);
addAndMakeVisible (adsr);
}
@@ -33,6 +32,8 @@ void TapSynthAudioProcessorEditor::paint (juce::Graphics& g)
void TapSynthAudioProcessorEditor::resized()
{
adsr.setBounds (getWidth() / 2, 0, getWidth() / 2, getHeight());
+ osc1.setBounds (0, 25, getWidth() / 2, 100);
+ osc2.setBounds (0, 150, getHeight() / 2, 100);
}
```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h
index 9f78696..d789924 100644
```diff
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -10,6 +10,7 @@
#include
#include "PluginProcessor.h"
+#include "UI/OscComponent.h"
#include "UI/AdsrComponent.h"
//==============================================================================
@@ -26,9 +27,9 @@ public:
void resized() override;
private:
- juce::ComboBox oscSelector;
- std::unique_ptr oscSelAttachment;
TapSynthAudioProcessor& audioProcessor;
+ OscComponent osc1;
+ OscComponent osc2;
AdsrComponent adsr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 9163db2..8297f48 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -23,7 +23,12 @@ TapSynthAudioProcessor::TapSynthAudioProcessor()
#endif
{
synth.addSound (new SynthSound());
- synth.addVoice (new SynthVoice());
+ //synth.addVoice (new SynthVoice());
+
+ for (int i = 0; i < 5; i++)
+ {
+ synth.addVoice (new SynthVoice());
+ }
}
TapSynthAudioProcessor::~TapSynthAudioProcessor()
@@ -150,16 +155,31 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
{
if (auto voice = dynamic_cast(synth.getVoice(i)))
{
- // Osc controls
- // ADSR
- // LFO
-
auto& attack = *apvts.getRawParameterValue ("ATTACK");
auto& decay = *apvts.getRawParameterValue ("DECAY");
auto& sustain = *apvts.getRawParameterValue ("SUSTAIN");
auto& release = *apvts.getRawParameterValue ("RELEASE");
- voice->update (attack.load(), decay.load(), sustain.load(), release.load());
+ auto& osc1Choice = *apvts.getRawParameterValue ("OSC1");
+ auto& osc2Choice = *apvts.getRawParameterValue ("OSC2");
+ auto& osc1Gain = *apvts.getRawParameterValue ("OSC1GAIN");
+ auto& osc2Gain = *apvts.getRawParameterValue ("OSC2GAIN");
+ auto& osc1Pitch = *apvts.getRawParameterValue ("OSC1PITCH");
+ auto& osc2Pitch = *apvts.getRawParameterValue ("OSC2PITCH");
+
+ auto& osc1 = voice->getOscillator1();
+ auto& osc2 = voice->getOscillator2();
+ auto& adsr = voice->getAdsr();
+
+ osc1.setType (osc1Choice);
+ osc1.setGain (osc1Gain);
+ osc1.setPitchVal (osc1Pitch);
+
+ osc2.setType (osc2Choice);
+ osc2.setGain (osc2Gain);
+ osc2.setPitchVal (osc2Pitch);
+
+ adsr.update (attack.load(), decay.load(), sustain.load(), release.load());
}
}
@@ -203,7 +223,16 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
std::vector> params;
// OSC select
- params.push_back (std::make_unique ("OSC", "Oscillator", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+ params.push_back (std::make_unique ("OSC1", "Oscillator 1", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+ params.push_back (std::make_unique ("OSC2", "Oscillator 2", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+
+ // OSC Gain
+ params.push_back (std::make_unique("OSC1GAIN", "Oscillator 1 Gain", juce::NormalisableRange { -40.0f, 0.2f, }, 0.1f, "dB"));
+ params.push_back (std::make_unique("OSC2GAIN", "Oscillator 2 Gain", juce::NormalisableRange { -40.0f, 0.2f, }, 0.1f, "dB"));
+
+ // OSC Pitch val
+ params.push_back (std::make_unique("OSC1PITCH", "Oscillator 1 Pitch", -48, 48, 0));
+ params.push_back (std::make_unique("OSC2PITCH", "Oscillator 2 Pitch", -48, 48, 0));
// ADSR
params.push_back (std::make_unique("ATTACK", "Attack", juce::NormalisableRange { 0.1f, 1.0f, }, 0.1f));
```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp
index 90a8185..7bbc7f7 100644
```diff
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -18,7 +18,8 @@ bool SynthVoice::canPlaySound (juce::SynthesiserSound* sound)
void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound *sound, int currentPitchWheelPosition)
{
- osc.setFrequency (juce::MidiMessage::getMidiNoteInHertz (midiNoteNumber));
+ osc1.setFreq (midiNoteNumber);
+ osc2.setFreq (midiNoteNumber);
adsr.noteOn();
}
@@ -49,19 +50,15 @@ void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outp
spec.sampleRate = sampleRate;
spec.numChannels = outputChannels;
- osc.prepare (spec);
- gain.prepare (spec);
+ osc1.prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
+ osc2.prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
- gain.setGainLinear (0.3f);
+ gain.prepare (spec);
+ gain.setGainLinear (0.07f);
isPrepared = true;
}
-void SynthVoice::update (const float attack, const float decay, const float sustain, const float release)
-{
- adsr.updateADSR (attack, decay, sustain, release);
-}
-
void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples)
{
jassert (isPrepared);
@@ -71,9 +68,11 @@ void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int
synthBuffer.setSize (outputBuffer.getNumChannels(), numSamples, false, false, true);
synthBuffer.clear();
-
juce::dsp::AudioBlock audioBlock { synthBuffer };
- osc.process (juce::dsp::ProcessContextReplacing (audioBlock));
+
+ osc1.renderNextBlock (audioBlock);
+ osc2.renderNextBlock (audioBlock);
+
gain.process (juce::dsp::ProcessContextReplacing (audioBlock));
adsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples());
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index 972ec6e..afd4876 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -12,6 +12,7 @@
#include
#include "SynthSound.h"
+#include "Data/OscData.h"
#include "Data/AdsrData.h"
class SynthVoice : public juce::SynthesiserVoice
@@ -25,17 +26,16 @@ public:
void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
void renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples) override;
- void update (const float attack, const float decay, const float sustain, const float release);
+ OscData& getOscillator1() { return osc1; }
+ OscData& getOscillator2() { return osc2; }
+ AdsrData& getAdsr() { return adsr; }
private:
+ OscData osc1;
+ OscData osc2;
AdsrData adsr;
juce::AudioBuffer synthBuffer;
- juce::dsp::Oscillator osc { [](float x) { return x / juce::MathConstants::pi; }};
juce::dsp::Gain gain;
bool isPrepared { false };
-
- // return std::sin (x); //Sine Wave
- // return x / MathConstants::pi; // Saw Wave
- // return x < 0.0f ? -1.0f : 1.0f; // Square Wave
};
```
diff --git a/Source/UI/AdsrComponent.h b/Source/UI/AdsrComponent.h
index 703a366..03e5071 100644
```diff
--- a/Source/UI/AdsrComponent.h
+++ b/Source/UI/AdsrComponent.h
@@ -15,7 +15,7 @@
//==============================================================================
/*
*/
-class AdsrComponent : public juce::Component
+class AdsrComponent : public juce::Component
{
public:
AdsrComponent (juce::AudioProcessorValueTreeState& apvts);
```
diff --git a/Source/UI/OscComponent.cpp b/Source/UI/OscComponent.cpp
new file mode 100644
```diff
index 0000000..ff59131
--- /dev/null
+++ b/Source/UI/OscComponent.cpp
@@ -0,0 +1,52 @@
+/*
+ ==============================================================================
+
+ OscComponent.cpp
+ Created: 14 Feb 2021 6:51:39pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include
+#include "OscComponent.h"
+
+//==============================================================================
+OscComponent::OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId)
+{
+ juce::StringArray oscChoices { "Sine", "Saw", "Square" };
+ oscSelector.addItemList (oscChoices, 1);
+ oscSelector.setSelectedItemIndex (0);
+ addAndMakeVisible (oscSelector);
+
+ gainSlider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
+ gainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
+ addAndMakeVisible (gainSlider);
+
+ pitchSlider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
+ pitchSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
+ addAndMakeVisible (pitchSlider);
+
+ oscSelAttachment = std::make_unique(apvts, oscId, oscSelector);
+
+ gainAttachment = std::make_unique(apvts, gainId, gainSlider);
+
+ pitchAttachment = std::make_unique(apvts, pitchId, pitchSlider);
+}
+
+OscComponent::~OscComponent()
+{
+}
+
+void OscComponent::paint (juce::Graphics& g)
+{
+ g.fillAll (juce::Colours::black);
+}
+
+void OscComponent::resized()
+{
+ auto bounds = getLocalBounds();
+ oscSelector.setBounds (bounds.removeFromLeft (getWidth() / 2));
+ gainSlider.setBounds (bounds.removeFromLeft(getWidth() / 4));
+ pitchSlider.setBounds (bounds);
+}
```
diff --git a/Source/UI/OscComponent.h b/Source/UI/OscComponent.h
new file mode 100644
```diff
index 0000000..1efbedf
--- /dev/null
+++ b/Source/UI/OscComponent.h
@@ -0,0 +1,36 @@
+/*
+ ==============================================================================
+
+ OscComponent.h
+ Created: 14 Feb 2021 6:51:39pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+//==============================================================================
+/*
+*/
+class OscComponent : public juce::Component
+{
+public:
+ OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId);
+ ~OscComponent() override;
+
+ void paint (juce::Graphics&) override;
+ void resized() override;
+
+private:
+ juce::ComboBox oscSelector;
+ juce::Slider gainSlider;
+ juce::Slider pitchSlider;
+ std::unique_ptr oscSelAttachment;
+ std::unique_ptr gainAttachment;
+ std::unique_ptr pitchAttachment;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscComponent)
+};
```
diff --git a/tapSynth.jucer b/tapSynth.jucer
index a8f0f5c..6c2d6f1 100644
```diff
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -17,11 +17,16 @@
+
+
+
+
```
Diff 6: fa658b7c0faee65d2f4b191dd8089a5c602c6a40 to 35915fe2d7e0e8ede1372eed6062820dcddb47f7
diff --git a/Source/Data/OscData.cpp b/Source/Data/OscData.cpp
index ad99bd3..6c41eb3 100644
```diff
--- a/Source/Data/OscData.cpp
+++ b/Source/Data/OscData.cpp
@@ -18,6 +18,7 @@ void OscData::prepareToPlay (double sampleRate, int samplesPerBlock, int outputC
spec.numChannels = outputChannels;
prepare (spec);
+ fmOsc.prepare (spec);
gain.prepare (spec);
}
@@ -52,21 +53,35 @@ void OscData::setGain (const float levelInDecibels)
gain.setGainDecibels(levelInDecibels);
}
-void OscData::setPitchVal (const int pitch)
+void OscData::setOscPitch (const int pitch)
{
- pitchVal = pitch;
- setFrequency (juce::MidiMessage::getMidiNoteInHertz (lastMidiNote + pitchVal));
+ lastPitch = pitch;
+ setFrequency (juce::MidiMessage::getMidiNoteInHertz ((lastMidiNote + lastPitch) + fmModulator));
+
}
void OscData::setFreq (const int midiNoteNumber)
{
- setFrequency (juce::MidiMessage::getMidiNoteInHertz (midiNoteNumber + pitchVal));
+ setFrequency (juce::MidiMessage::getMidiNoteInHertz ((midiNoteNumber + lastPitch) + fmModulator));
lastMidiNote = midiNoteNumber;
}
+void OscData::setFmOsc (const float freq, const float depth)
+{
+ fmDepth = depth;
+ fmOsc.setFrequency (freq);
+ setFrequency (juce::MidiMessage::getMidiNoteInHertz ((lastMidiNote + lastPitch) + fmModulator));
+}
+
void OscData::renderNextBlock (juce::dsp::AudioBlock& audioBlock)
{
jassert (audioBlock.getNumSamples() > 0);
process (juce::dsp::ProcessContextReplacing (audioBlock));
gain.process (juce::dsp::ProcessContextReplacing (audioBlock));
}
+
+float OscData::processNextSample (float input)
+{
+ fmModulator = fmOsc.processSample (input) * fmDepth;
+ return gain.processSample (processSample (input));
+}
```
diff --git a/Source/Data/OscData.h b/Source/Data/OscData.h
index 83a6c52..5380444 100644
```diff
--- a/Source/Data/OscData.h
+++ b/Source/Data/OscData.h
@@ -18,14 +18,19 @@ public:
void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
void setType (const int oscSelection);
void setGain (const float levelInDecibels);
- void setPitchVal (const int pitch);
+ void setOscPitch (const int pitch);
void setFreq (const int midiNoteNumber);
+ void setFmOsc (const float freq, const float depth);
void renderNextBlock (juce::dsp::AudioBlock& audioBlock);
+ float processNextSample (float input);
private:
+ juce::dsp::Oscillator fmOsc { [](float x) { return std::sin (x); }};
juce::dsp::Gain gain;
- int pitchVal { 0 };
+ int lastPitch { 0 };
int lastMidiNote { 0 };
+ float fmDepth { 0.0f };
+ float fmModulator { 0.0f };
};
// return std::sin (x); //Sine Wave
```
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index 07f155c..05181ab 100644
```diff
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -11,12 +11,19 @@
//==============================================================================
TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcessor& p)
- : AudioProcessorEditor (&p), audioProcessor (p), osc1 (audioProcessor.apvts, "OSC1", "OSC1GAIN", "OSC1PITCH"), osc2 (audioProcessor.apvts, "OSC2", "OSC2GAIN", "OSC2PITCH"), adsr (audioProcessor.apvts)
+: AudioProcessorEditor (&p)
+, audioProcessor (p)
+, osc1 (audioProcessor.apvts, "OSC1", "OSC1GAIN", "OSC1PITCH", "OSC1FMFREQ", "OSC1FMDEPTH")
+, osc2 (audioProcessor.apvts, "OSC2", "OSC2GAIN", "OSC2PITCH", "OSC2FMFREQ", "OSC2FMDEPTH")
+, adsr (audioProcessor.apvts, "ATTACK", "DECAY", "SUSTAIN", "RELEASE")
{
- setSize (400, 300);
+ setSize (650, 240);
addAndMakeVisible (osc1);
addAndMakeVisible (osc2);
addAndMakeVisible (adsr);
+
+ osc1.setName ("Oscillator 1");
+ osc2.setName ("Oscillator 2");
}
TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
@@ -31,9 +38,11 @@ void TapSynthAudioProcessorEditor::paint (juce::Graphics& g)
void TapSynthAudioProcessorEditor::resized()
{
- adsr.setBounds (getWidth() / 2, 0, getWidth() / 2, getHeight());
- osc1.setBounds (0, 25, getWidth() / 2, 100);
- osc2.setBounds (0, 150, getHeight() / 2, 100);
+ const auto oscWidth = 420;
+ const auto oscHeight = 120;
+ osc1.setBounds (0, 0, oscWidth, oscHeight);
+ osc2.setBounds (0, osc1.getBottom(), oscWidth, oscHeight);
+ adsr.setBounds (osc1.getRight(), 0, 230, 240);
}
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 8297f48..31dcc48 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -166,19 +166,28 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
auto& osc2Gain = *apvts.getRawParameterValue ("OSC2GAIN");
auto& osc1Pitch = *apvts.getRawParameterValue ("OSC1PITCH");
auto& osc2Pitch = *apvts.getRawParameterValue ("OSC2PITCH");
-
+ auto& osc1FmFreq = *apvts.getRawParameterValue ("OSC1FMFREQ");
+ auto& osc2FmFreq = *apvts.getRawParameterValue ("OSC2FMFREQ");
+ auto& osc1FmDepth = *apvts.getRawParameterValue ("OSC1FMDEPTH");
+ auto& osc2FmDepth = *apvts.getRawParameterValue ("OSC2FMDEPTH");
+
auto& osc1 = voice->getOscillator1();
auto& osc2 = voice->getOscillator2();
auto& adsr = voice->getAdsr();
+
+ for (int i = 0; i < 2; i++)
+ {
+ osc1[i].setType (osc1Choice);
+ osc1[i].setGain (osc1Gain);
+ osc1[i].setOscPitch (osc1Pitch);
+ osc1[i].setFmOsc (osc1FmFreq, osc1FmDepth);
+
+ osc2[i].setType (osc2Choice);
+ osc2[i].setGain (osc2Gain);
+ osc2[i].setOscPitch (osc2Pitch);
+ osc2[i].setFmOsc (osc2FmFreq, osc2FmDepth);
+ }
- osc1.setType (osc1Choice);
- osc1.setGain (osc1Gain);
- osc1.setPitchVal (osc1Pitch);
-
- osc2.setType (osc2Choice);
- osc2.setGain (osc2Gain);
- osc2.setPitchVal (osc2Pitch);
-
adsr.update (attack.load(), decay.load(), sustain.load(), release.load());
}
}
@@ -227,18 +236,26 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
params.push_back (std::make_unique ("OSC2", "Oscillator 2", juce::StringArray { "Sine", "Saw", "Square" }, 0));
// OSC Gain
- params.push_back (std::make_unique("OSC1GAIN", "Oscillator 1 Gain", juce::NormalisableRange { -40.0f, 0.2f, }, 0.1f, "dB"));
- params.push_back (std::make_unique("OSC2GAIN", "Oscillator 2 Gain", juce::NormalisableRange { -40.0f, 0.2f, }, 0.1f, "dB"));
+ params.push_back (std::make_unique("OSC1GAIN", "Oscillator 1 Gain", juce::NormalisableRange { -40.0f, 0.2f, 0.1f }, 0.1f, "dB"));
+ params.push_back (std::make_unique("OSC2GAIN", "Oscillator 2 Gain", juce::NormalisableRange { -40.0f, 0.2f, 0.1f }, 0.1f, "dB"));
// OSC Pitch val
params.push_back (std::make_unique("OSC1PITCH", "Oscillator 1 Pitch", -48, 48, 0));
params.push_back (std::make_unique("OSC2PITCH", "Oscillator 2 Pitch", -48, 48, 0));
+ // FM Osc Freq
+ params.push_back (std::make_unique("OSC1FMFREQ", "Oscillator 1 FM Frequency", juce::NormalisableRange { 0.0f, 1000.0f, 0.1f }, 0.0f, "Hz"));
+ params.push_back (std::make_unique("OSC2FMFREQ", "Oscillator 1 FM Frequency", juce::NormalisableRange { 0.0f, 1000.0f, 0.1f }, 0.0f, "Hz"));
+
+ // FM Osc Depth
+ params.push_back (std::make_unique("OSC1FMDEPTH", "Oscillator 1 FM Depth", juce::NormalisableRange { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique("OSC2FMDEPTH", "Oscillator 2 FM Depth", juce::NormalisableRange { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+
// ADSR
- params.push_back (std::make_unique("ATTACK", "Attack", juce::NormalisableRange { 0.1f, 1.0f, }, 0.1f));
- params.push_back (std::make_unique("DECAY", "Decay", juce::NormalisableRange { 0.1f, 1.0f, }, 0.1f));
- params.push_back (std::make_unique("SUSTAIN", "Sustain", juce::NormalisableRange { 0.1f, 1.0f, }, 1.0f));
- params.push_back (std::make_unique("RELEASE", "Release", juce::NormalisableRange { 0.1f, 3.0f, }, 0.4f));
+ params.push_back (std::make_unique("ATTACK", "Attack", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique("DECAY", "Decay", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique("SUSTAIN", "Sustain", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 1.0f));
+ params.push_back (std::make_unique("RELEASE", "Release", juce::NormalisableRange { 0.1f, 3.0f, 0.1f }, 0.4f));
return { params.begin(), params.end() };
}
```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp
index 7bbc7f7..893ec30 100644
```diff
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -18,8 +18,12 @@ bool SynthVoice::canPlaySound (juce::SynthesiserSound* sound)
void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::SynthesiserSound *sound, int currentPitchWheelPosition)
{
- osc1.setFreq (midiNoteNumber);
- osc2.setFreq (midiNoteNumber);
+ for (int i = 0; i < 2; i++)
+ {
+ osc1[i].setFreq (midiNoteNumber);
+ osc2[i].setFreq (midiNoteNumber);
+ }
+
adsr.noteOn();
}
@@ -50,8 +54,11 @@ void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outp
spec.sampleRate = sampleRate;
spec.numChannels = outputChannels;
- osc1.prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
- osc2.prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
+ for (int i = 0; i < 2; i++)
+ {
+ osc1[i].prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
+ osc2[i].prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
+ }
gain.prepare (spec);
gain.setGainLinear (0.07f);
@@ -68,13 +75,21 @@ void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int
synthBuffer.setSize (outputBuffer.getNumChannels(), numSamples, false, false, true);
synthBuffer.clear();
+
+ for (int i = 0; i < synthBuffer.getNumChannels(); ++i)
+ {
+ auto* buffer = synthBuffer.getWritePointer (i, 0);
+
+ for (int j = 0; j < synthBuffer.getNumSamples(); ++j)
+ {
+ buffer[j] = osc1[i].processNextSample (buffer[j]) + osc2[i].processNextSample (buffer[j]);
+ }
+ }
+
juce::dsp::AudioBlock audioBlock { synthBuffer };
- osc1.renderNextBlock (audioBlock);
- osc2.renderNextBlock (audioBlock);
-
gain.process (juce::dsp::ProcessContextReplacing (audioBlock));
-
+
adsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples());
for (int channel = 0; channel < outputBuffer.getNumChannels(); ++channel)
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index afd4876..ff8924f 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -26,13 +26,15 @@ public:
void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
void renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples) override;
- OscData& getOscillator1() { return osc1; }
- OscData& getOscillator2() { return osc2; }
+ std::array& getOscillator1() { return osc1; }
+ std::array& getOscillator2() { return osc2; }
AdsrData& getAdsr() { return adsr; }
private:
- OscData osc1;
- OscData osc2;
+// OscData osc1;
+// OscData osc2;
+ std::array osc1;
+ std::array osc2;
AdsrData adsr;
juce::AudioBuffer synthBuffer;
```
diff --git a/Source/UI/AdsrComponent.cpp b/Source/UI/AdsrComponent.cpp
index 3147550..e03f69a 100644
```diff
--- a/Source/UI/AdsrComponent.cpp
+++ b/Source/UI/AdsrComponent.cpp
@@ -12,19 +12,12 @@
#include "AdsrComponent.h"
//==============================================================================
-AdsrComponent::AdsrComponent (juce::AudioProcessorValueTreeState& apvts)
+AdsrComponent::AdsrComponent (juce::AudioProcessorValueTreeState& apvts, juce::String attackId, juce::String decayId, juce::String sustainId, juce::String releaseId)
{
- using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
-
- attackAttachment = std::make_unique(apvts, "ATTACK", attackSlider);
- decayAttachment = std::make_unique(apvts, "DECAY", decaySlider);
- sustainAttachment = std::make_unique(apvts, "SUSTAIN", sustainSlider);
- releaseAttachment = std::make_unique(apvts, "RELEASE", releaseSlider);
-
- setSliderParams (attackSlider);
- setSliderParams (decaySlider);
- setSliderParams (sustainSlider);
- setSliderParams (releaseSlider);
+ setSliderParams (attackSlider, attackLabel, attackAttachment, attackId, apvts);
+ setSliderParams (decaySlider, decayLabel, decayAttachment, decayId, apvts);
+ setSliderParams (sustainSlider, sustainLabel, sustainAttachment, sustainId, apvts);
+ setSliderParams (releaseSlider, releaseLabel, releaseAttachment, releaseId, apvts);
}
AdsrComponent::~AdsrComponent()
@@ -34,26 +27,53 @@ AdsrComponent::~AdsrComponent()
void AdsrComponent::paint (juce::Graphics& g)
{
g.fillAll (juce::Colours::black);
+ auto bounds = getLocalBounds();
+ g.setColour (juce::Colours::white);
+ g.drawRoundedRectangle (bounds.toFloat().reduced (10.0f), 5.0f, 2.0f);
+
+ g.setColour (juce::Colours::yellow);
+ g.setFont (fontHeight);
+ g.setFont (g.getCurrentFont().boldened());
+ g.drawText ("Envelope", 20, 15, 100, 25, juce::Justification::left);
}
void AdsrComponent::resized()
{
const auto bounds = getLocalBounds().reduced (10);
- const auto padding = 10;
+ const auto padding = 5;
const auto sliderWidth = bounds.getWidth() / 4 - padding;
- const auto sliderHeight = bounds.getHeight();
- const auto sliderStartX = 0;
- const auto sliderStartY = 0;
+ const auto sliderHeight = bounds.getHeight() - 70;
+ const auto sliderStartX = 15;
+ const auto sliderStartY = 70;
+ const auto labelStartY = 55;
+ const auto labelWidth = 70;
+ const auto labelHeight = 18;
+ const auto labelOffset = 10;
+ attackLabel.setBounds (sliderStartX - labelOffset, labelStartY, labelWidth, labelHeight);
attackSlider.setBounds (sliderStartX, sliderStartY, sliderWidth, sliderHeight);
+
+ decayLabel.setBounds (attackSlider.getRight() + padding - labelOffset, labelStartY, labelWidth, labelHeight);
decaySlider.setBounds (attackSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+
+ sustainLabel.setBounds (decaySlider.getRight() + padding - labelOffset, labelStartY, labelWidth, labelHeight);
sustainSlider.setBounds (decaySlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
+
+ releaseLabel.setBounds (sustainSlider.getRight() + padding - labelOffset, labelStartY, labelWidth, labelHeight);
releaseSlider.setBounds (sustainSlider.getRight() + padding, sliderStartY, sliderWidth, sliderHeight);
}
-void AdsrComponent::setSliderParams (juce::Slider& slider)
+using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+void AdsrComponent::setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts)
{
slider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
- slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
+ slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, textBoxWidth, textBoxHeight);
addAndMakeVisible (slider);
+
+ label.setFont (fontHeight);
+ label.setJustificationType (juce::Justification::centred);
+ addAndMakeVisible (label);
+
+ attachment = std::make_unique(apvts, paramId, slider);
}
```
diff --git a/Source/UI/AdsrComponent.h b/Source/UI/AdsrComponent.h
index 03e5071..82b41f1 100644
```diff
--- a/Source/UI/AdsrComponent.h
+++ b/Source/UI/AdsrComponent.h
@@ -18,26 +18,35 @@
class AdsrComponent : public juce::Component
{
public:
- AdsrComponent (juce::AudioProcessorValueTreeState& apvts);
+ AdsrComponent (juce::AudioProcessorValueTreeState& apvts, juce::String attackId, juce::String decayId, juce::String sustainId, juce::String releaseId);
~AdsrComponent() override;
void paint (juce::Graphics&) override;
void resized() override;
private:
- void setSliderParams (juce::Slider& slider);
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ void setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts);
juce::Slider attackSlider;
juce::Slider decaySlider;
juce::Slider sustainSlider;
juce::Slider releaseSlider;
- using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
-
+ juce::Label attackLabel { "A", "A" };
+ juce::Label decayLabel { "D", "D" };
+ juce::Label sustainLabel { "S", "S" };
+ juce::Label releaseLabel { "R", "R" };
+
std::unique_ptr attackAttachment;
std::unique_ptr decayAttachment;
std::unique_ptr sustainAttachment;
std::unique_ptr releaseAttachment;
+ static constexpr float fontHeight { 15.0f };
+ static constexpr int textBoxWidth { 35 };
+ static constexpr int textBoxHeight { 20 };
+
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AdsrComponent)
};
```
diff --git a/Source/UI/OscComponent.cpp b/Source/UI/OscComponent.cpp
index ff59131..f5f3109 100644
```diff
--- a/Source/UI/OscComponent.cpp
+++ b/Source/UI/OscComponent.cpp
@@ -12,26 +12,19 @@
#include "OscComponent.h"
//==============================================================================
-OscComponent::OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId)
+OscComponent::OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId, juce::String fmFreqId, juce::String fmDepthId)
{
juce::StringArray oscChoices { "Sine", "Saw", "Square" };
oscSelector.addItemList (oscChoices, 1);
oscSelector.setSelectedItemIndex (0);
addAndMakeVisible (oscSelector);
- gainSlider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
- gainSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
- addAndMakeVisible (gainSlider);
-
- pitchSlider.setSliderStyle (juce::Slider::SliderStyle::LinearVertical);
- pitchSlider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, 50, 25);
- addAndMakeVisible (pitchSlider);
-
oscSelAttachment = std::make_unique(apvts, oscId, oscSelector);
- gainAttachment = std::make_unique(apvts, gainId, gainSlider);
-
- pitchAttachment = std::make_unique(apvts, pitchId, pitchSlider);
+ setSliderParams (gainSlider, gainLabel, gainAttachment, gainId, apvts);
+ setSliderParams (pitchSlider, pitchLabel, pitchAttachment, pitchId, apvts);
+ setSliderParams (fmFreqSlider, fmFreqLabel, fmFreqAttachment, fmFreqId, apvts);
+ setSliderParams (fmDepthSlider, fmDepthLabel, fmDepthAttachment, fmDepthId, apvts);
}
OscComponent::~OscComponent()
@@ -41,12 +34,48 @@ OscComponent::~OscComponent()
void OscComponent::paint (juce::Graphics& g)
{
g.fillAll (juce::Colours::black);
+ auto bounds = getLocalBounds();
+ g.setColour (juce::Colours::white);
+ g.drawRoundedRectangle (bounds.toFloat().reduced (10.0f), 5.0f, 2.0f);
+
+ g.setColour (juce::Colours::yellow);
+ g.setFont (fontHeight);
+ g.setFont (g.getCurrentFont().boldened());
+ g.drawText (name, 20, 15, 100, 25, juce::Justification::left);
}
void OscComponent::resized()
{
- auto bounds = getLocalBounds();
- oscSelector.setBounds (bounds.removeFromLeft (getWidth() / 2));
- gainSlider.setBounds (bounds.removeFromLeft(getWidth() / 4));
- pitchSlider.setBounds (bounds);
+ const auto dialSize = 70;
+ const auto labelWidth = 70;
+ const auto labelHeight = 18;
+
+ oscSelector.setBounds (18, 40, 100, 25);
+
+ gainLabel.setBounds (120, 15, labelWidth, labelHeight);
+ gainSlider.setBounds (120, 30, dialSize, dialSize);
+
+ pitchLabel.setBounds (190, 15, labelWidth, labelHeight);
+ pitchSlider.setBounds (190, 30, dialSize, dialSize);
+
+ fmFreqLabel.setBounds (260, 15, labelWidth, labelHeight);
+ fmFreqSlider.setBounds (260, 30, dialSize, dialSize);
+
+ fmDepthLabel.setBounds (330, 15, labelWidth, labelHeight);
+ fmDepthSlider.setBounds (330, 30, dialSize, dialSize);
+}
+
+using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+void OscComponent::setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts)
+{
+ slider.setSliderStyle (juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag);
+ slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, textBoxWidth, textBoxHeight);
+ addAndMakeVisible (slider);
+
+ label.setFont (fontHeight);
+ label.setJustificationType (juce::Justification::centred);
+ addAndMakeVisible (label);
+
+ attachment = std::make_unique(apvts, paramId, slider);
}
```
diff --git a/Source/UI/OscComponent.h b/Source/UI/OscComponent.h
index 1efbedf..c194ec3 100644
```diff
--- a/Source/UI/OscComponent.h
+++ b/Source/UI/OscComponent.h
@@ -18,19 +18,41 @@
class OscComponent : public juce::Component
{
public:
- OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId);
+ OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId, juce::String fmPitchId, juce::String fmFreqId);
~OscComponent() override;
void paint (juce::Graphics&) override;
void resized() override;
+
+ void setName (const juce::String n) { name = n; }
private:
+ using sliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ void setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr&, juce::String paramId, juce::AudioProcessorValueTreeState& apvts);
+
juce::ComboBox oscSelector;
juce::Slider gainSlider;
juce::Slider pitchSlider;
+ juce::Slider fmFreqSlider;
+ juce::Slider fmDepthSlider;
+
+ juce::Label gainLabel { "Gain", "Gain" };
+ juce::Label pitchLabel { "Pitch", "Pitch" };
+ juce::Label fmFreqLabel { "FM Freq", "FM Freq" };
+ juce::Label fmDepthLabel { "FM Depth", "FM Depth" };
+
std::unique_ptr oscSelAttachment;
- std::unique_ptr gainAttachment;
- std::unique_ptr pitchAttachment;
+ std::unique_ptr gainAttachment;
+ std::unique_ptr pitchAttachment;
+ std::unique_ptr fmFreqAttachment;
+ std::unique_ptr fmDepthAttachment;
+
+ juce::String name { "" };
+
+ static constexpr float fontHeight { 15.0f };
+ static constexpr int textBoxWidth { 35 };
+ static constexpr int textBoxHeight { 20 };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscComponent)
};
```
Diff 7: 35915fe2d7e0e8ede1372eed6062820dcddb47f7 to 8d9a9945d63503d9d6864fb59aed85b8f31c3541
diff --git a/Source/Data/FilterData.cpp b/Source/Data/FilterData.cpp
new file mode 100644
```diff
index 0000000..28b0961
--- /dev/null
+++ b/Source/Data/FilterData.cpp
@@ -0,0 +1,40 @@
+/*
+ ==============================================================================
+
+ FilterData.cpp
+ Created: 18 Feb 2021 9:26:23pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include "FilterData.h"
+
+void FilterData::setParams (const int filterType, const float filterCutoff, const float filterResonance)
+{
+ selectFilterType (filterType);
+ setCutoffFrequency (filterCutoff);
+ setResonance (filterResonance);
+}
+
+void FilterData::selectFilterType (const int filterType)
+{
+ switch (filterType)
+ {
+ case 0:
+ setType (juce::dsp::StateVariableTPTFilterType::lowpass);
+ break;
+
+ case 1:
+ setType (juce::dsp::StateVariableTPTFilterType::bandpass);
+ break;
+
+ case 2:
+ setType (juce::dsp::StateVariableTPTFilterType::highpass);
+ break;
+
+ default:
+ setType (juce::dsp::StateVariableTPTFilterType::lowpass);
+ break;
+ }
+}
```
diff --git a/Source/Data/FilterData.h b/Source/Data/FilterData.h
new file mode 100644
```diff
index 0000000..b51e220
--- /dev/null
+++ b/Source/Data/FilterData.h
@@ -0,0 +1,22 @@
+/*
+ ==============================================================================
+
+ FilterData.h
+ Created: 18 Feb 2021 9:26:23pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+
+class FilterData : public juce::dsp::StateVariableTPTFilter
+{
+public:
+ void setParams (const int filterType, const float filterCutoff, const float filterResonance);
+private:
+ void selectFilterType (const int type);
+};
```
diff --git a/Source/Data/OscData.cpp b/Source/Data/OscData.cpp
index 6c41eb3..a23d61d 100644
```diff
--- a/Source/Data/OscData.cpp
+++ b/Source/Data/OscData.cpp
@@ -85,3 +85,11 @@ float OscData::processNextSample (float input)
fmModulator = fmOsc.processSample (input) * fmDepth;
return gain.processSample (processSample (input));
}
+
+void OscData::setParams (const int oscChoice, const float oscGain, const int oscPitch, const float fmFreq, const float fmDepth)
+{
+ setType (oscChoice);
+ setGain (oscGain);
+ setOscPitch (oscPitch);
+ setFmOsc (fmFreq, fmDepth);
+}
```
diff --git a/Source/Data/OscData.h b/Source/Data/OscData.h
index 5380444..48abba3 100644
```diff
--- a/Source/Data/OscData.h
+++ b/Source/Data/OscData.h
@@ -23,6 +23,8 @@ public:
void setFmOsc (const float freq, const float depth);
void renderNextBlock (juce::dsp::AudioBlock& audioBlock);
float processNextSample (float input);
+ void setParams (const int oscChoice, const float oscGain, const int oscPitch, const float fmFreq, const float fmDepth);
+
private:
juce::dsp::Oscillator fmOsc { [](float x) { return std::sin (x); }};
```
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index 05181ab..670af14 100644
```diff
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -15,15 +15,19 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess
, audioProcessor (p)
, osc1 (audioProcessor.apvts, "OSC1", "OSC1GAIN", "OSC1PITCH", "OSC1FMFREQ", "OSC1FMDEPTH")
, osc2 (audioProcessor.apvts, "OSC2", "OSC2GAIN", "OSC2PITCH", "OSC2FMFREQ", "OSC2FMDEPTH")
+, filter (audioProcessor.apvts, "FILTERTYPE", "FILTERCUTOFF", "FILTERRESONANCE")
, adsr (audioProcessor.apvts, "ATTACK", "DECAY", "SUSTAIN", "RELEASE")
{
- setSize (650, 240);
addAndMakeVisible (osc1);
addAndMakeVisible (osc2);
+ addAndMakeVisible (filter);
addAndMakeVisible (adsr);
osc1.setName ("Oscillator 1");
osc2.setName ("Oscillator 2");
+ filter.setName ("Filter");
+
+ setSize (830, 240);
}
TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
@@ -42,7 +46,8 @@ void TapSynthAudioProcessorEditor::resized()
const auto oscHeight = 120;
osc1.setBounds (0, 0, oscWidth, oscHeight);
osc2.setBounds (0, osc1.getBottom(), oscWidth, oscHeight);
- adsr.setBounds (osc1.getRight(), 0, 230, 240);
+ filter.setBounds (osc1.getRight(), 0, 180, 240);
+ adsr.setBounds (filter.getRight(), 0, 230, 240);
}
```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h
index d789924..ba61702 100644
```diff
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -11,6 +11,7 @@
#include
#include "PluginProcessor.h"
#include "UI/OscComponent.h"
+#include "UI/FilterComponent.h"
#include "UI/AdsrComponent.h"
//==============================================================================
@@ -30,6 +31,7 @@ private:
TapSynthAudioProcessor& audioProcessor;
OscComponent osc1;
OscComponent osc2;
+ FilterComponent filter;
AdsrComponent adsr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 31dcc48..af7d686 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -23,12 +23,13 @@ TapSynthAudioProcessor::TapSynthAudioProcessor()
#endif
{
synth.addSound (new SynthSound());
- //synth.addVoice (new SynthVoice());
for (int i = 0; i < 5; i++)
{
synth.addVoice (new SynthVoice());
}
+
+ filter.setType (juce::dsp::StateVariableTPTFilterType::lowpass);
}
TapSynthAudioProcessor::~TapSynthAudioProcessor()
@@ -110,6 +111,13 @@ void TapSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlo
voice->prepareToPlay (sampleRate, samplesPerBlock, getTotalNumOutputChannels());
}
}
+
+ juce::dsp::ProcessSpec spec;
+ spec.maximumBlockSize = samplesPerBlock;
+ spec.sampleRate = sampleRate;
+ spec.numChannels = getTotalNumOutputChannels();
+
+ filter.prepare (spec);
}
void TapSynthAudioProcessor::releaseResources()
@@ -151,48 +159,12 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
buffer.clear (i, 0, buffer.getNumSamples());
- for (int i = 0; i < synth.getNumVoices(); ++i)
- {
- if (auto voice = dynamic_cast(synth.getVoice(i)))
- {
- auto& attack = *apvts.getRawParameterValue ("ATTACK");
- auto& decay = *apvts.getRawParameterValue ("DECAY");
- auto& sustain = *apvts.getRawParameterValue ("SUSTAIN");
- auto& release = *apvts.getRawParameterValue ("RELEASE");
-
- auto& osc1Choice = *apvts.getRawParameterValue ("OSC1");
- auto& osc2Choice = *apvts.getRawParameterValue ("OSC2");
- auto& osc1Gain = *apvts.getRawParameterValue ("OSC1GAIN");
- auto& osc2Gain = *apvts.getRawParameterValue ("OSC2GAIN");
- auto& osc1Pitch = *apvts.getRawParameterValue ("OSC1PITCH");
- auto& osc2Pitch = *apvts.getRawParameterValue ("OSC2PITCH");
- auto& osc1FmFreq = *apvts.getRawParameterValue ("OSC1FMFREQ");
- auto& osc2FmFreq = *apvts.getRawParameterValue ("OSC2FMFREQ");
- auto& osc1FmDepth = *apvts.getRawParameterValue ("OSC1FMDEPTH");
- auto& osc2FmDepth = *apvts.getRawParameterValue ("OSC2FMDEPTH");
-
- auto& osc1 = voice->getOscillator1();
- auto& osc2 = voice->getOscillator2();
- auto& adsr = voice->getAdsr();
-
- for (int i = 0; i < 2; i++)
- {
- osc1[i].setType (osc1Choice);
- osc1[i].setGain (osc1Gain);
- osc1[i].setOscPitch (osc1Pitch);
- osc1[i].setFmOsc (osc1FmFreq, osc1FmDepth);
-
- osc2[i].setType (osc2Choice);
- osc2[i].setGain (osc2Gain);
- osc2[i].setOscPitch (osc2Pitch);
- osc2[i].setFmOsc (osc2FmFreq, osc2FmDepth);
- }
-
- adsr.update (attack.load(), decay.load(), sustain.load(), release.load());
- }
- }
+ setParams();
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
+
+ juce::dsp::AudioBlock block { buffer };
+ filter.process (juce::dsp::ProcessContextReplacing(block));
}
//==============================================================================
@@ -251,6 +223,12 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
params.push_back (std::make_unique("OSC1FMDEPTH", "Oscillator 1 FM Depth", juce::NormalisableRange { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
params.push_back (std::make_unique("OSC2FMDEPTH", "Oscillator 2 FM Depth", juce::NormalisableRange { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+ //Filter
+ params.push_back (std::make_unique("FILTERTYPE", "Filter Type", juce::StringArray { "Low Pass", "Band Pass", "High Pass" }, 0));
+ params.push_back (std::make_unique("FILTERCUTOFF", "Filter Cutoff", juce::NormalisableRange { 20.0f, 20000.0f, 0.1f, 0.6f }, 20000.0f, "Hz"));
+ params.push_back (std::make_unique("FILTERRESONANCE", "Filter Resonance", juce::NormalisableRange { 0.1f, 2.0f, 0.1f }, 0.1f, ""));
+
+
// ADSR
params.push_back (std::make_unique("ATTACK", "Attack", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
params.push_back (std::make_unique("DECAY", "Decay", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
@@ -259,3 +237,47 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
return { params.begin(), params.end() };
}
+
+void TapSynthAudioProcessor::setParams()
+{
+ for (int i = 0; i < synth.getNumVoices(); ++i)
+ {
+ if (auto voice = dynamic_cast(synth.getVoice(i)))
+ {
+ auto& attack = *apvts.getRawParameterValue ("ATTACK");
+ auto& decay = *apvts.getRawParameterValue ("DECAY");
+ auto& sustain = *apvts.getRawParameterValue ("SUSTAIN");
+ auto& release = *apvts.getRawParameterValue ("RELEASE");
+
+ auto& osc1Choice = *apvts.getRawParameterValue ("OSC1");
+ auto& osc2Choice = *apvts.getRawParameterValue ("OSC2");
+ auto& osc1Gain = *apvts.getRawParameterValue ("OSC1GAIN");
+ auto& osc2Gain = *apvts.getRawParameterValue ("OSC2GAIN");
+ auto& osc1Pitch = *apvts.getRawParameterValue ("OSC1PITCH");
+ auto& osc2Pitch = *apvts.getRawParameterValue ("OSC2PITCH");
+ auto& osc1FmFreq = *apvts.getRawParameterValue ("OSC1FMFREQ");
+ auto& osc2FmFreq = *apvts.getRawParameterValue ("OSC2FMFREQ");
+ auto& osc1FmDepth = *apvts.getRawParameterValue ("OSC1FMDEPTH");
+ auto& osc2FmDepth = *apvts.getRawParameterValue ("OSC2FMDEPTH");
+
+ auto& osc1 = voice->getOscillator1();
+ auto& osc2 = voice->getOscillator2();
+
+ auto& adsr = voice->getAdsr();
+
+ for (int i = 0; i < 2; i++)
+ {
+ osc1[i].setParams (osc1Choice, osc1Gain, osc1Pitch, osc1FmFreq, osc1FmDepth);
+ osc2[i].setParams (osc2Choice, osc2Gain, osc2Pitch, osc2FmFreq, osc2FmDepth);
+ }
+
+ adsr.update (attack.load(), decay.load(), sustain.load(), release.load());
+ }
+ }
+
+ auto& filterType = *apvts.getRawParameterValue ("FILTERTYPE");
+ auto& filterCutoff = *apvts.getRawParameterValue ("FILTERCUTOFF");
+ auto& filterResonance = *apvts.getRawParameterValue ("FILTERRESONANCE");
+
+ filter.setParams (filterType, filterCutoff, filterResonance);
+}
```
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h
index 68c34b4..f31821c 100644
```diff
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -11,6 +11,7 @@
#include
#include "SynthVoice.h"
#include "SynthSound.h"
+#include "Data/FilterData.h"
//==============================================================================
/**
@@ -59,7 +60,9 @@ public:
private:
juce::Synthesiser synth;
+ FilterData filter;
juce::AudioProcessorValueTreeState::ParameterLayout createParams();
+ void setParams();
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessor)
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index ff8924f..17c3850 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -31,8 +31,6 @@ public:
AdsrData& getAdsr() { return adsr; }
private:
-// OscData osc1;
-// OscData osc2;
std::array osc1;
std::array osc2;
AdsrData adsr;
```
diff --git a/Source/UI/FilterComponent.cpp b/Source/UI/FilterComponent.cpp
new file mode 100644
```diff
index 0000000..d4fa1ca
--- /dev/null
+++ b/Source/UI/FilterComponent.cpp
@@ -0,0 +1,71 @@
+/*
+ ==============================================================================
+
+ FilterComponent.cpp
+ Created: 18 Feb 2021 10:00:39pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include
+#include "FilterComponent.h"
+
+//==============================================================================
+FilterComponent::FilterComponent (juce::AudioProcessorValueTreeState& apvts, juce::String filterTypeId, juce::String cutoffId, juce::String resonanceId)
+{
+ juce::StringArray filterTypeChoices { "Low Pass", "Band Pass", "High Pass" };
+ filterTypeSelector.addItemList (filterTypeChoices, 1);
+ filterTypeSelector.setSelectedItemIndex (0);
+ addAndMakeVisible (filterTypeSelector);
+
+ setSliderParams (cutoffSlider, cutoffLabel, cutoffAttachment, cutoffId, apvts);
+ setSliderParams (resonanceSlider, resonanceLabel, resonanceAttachment, resonanceId, apvts);
+}
+
+FilterComponent::~FilterComponent()
+{
+}
+
+void FilterComponent::paint (juce::Graphics& g)
+{
+ g.fillAll (juce::Colours::black);
+ auto bounds = getLocalBounds();
+ g.setColour (juce::Colours::white);
+ g.drawRoundedRectangle (bounds.toFloat().reduced (10.0f), 5.0f, 2.0f);
+
+ g.setColour (juce::Colours::yellow);
+ g.setFont (fontHeight);
+ g.setFont (g.getCurrentFont().boldened());
+ g.drawText (name, 20, 15, 100, 25, juce::Justification::left);
+}
+
+void FilterComponent::resized()
+{
+ const auto dialSize = 70;
+ const auto labelWidth = 70;
+ const auto labelHeight = 18;
+ const auto startX = 18;
+ const auto startY = 40;
+
+ filterTypeSelector.setBounds (startX, startY, 145, 25);
+ cutoffLabel.setBounds (startX, 80, labelWidth, labelHeight);
+ cutoffSlider.setBounds (startX, 95, dialSize, dialSize);
+ resonanceLabel.setBounds (cutoffLabel.getRight(), 80, labelWidth, labelHeight);
+ resonanceSlider.setBounds (cutoffSlider.getRight(), 95, dialSize, dialSize);
+}
+
+using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+void FilterComponent::setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts)
+{
+ slider.setSliderStyle (juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag);
+ slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, textBoxWidth, textBoxHeight);
+ addAndMakeVisible (slider);
+
+ label.setFont (fontHeight);
+ label.setJustificationType (juce::Justification::centred);
+ addAndMakeVisible (label);
+
+ attachment = std::make_unique(apvts, paramId, slider);
+}
```
diff --git a/Source/UI/FilterComponent.h b/Source/UI/FilterComponent.h
new file mode 100644
```diff
index 0000000..aced363
--- /dev/null
+++ b/Source/UI/FilterComponent.h
@@ -0,0 +1,51 @@
+/*
+ ==============================================================================
+
+ FilterComponent.h
+ Created: 18 Feb 2021 10:00:39pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+//==============================================================================
+/*
+*/
+class FilterComponent : public juce::Component
+{
+public:
+ FilterComponent (juce::AudioProcessorValueTreeState& apvts, juce::String filterTypeId, juce::String cutoffId, juce::String resonanceId);
+ ~FilterComponent() override;
+
+ void paint (juce::Graphics&) override;
+ void resized() override;
+
+ void setName (const juce::String n) { name = n; }
+
+private:
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ void setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts);
+
+ juce::ComboBox filterTypeSelector;
+ juce::Slider cutoffSlider;
+ juce::Slider resonanceSlider;
+
+ juce::Label cutoffLabel { "Cutoff", "Cutoff" };
+ juce::Label resonanceLabel { "Resonance", "Resonance" };
+
+ std::unique_ptr filterTypeAttachment;
+ std::unique_ptr cutoffAttachment;
+ std::unique_ptr resonanceAttachment;
+
+ juce::String name { "" };
+ static constexpr float fontHeight { 15.0f };
+ static constexpr int textBoxWidth { 50 };
+ static constexpr int textBoxHeight { 20 };
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterComponent)
+};
```
diff --git a/tapSynth.jucer b/tapSynth.jucer
index 6c2d6f1..910e80f 100644
```diff
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -17,6 +17,8 @@
+
+
@@ -24,6 +26,10 @@
+
+
```
Diff 8: 8d9a9945d63503d9d6864fb59aed85b8f31c3541 to 6d1eb19d65eaf8e29bfe7217c2d9d00173303027
diff --git a/Source/Data/FilterData.cpp b/Source/Data/FilterData.cpp
index 28b0961..1c31eba 100644
```diff
--- a/Source/Data/FilterData.cpp
+++ b/Source/Data/FilterData.cpp
@@ -17,8 +17,26 @@ void FilterData::setParams (const int filterType, const float filterCutoff, cons
setResonance (filterResonance);
}
-void FilterData::selectFilterType (const int filterType)
+void FilterData::setLfoParams (const float freq, const float depth)
+{
+ lfo.setGain (juce::Decibels::gainToDecibels (depth));
+ lfo.setFrequency (freq);
+}
+
+void FilterData::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels)
{
+ juce::dsp::ProcessSpec spec;
+ spec.maximumBlockSize = samplesPerBlock;
+ spec.sampleRate = sampleRate;
+ spec.numChannels = outputChannels;
+
+ prepare (spec);
+ lfo.prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
+}
+
+
+void FilterData::selectFilterType (const int filterType)
+{
switch (filterType)
{
case 0:
@@ -38,3 +56,14 @@ void FilterData::selectFilterType (const int filterType)
break;
}
}
+
+void FilterData::processNextBlock(juce::AudioBuffer& buffer)
+{
+ juce::dsp::AudioBlock block { buffer };
+ process (juce::dsp::ProcessContextReplacing(block));
+}
+
+float FilterData::processNextSample (int channel, float inputValue)
+{
+ return processSample (channel, inputValue);
+}
```
diff --git a/Source/Data/FilterData.h b/Source/Data/FilterData.h
index b51e220..0733654 100644
```diff
--- a/Source/Data/FilterData.h
+++ b/Source/Data/FilterData.h
@@ -10,13 +10,19 @@
#pragma once
+#include "OscData.h"
#include
class FilterData : public juce::dsp::StateVariableTPTFilter
{
public:
+ void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels);
void setParams (const int filterType, const float filterCutoff, const float filterResonance);
+ void setLfoParams (const float freq, const float depth);
+ void processNextBlock (juce::AudioBuffer& buffer);
+ float processNextSample (int channel, float inputValue);
private:
void selectFilterType (const int type);
+ OscData lfo;
};
```
diff --git a/Source/Data/OscData.cpp b/Source/Data/OscData.cpp
index a23d61d..b99f860 100644
```diff
--- a/Source/Data/OscData.cpp
+++ b/Source/Data/OscData.cpp
@@ -50,7 +50,7 @@ void OscData::setType (const int oscSelection)
void OscData::setGain (const float levelInDecibels)
{
- gain.setGainDecibels(levelInDecibels);
+ gain.setGainDecibels (levelInDecibels);
}
void OscData::setOscPitch (const int pitch)
```
diff --git a/Source/Data/OscData.h b/Source/Data/OscData.h
index 48abba3..91c829b 100644
```diff
--- a/Source/Data/OscData.h
+++ b/Source/Data/OscData.h
@@ -25,7 +25,6 @@ public:
float processNextSample (float input);
void setParams (const int oscChoice, const float oscGain, const int oscPitch, const float fmFreq, const float fmDepth);
-
private:
juce::dsp::Oscillator fmOsc { [](float x) { return std::sin (x); }};
juce::dsp::Gain gain;
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index af7d686..d280c94 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -24,12 +24,15 @@ TapSynthAudioProcessor::TapSynthAudioProcessor()
{
synth.addSound (new SynthSound());
- for (int i = 0; i < 5; i++)
+ for (int i = 0; i < numVoices; i++)
{
synth.addVoice (new SynthVoice());
}
- filter.setType (juce::dsp::StateVariableTPTFilterType::lowpass);
+ for (int ch = 0; ch < numChannelsToProcess; ++ch)
+ {
+ filter[ch].setType (juce::dsp::StateVariableTPTFilterType::lowpass);
+ }
}
TapSynthAudioProcessor::~TapSynthAudioProcessor()
@@ -112,12 +115,10 @@ void TapSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlo
}
}
- juce::dsp::ProcessSpec spec;
- spec.maximumBlockSize = samplesPerBlock;
- spec.sampleRate = sampleRate;
- spec.numChannels = getTotalNumOutputChannels();
-
- filter.prepare (spec);
+ for (int ch = 0; ch < numChannelsToProcess; ++ch)
+ {
+ filter[ch].prepareToPlay (sampleRate, samplesPerBlock, getTotalNumOutputChannels());
+ }
}
void TapSynthAudioProcessor::releaseResources()
@@ -163,8 +164,15 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
- juce::dsp::AudioBlock block { buffer };
- filter.process (juce::dsp::ProcessContextReplacing(block));
+ for (int ch = 0; ch < numChannelsToProcess; ++ch)
+ {
+ auto* output = buffer.getWritePointer (ch);
+
+ for (int s = 0; s < buffer.getNumSamples(); ++s)
+ {
+ output[s] = filter[ch].processNextSample (ch, buffer.getSample (ch, s));
+ }
+ }
}
//==============================================================================
@@ -223,12 +231,15 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
params.push_back (std::make_unique("OSC1FMDEPTH", "Oscillator 1 FM Depth", juce::NormalisableRange { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
params.push_back (std::make_unique("OSC2FMDEPTH", "Oscillator 2 FM Depth", juce::NormalisableRange { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+ // LFO
+ params.push_back (std::make_unique("LFO1FREQ", "LFO1 Frequency", juce::NormalisableRange { 0.0f, 20.0f, 0.1f }, 0.0f, "Hz"));
+ params.push_back (std::make_unique("LFO1DEPTH", "LFO1 Depth", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+
//Filter
params.push_back (std::make_unique("FILTERTYPE", "Filter Type", juce::StringArray { "Low Pass", "Band Pass", "High Pass" }, 0));
params.push_back (std::make_unique("FILTERCUTOFF", "Filter Cutoff", juce::NormalisableRange { 20.0f, 20000.0f, 0.1f, 0.6f }, 20000.0f, "Hz"));
params.push_back (std::make_unique("FILTERRESONANCE", "Filter Resonance", juce::NormalisableRange { 0.1f, 2.0f, 0.1f }, 0.1f, ""));
-
// ADSR
params.push_back (std::make_unique("ATTACK", "Attack", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
params.push_back (std::make_unique("DECAY", "Decay", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
@@ -239,6 +250,12 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
}
void TapSynthAudioProcessor::setParams()
+{
+ setVoiceParams();
+ setFilterParams();
+}
+
+void TapSynthAudioProcessor::setVoiceParams()
{
for (int i = 0; i < synth.getNumVoices(); ++i)
{
@@ -265,7 +282,7 @@ void TapSynthAudioProcessor::setParams()
auto& adsr = voice->getAdsr();
- for (int i = 0; i < 2; i++)
+ for (int i = 0; i < getTotalNumOutputChannels(); i++)
{
osc1[i].setParams (osc1Choice, osc1Gain, osc1Pitch, osc1FmFreq, osc1FmDepth);
osc2[i].setParams (osc2Choice, osc2Gain, osc2Pitch, osc2FmFreq, osc2FmDepth);
@@ -274,10 +291,20 @@ void TapSynthAudioProcessor::setParams()
adsr.update (attack.load(), decay.load(), sustain.load(), release.load());
}
}
-
+}
+
+void TapSynthAudioProcessor::setFilterParams()
+{
auto& filterType = *apvts.getRawParameterValue ("FILTERTYPE");
auto& filterCutoff = *apvts.getRawParameterValue ("FILTERCUTOFF");
auto& filterResonance = *apvts.getRawParameterValue ("FILTERRESONANCE");
- filter.setParams (filterType, filterCutoff, filterResonance);
+ auto& lfoFreq = *apvts.getRawParameterValue ("LFO1FREQ");
+ auto& lfoDepth = *apvts.getRawParameterValue ("LFO1DEPTH");
+
+ for (int ch = 0; ch < numChannelsToProcess; ++ch)
+ {
+ filter[ch].setParams (filterType, filterCutoff, filterResonance);
+ filter[ch].setLfoParams (lfoFreq, lfoDepth);
+ }
}
```
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h
index f31821c..b8bd1b5 100644
```diff
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -59,10 +59,14 @@ public:
juce::AudioProcessorValueTreeState apvts;
private:
+ static constexpr int numChannelsToProcess { 2 };
juce::Synthesiser synth;
- FilterData filter;
+ std::array filter;
juce::AudioProcessorValueTreeState::ParameterLayout createParams();
void setParams();
+ void setVoiceParams();
+ void setFilterParams();
+ static constexpr int numVoices { 5 };
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessor)
```
diff --git a/Source/UI/FilterComponent.cpp b/Source/UI/FilterComponent.cpp
index d4fa1ca..e336eb3 100644
```diff
--- a/Source/UI/FilterComponent.cpp
+++ b/Source/UI/FilterComponent.cpp
@@ -19,6 +19,8 @@ FilterComponent::FilterComponent (juce::AudioProcessorValueTreeState& apvts, juc
filterTypeSelector.setSelectedItemIndex (0);
addAndMakeVisible (filterTypeSelector);
+ filterTypeAttachment = std::make_unique(apvts, filterTypeId, filterTypeSelector);
+
setSliderParams (cutoffSlider, cutoffLabel, cutoffAttachment, cutoffId, apvts);
setSliderParams (resonanceSlider, resonanceLabel, resonanceAttachment, resonanceId, apvts);
}
```
Diff 9: 6d1eb19d65eaf8e29bfe7217c2d9d00173303027 to 5e9bdd2f6d524047bc80c3c4a01854ecb5a859fd
diff --git a/Source/Data/FilterData.cpp b/Source/Data/FilterData.cpp
index 1c31eba..646b49c 100644
```diff
--- a/Source/Data/FilterData.cpp
+++ b/Source/Data/FilterData.cpp
@@ -19,8 +19,8 @@ void FilterData::setParams (const int filterType, const float filterCutoff, cons
void FilterData::setLfoParams (const float freq, const float depth)
{
- lfo.setGain (juce::Decibels::gainToDecibels (depth));
- lfo.setFrequency (freq);
+// lfoGain = juce::Decibels::gainToDecibels (depth);
+// lfo.setFrequency (freq);
}
void FilterData::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels)
@@ -31,7 +31,6 @@ void FilterData::prepareToPlay (double sampleRate, int samplesPerBlock, int outp
spec.numChannels = outputChannels;
prepare (spec);
- lfo.prepareToPlay (sampleRate, samplesPerBlock, outputChannels);
}
```
diff --git a/Source/Data/FilterData.h b/Source/Data/FilterData.h
index 0733654..cecaede 100644
```diff
--- a/Source/Data/FilterData.h
+++ b/Source/Data/FilterData.h
@@ -22,7 +22,8 @@ public:
void setLfoParams (const float freq, const float depth);
void processNextBlock (juce::AudioBuffer& buffer);
float processNextSample (int channel, float inputValue);
+
private:
void selectFilterType (const int type);
- OscData lfo;
+ juce::dsp::Oscillator lfo { [](float x) { return std::sin (x); }};
};
```
diff --git a/Source/Data/OscData.cpp b/Source/Data/OscData.cpp
index b99f860..89fba49 100644
```diff
--- a/Source/Data/OscData.cpp
+++ b/Source/Data/OscData.cpp
@@ -19,7 +19,7 @@ void OscData::prepareToPlay (double sampleRate, int samplesPerBlock, int outputC
prepare (spec);
fmOsc.prepare (spec);
- gain.prepare (spec);
+ gain.prepare (spec);
}
void OscData::setType (const int oscSelection)
```
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp
index 670af14..e0ebd83 100644
```diff
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -17,17 +17,23 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess
, osc2 (audioProcessor.apvts, "OSC2", "OSC2GAIN", "OSC2PITCH", "OSC2FMFREQ", "OSC2FMDEPTH")
, filter (audioProcessor.apvts, "FILTERTYPE", "FILTERCUTOFF", "FILTERRESONANCE")
, adsr (audioProcessor.apvts, "ATTACK", "DECAY", "SUSTAIN", "RELEASE")
+, lfo1 (audioProcessor.apvts, "LFO1FREQ", "LFO1DEPTH")
+, filterAdsr (audioProcessor.apvts, "FILTERATTACK", "FILTERDECAY", "FILTERSUSTAIN", "FILTERRELEASE")
+, reverb (audioProcessor.apvts, "REVERBSIZE", "REVERBDAMPING", "REVERBWIDTH", "REVERBDRY", "REVERBWET", "REVERBFREEZE")
{
addAndMakeVisible (osc1);
addAndMakeVisible (osc2);
addAndMakeVisible (filter);
addAndMakeVisible (adsr);
+ addAndMakeVisible (lfo1);
+ addAndMakeVisible (filterAdsr);
+ addAndMakeVisible (reverb);
osc1.setName ("Oscillator 1");
osc2.setName ("Oscillator 2");
filter.setName ("Filter");
- setSize (830, 240);
+ setSize (830, 525);
}
TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
@@ -43,11 +49,14 @@ void TapSynthAudioProcessorEditor::paint (juce::Graphics& g)
void TapSynthAudioProcessorEditor::resized()
{
const auto oscWidth = 420;
- const auto oscHeight = 120;
+ const auto oscHeight = 180;
osc1.setBounds (0, 0, oscWidth, oscHeight);
osc2.setBounds (0, osc1.getBottom(), oscWidth, oscHeight);
- filter.setBounds (osc1.getRight(), 0, 180, 240);
- adsr.setBounds (filter.getRight(), 0, 230, 240);
+ filter.setBounds (osc1.getRight(), 0, 180, 200);
+ lfo1.setBounds (osc2.getRight(), filter.getBottom(), 180, 160);
+ adsr.setBounds (filter.getRight(), 0, 230, 360);
+ reverb.setBounds (0, osc2.getBottom(), oscWidth, 150);
+ filterAdsr.setBounds (reverb.getRight(), lfo1.getBottom(), 180, 150);
}
```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h
index ba61702..08db2b9 100644
```diff
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -13,6 +13,8 @@
#include "UI/OscComponent.h"
#include "UI/FilterComponent.h"
#include "UI/AdsrComponent.h"
+#include "UI/LfoComponent.h"
+#include "UI/ReverbComponent.h"
//==============================================================================
/**
@@ -33,6 +35,9 @@ private:
OscComponent osc2;
FilterComponent filter;
AdsrComponent adsr;
+ LfoComponent lfo1;
+ AdsrComponent filterAdsr;
+ ReverbComponent reverb;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
};
```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index d280c94..4ddca45 100644
```diff
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -8,6 +8,7 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
+#include
//==============================================================================
TapSynthAudioProcessor::TapSynthAudioProcessor()
@@ -115,10 +116,26 @@ void TapSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlo
}
}
+ juce::dsp::ProcessSpec spec;
+ spec.maximumBlockSize = samplesPerBlock;
+ spec.sampleRate = sampleRate;
+ spec.numChannels = getTotalNumOutputChannels();
+
for (int ch = 0; ch < numChannelsToProcess; ++ch)
{
filter[ch].prepareToPlay (sampleRate, samplesPerBlock, getTotalNumOutputChannels());
+ lfo[ch].prepare (spec);
+ lfo[ch].initialise ([](float x) { return std::sin (x); });
}
+
+ reverbParams.roomSize = 0.5f;
+ reverbParams.width = 1.0f;
+ reverbParams.damping = 0.5f;
+ reverbParams.freezeMode = 0.0f;
+ reverbParams.dryLevel = 1.0f;
+ reverbParams.wetLevel = 0.0f;
+
+ reverb.setParameters (reverbParams);
}
void TapSynthAudioProcessor::releaseResources()
@@ -161,7 +178,7 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
buffer.clear (i, 0, buffer.getNumSamples());
setParams();
-
+
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
for (int ch = 0; ch < numChannelsToProcess; ++ch)
@@ -169,10 +186,14 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc
auto* output = buffer.getWritePointer (ch);
for (int s = 0; s < buffer.getNumSamples(); ++s)
- {
+ {
+ lfoOutput[ch] = lfo[ch].processSample (buffer.getSample (ch, s));
output[s] = filter[ch].processNextSample (ch, buffer.getSample (ch, s));
}
}
+
+ juce::dsp::AudioBlock block { buffer };
+ reverb.process (juce::dsp::ProcessContextReplacing (block));
}
//==============================================================================
@@ -233,7 +254,7 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
// LFO
params.push_back (std::make_unique("LFO1FREQ", "LFO1 Frequency", juce::NormalisableRange { 0.0f, 20.0f, 0.1f }, 0.0f, "Hz"));
- params.push_back (std::make_unique("LFO1DEPTH", "LFO1 Depth", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique("LFO1DEPTH", "LFO1 Depth", juce::NormalisableRange { 0.0f, 10000.0f, 0.1f, 0.3f }, 0.0f, ""));
//Filter
params.push_back (std::make_unique("FILTERTYPE", "Filter Type", juce::StringArray { "Low Pass", "Band Pass", "High Pass" }, 0));
@@ -246,6 +267,20 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
params.push_back (std::make_unique("SUSTAIN", "Sustain", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 1.0f));
params.push_back (std::make_unique("RELEASE", "Release", juce::NormalisableRange { 0.1f, 3.0f, 0.1f }, 0.4f));
+ params.push_back (std::make_unique("FILTERADSRDEPTH", "Filter ADSR Depth", juce::NormalisableRange { 0.0f, 10000.0f, 0.1f, 0.3f }, 10000.0f, ""));
+ params.push_back (std::make_unique("FILTERATTACK", "Filter Attack", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique("FILTERDECAY", "Filter Decay", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique("FILTERSUSTAIN", "Filter Sustain", juce::NormalisableRange { 0.1f, 1.0f, 0.1f }, 1.0f));
+ params.push_back (std::make_unique("FILTERRELEASE", "Filter Release", juce::NormalisableRange { 0.1f, 3.0f, 0.1f }, 0.4f));
+
+ // Reverb
+ params.push_back (std::make_unique("REVERBSIZE", "Reverb Size", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique("REVERBWIDTH", "Reverb Width", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 1.0f, ""));
+ params.push_back (std::make_unique("REVERBDAMPING", "Reverb Damping", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.5f, ""));
+ params.push_back (std::make_unique("REVERBDRY", "Reverb Dry", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 1.0f, ""));
+ params.push_back (std::make_unique("REVERBWET", "Reverb Wet", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique("REVERBFREEZE", "Reverb Freeze", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+
return { params.begin(), params.end() };
}
@@ -253,6 +288,7 @@ void TapSynthAudioProcessor::setParams()
{
setVoiceParams();
setFilterParams();
+ setReverbParams();
}
void TapSynthAudioProcessor::setVoiceParams()
@@ -276,11 +312,17 @@ void TapSynthAudioProcessor::setVoiceParams()
auto& osc2FmFreq = *apvts.getRawParameterValue ("OSC2FMFREQ");
auto& osc1FmDepth = *apvts.getRawParameterValue ("OSC1FMDEPTH");
auto& osc2FmDepth = *apvts.getRawParameterValue ("OSC2FMDEPTH");
+
+ auto& filterAttack = *apvts.getRawParameterValue ("FILTERATTACK");
+ auto& filterDecay = *apvts.getRawParameterValue ("FILTERDECAY");
+ auto& filterSustain = *apvts.getRawParameterValue ("FILTERSUSTAIN");
+ auto& filterRelease = *apvts.getRawParameterValue ("FILTERRELEASE");
auto& osc1 = voice->getOscillator1();
auto& osc2 = voice->getOscillator2();
auto& adsr = voice->getAdsr();
+ auto& filterAdsr = voice->getFilterAdsr();
for (int i = 0; i < getTotalNumOutputChannels(); i++)
{
@@ -289,6 +331,7 @@ void TapSynthAudioProcessor::setVoiceParams()
}
adsr.update (attack.load(), decay.load(), sustain.load(), release.load());
+ filterAdsr.update (filterAttack, filterDecay, filterSustain, filterRelease);
}
}
}
@@ -302,9 +345,30 @@ void TapSynthAudioProcessor::setFilterParams()
auto& lfoFreq = *apvts.getRawParameterValue ("LFO1FREQ");
auto& lfoDepth = *apvts.getRawParameterValue ("LFO1DEPTH");
+ auto& filterAdsrDepth = *apvts.getRawParameterValue ("FILTERADSRDEPTH");
+
+ auto adsrOutput = dynamic_cast(synth.getVoice(0))->getFilterAdsr().getNextSample();
+
for (int ch = 0; ch < numChannelsToProcess; ++ch)
{
- filter[ch].setParams (filterType, filterCutoff, filterResonance);
- filter[ch].setLfoParams (lfoFreq, lfoDepth);
+ lfo[ch].setFrequency (lfoFreq);
+ filterCutoff = (200.0f * adsrOutput) + filterCutoff;
+ filterCutoff = (lfoDepth * lfoOutput[ch]) + filterCutoff;
+ DBG (filterCutoff);
+ auto cutoff = std::clamp (filterCutoff, 20.0f, 20000.0f);
+
+ filter[ch].setParams (filterType, cutoff, filterResonance);
}
}
+
+void TapSynthAudioProcessor::setReverbParams()
+{
+ reverbParams.roomSize = *apvts.getRawParameterValue ("REVERBSIZE");
+ reverbParams.width = *apvts.getRawParameterValue ("REVERBWIDTH");
+ reverbParams.damping = *apvts.getRawParameterValue ("REVERBDAMPING");
+ reverbParams.dryLevel = *apvts.getRawParameterValue ("REVERBDRY");
+ reverbParams.wetLevel = *apvts.getRawParameterValue ("REVERBWET");
+ reverbParams.freezeMode = *apvts.getRawParameterValue ("REVERBFREEZE");
+
+ reverb.setParameters (reverbParams);
+}
```
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h
index b8bd1b5..4b5af6b 100644
```diff
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -12,6 +12,7 @@
#include "SynthVoice.h"
#include "SynthSound.h"
#include "Data/FilterData.h"
+#include "Data/AdsrData.h"
//==============================================================================
/**
@@ -62,11 +63,18 @@ private:
static constexpr int numChannelsToProcess { 2 };
juce::Synthesiser synth;
std::array filter;
+
juce::AudioProcessorValueTreeState::ParameterLayout createParams();
void setParams();
void setVoiceParams();
void setFilterParams();
+ void setReverbParams();
+
static constexpr int numVoices { 5 };
+ std::array, numChannelsToProcess> lfo;
+ std::array lfoOutput { 0.0f, 0.0f };
+ juce::dsp::Reverb reverb;
+ juce::Reverb::Parameters reverbParams;
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessor)
```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp
index 893ec30..50951da 100644
```diff
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -25,11 +25,13 @@ void SynthVoice::startNote (int midiNoteNumber, float velocity, juce::Synthesise
}
adsr.noteOn();
+ filterAdsr.noteOn();
}
void SynthVoice::stopNote (float velocity, bool allowTailOff)
{
adsr.noteOff();
+ filterAdsr.noteOff();
if (! allowTailOff || ! adsr.isActive())
clearCurrentNote();
@@ -48,6 +50,7 @@ void SynthVoice::pitchWheelMoved (int newPitchWheelValue)
void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels)
{
adsr.setSampleRate (sampleRate);
+ filterAdsr.setSampleRate (sampleRate);
juce::dsp::ProcessSpec spec;
spec.maximumBlockSize = samplesPerBlock;
@@ -74,6 +77,10 @@ void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int
return;
synthBuffer.setSize (outputBuffer.getNumChannels(), numSamples, false, false, true);
+
+ filterAdsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples());
+ filterAdsrOutput = filterAdsr.getNextSample();
+
synthBuffer.clear();
for (int i = 0; i < synthBuffer.getNumChannels(); ++i)
```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h
index 17c3850..26e8963 100644
```diff
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -29,12 +29,16 @@ public:
std::array& getOscillator1() { return osc1; }
std::array& getOscillator2() { return osc2; }
AdsrData& getAdsr() { return adsr; }
+ AdsrData& getFilterAdsr() { return filterAdsr; }
+ float getFilterAdsrOutput() { return filterAdsrOutput; }
private:
std::array osc1;
std::array osc2;
AdsrData adsr;
+ AdsrData filterAdsr;
juce::AudioBuffer synthBuffer;
+ float filterAdsrOutput { 0.0f };
juce::dsp::Gain gain;
bool isPrepared { false };
```
diff --git a/Source/UI/LfoComponent.cpp b/Source/UI/LfoComponent.cpp
new file mode 100644
```diff
index 0000000..34a4cd9
--- /dev/null
+++ b/Source/UI/LfoComponent.cpp
@@ -0,0 +1,61 @@
+/*
+ ==============================================================================
+
+ LfoComponent.cpp
+ Created: 19 Feb 2021 8:12:35pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include
+#include "LfoComponent.h"
+
+//==============================================================================
+LfoComponent::LfoComponent (juce::AudioProcessorValueTreeState& apvts, juce::String lfoFreqId, juce::String lfoDepthId)
+{
+ setSliderParams (lfoFreqSlider, lfoFreqLabel, lfoFreqAttachment, lfoFreqId, apvts);
+ setSliderParams (lfoDepthSlider, lfoDepthLabel, lfoDepthAttachment, lfoDepthId, apvts);
+}
+
+LfoComponent::~LfoComponent()
+{
+}
+
+void LfoComponent::paint (juce::Graphics& g)
+{
+ g.fillAll (juce::Colours::black);
+ auto bounds = getLocalBounds();
+ g.setColour (juce::Colours::white);
+ g.drawRoundedRectangle (bounds.toFloat().reduced (10.0f), 5.0f, 2.0f);
+
+ g.setColour (juce::Colours::yellow);
+ g.setFont (fontHeight);
+ g.setFont (g.getCurrentFont().boldened());
+ g.drawText ("Filter LFO", 20, 15, 100, 25, juce::Justification::left);
+}
+
+void LfoComponent::resized()
+{
+ const auto dialSize = 70;
+ const auto labelWidth = 70;
+ const auto labelHeight = 18;
+
+ lfoFreqLabel.setBounds (18, 40, labelWidth, labelHeight);
+ lfoFreqSlider.setBounds (18, 55, dialSize, dialSize);
+ lfoDepthLabel.setBounds (90, 40, labelWidth, labelHeight);
+ lfoDepthSlider.setBounds (90, 55, dialSize, dialSize);
+}
+
+void LfoComponent::setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts)
+{
+ slider.setSliderStyle (juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag);
+ slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, textBoxWidth, textBoxHeight);
+ addAndMakeVisible (slider);
+
+ label.setFont (fontHeight);
+ label.setJustificationType (juce::Justification::centred);
+ addAndMakeVisible (label);
+
+ attachment = std::make_unique(apvts, paramId, slider);
+}
```
diff --git a/Source/UI/LfoComponent.h b/Source/UI/LfoComponent.h
new file mode 100644
```diff
index 0000000..eea85b9
--- /dev/null
+++ b/Source/UI/LfoComponent.h
@@ -0,0 +1,46 @@
+/*
+ ==============================================================================
+
+ LfoComponent.h
+ Created: 19 Feb 2021 8:12:35pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+//==============================================================================
+/*
+*/
+class LfoComponent : public juce::Component
+{
+public:
+ LfoComponent (juce::AudioProcessorValueTreeState& apvts, juce::String lfoFreqId, juce::String lfoDepthId);
+ ~LfoComponent() override;
+
+ void paint (juce::Graphics&) override;
+ void resized() override;
+
+private:
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ void setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts);
+
+ juce::Slider lfoFreqSlider;
+ juce::Slider lfoDepthSlider;
+
+ juce::Label lfoFreqLabel { "Freq", "Freq" };
+ juce::Label lfoDepthLabel { "Depth", "Depth" };
+
+ std::unique_ptr lfoFreqAttachment;
+ std::unique_ptr lfoDepthAttachment;
+
+ static constexpr float fontHeight { 15.0f };
+ static constexpr int textBoxWidth { 50 };
+ static constexpr int textBoxHeight { 20 };
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LfoComponent)
+};
```
diff --git a/Source/UI/ReverbComponent.cpp b/Source/UI/ReverbComponent.cpp
new file mode 100644
```diff
index 0000000..c8783b6
--- /dev/null
+++ b/Source/UI/ReverbComponent.cpp
@@ -0,0 +1,92 @@
+/*
+ ==============================================================================
+
+ ReverbComponent.cpp
+ Created: 19 Feb 2021 9:18:11pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#include
+#include "ReverbComponent.h"
+
+//==============================================================================
+ReverbComponent::ReverbComponent (juce::AudioProcessorValueTreeState& apvts, juce::String sizeId, juce::String dampingId, juce::String widthId, juce::String dryId, juce::String wetId, juce::String freezeId)
+{
+ setSliderParams (sizeSlider, sizeLabel, sizeAttachment, sizeId, apvts);
+ setSliderParams (dampingSlider, dampingLabel, dampingAttachment, dampingId, apvts);
+ setSliderParams (widthSlider, widthLabel, widthAttachment, widthId, apvts);
+ setSliderParams (drySlider, dryLabel, dryAttachment, dryId, apvts);
+ setSliderParams (wetSlider, wetLabel, wetAttachment, wetId, apvts);
+ setSliderParams (freezeSlider, freezeLabel, freezeAttachment, freezeId, apvts);
+}
+
+ReverbComponent::~ReverbComponent()
+{
+}
+
+void ReverbComponent::paint (juce::Graphics& g)
+{
+ g.fillAll (juce::Colours::black);
+ auto bounds = getLocalBounds();
+ g.setColour (juce::Colours::white);
+ g.drawRoundedRectangle (bounds.toFloat().reduced (10.0f), 5.0f, 2.0f);
+
+ g.setColour (juce::Colours::yellow);
+ g.setFont (fontHeight);
+ g.setFont (g.getCurrentFont().boldened());
+ g.drawText ("Reverb", 20, 15, 100, 25, juce::Justification::left);
+}
+
+void ReverbComponent::resized()
+{
+ const auto dialSize = 67;
+ const auto labelWidth = 67;
+ const auto labelHeight = 18;
+ const auto yLabelStart = 40;
+ const auto yDialStart = 55;
+
+
+ sizeLabel.setBounds (5, yLabelStart, labelWidth, labelHeight);
+ sizeSlider.setBounds (5, yDialStart, dialSize, dialSize);
+
+ dampingLabel.setBounds (sizeLabel.getRight(), yLabelStart, labelWidth, labelHeight);
+ dampingSlider.setBounds (sizeSlider.getRight(), yDialStart, dialSize, dialSize);
+
+ widthLabel.setBounds (dampingLabel.getRight(), yLabelStart, labelWidth, labelHeight);
+ widthSlider.setBounds (dampingSlider.getRight(), yDialStart, dialSize, dialSize);
+
+ dryLabel.setBounds (widthLabel.getRight(), yLabelStart, labelWidth, labelHeight);
+ drySlider.setBounds (widthSlider.getRight(), yDialStart, dialSize, dialSize);
+
+ wetLabel.setBounds (dryLabel.getRight(), yLabelStart, labelWidth, labelHeight);
+ wetSlider.setBounds (drySlider.getRight(), yDialStart, dialSize, dialSize);
+
+ freezeLabel.setBounds (wetLabel.getRight(), yLabelStart, labelWidth, labelHeight);
+ freezeSlider.setBounds (wetSlider.getRight(), yDialStart, dialSize, dialSize);
+
+// pitchLabel.setBounds (190, 15, labelWidth, labelHeight);
+// pitchSlider.setBounds (190, 30, dialSize, dialSize);
+//
+// fmFreqLabel.setBounds (260, 15, labelWidth, labelHeight);
+// fmFreqSlider.setBounds (260, 30, dialSize, dialSize);
+//
+// fmDepthLabel.setBounds (330, 15, labelWidth, labelHeight);
+// fmDepthSlider.setBounds (330, 30, dialSize, dialSize);
+}
+
+using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+void ReverbComponent::setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts)
+{
+ slider.setSliderStyle (juce::Slider::SliderStyle::RotaryHorizontalVerticalDrag);
+ slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, textBoxWidth, textBoxHeight);
+ addAndMakeVisible (slider);
+
+ label.setFont (fontHeight);
+ label.setJustificationType (juce::Justification::centred);
+ addAndMakeVisible (label);
+
+ attachment = std::make_unique(apvts, paramId, slider);
+}
```
diff --git a/Source/UI/ReverbComponent.h b/Source/UI/ReverbComponent.h
new file mode 100644
```diff
index 0000000..7f8c068
--- /dev/null
+++ b/Source/UI/ReverbComponent.h
@@ -0,0 +1,58 @@
+/*
+ ==============================================================================
+
+ ReverbComponent.h
+ Created: 19 Feb 2021 9:18:11pm
+ Author: Joshua Hodge
+
+ ==============================================================================
+*/
+
+#pragma once
+
+#include
+
+//==============================================================================
+/*
+*/
+class ReverbComponent : public juce::Component
+{
+public:
+ ReverbComponent (juce::AudioProcessorValueTreeState& apvts, juce::String sizeId, juce::String dampingId, juce::String widthId, juce::String dryId, juce::String wetId, juce::String freezeId);
+ ~ReverbComponent() override;
+
+ void paint (juce::Graphics&) override;
+ void resized() override;
+
+private:
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ void setSliderParams (juce::Slider& slider, juce::Label& label, std::unique_ptr& attachment, juce::String paramId, juce::AudioProcessorValueTreeState& apvts);
+
+ juce::Slider sizeSlider;
+ juce::Slider dampingSlider;
+ juce::Slider widthSlider;
+ juce::Slider drySlider;
+ juce::Slider wetSlider;
+ juce::Slider freezeSlider;
+
+ juce::Label sizeLabel { "Size", "Size" };
+ juce::Label dampingLabel { "Damping", "Damping" };
+ juce::Label widthLabel { "Width", "Width" };
+ juce::Label dryLabel { "Dry", "Dry" };
+ juce::Label wetLabel { "Wet", "Wet" };
+ juce::Label freezeLabel { "Freeze", "Freeze" };
+
+ std::unique_ptr sizeAttachment;
+ std::unique_ptr dampingAttachment;
+ std::unique_ptr