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 widthAttachment; + std::unique_ptr wetAttachment; + std::unique_ptr dryAttachment; + std::unique_ptr freezeAttachment; + + static constexpr float fontHeight { 15.0f }; + static constexpr int textBoxWidth { 35 }; + static constexpr int textBoxHeight { 20 }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbComponent) +}; ```
diff --git a/tapSynth.jucer b/tapSynth.jucer index 910e80f..ed5da62 100644
```diff --- a/tapSynth.jucer +++ b/tapSynth.jucer @@ -1,7 +1,8 @@ + addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn" + cppLanguageStandard="17"> + + + + ```

Diff 10: 5e9bdd2f6d524047bc80c3c4a01854ecb5a859fd to 50b31071e6888577f0da4dddf1c4c2ede4cce0ea

diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index e0ebd83..b162f54 100644
```diff --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -32,8 +32,21 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess osc1.setName ("Oscillator 1"); osc2.setName ("Oscillator 2"); filter.setName ("Filter"); + lfo1.setName ("Filter LFO"); + filterAdsr.setName ("Filter ADSR"); + adsr.setName ("ADSR"); - setSize (830, 525); + auto oscColour = juce::Colour::fromRGB (247, 190, 67); + auto filterColour = juce::Colour::fromRGB (246, 87, 64); + + osc1.setBoundsColour (oscColour); + osc2.setBoundsColour (oscColour); + + filterAdsr.setBoundsColour (filterColour); + filter.setBoundsColour (filterColour); + lfo1.setBoundsColour (filterColour); + + setSize (1080, 525); } TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor() @@ -54,9 +67,9 @@ void TapSynthAudioProcessorEditor::resized() osc2.setBounds (0, osc1.getBottom(), oscWidth, oscHeight); filter.setBounds (osc1.getRight(), 0, 180, 200); lfo1.setBounds (osc2.getRight(), filter.getBottom(), 180, 160); - adsr.setBounds (filter.getRight(), 0, 230, 360); + filterAdsr.setBounds (filter.getRight(), 0, 230, 360); + adsr.setBounds (filterAdsr.getRight(), 0, 230, 360); reverb.setBounds (0, osc2.getBottom(), oscWidth, 150); - filterAdsr.setBounds (reverb.getRight(), lfo1.getBottom(), 180, 150); } ```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 4ddca45..ab7f055 100644
```diff --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -352,9 +352,8 @@ void TapSynthAudioProcessor::setFilterParams() for (int ch = 0; ch < numChannelsToProcess; ++ch) { lfo[ch].setFrequency (lfoFreq); - filterCutoff = (200.0f * adsrOutput) + filterCutoff; + //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); ```
diff --git a/Source/UI/AdsrComponent.cpp b/Source/UI/AdsrComponent.cpp index e03f69a..d7a7292 100644
```diff --- a/Source/UI/AdsrComponent.cpp +++ b/Source/UI/AdsrComponent.cpp @@ -13,67 +13,31 @@ //============================================================================== AdsrComponent::AdsrComponent (juce::AudioProcessorValueTreeState& apvts, juce::String attackId, juce::String decayId, juce::String sustainId, juce::String releaseId) +: attack ("A", attackId, apvts, sliderWidth, sliderHeight, juce::Slider::SliderStyle::LinearVertical) +, decay ("D", decayId, apvts, sliderWidth, sliderHeight, juce::Slider::SliderStyle::LinearVertical) +, sustain ("S", sustainId, apvts, sliderWidth, sliderHeight, juce::Slider::SliderStyle::LinearVertical) +, release ("R", releaseId, apvts, sliderWidth, sliderHeight, juce::Slider::SliderStyle::LinearVertical) { - setSliderParams (attackSlider, attackLabel, attackAttachment, attackId, apvts); - setSliderParams (decaySlider, decayLabel, decayAttachment, decayId, apvts); - setSliderParams (sustainSlider, sustainLabel, sustainAttachment, sustainId, apvts); - setSliderParams (releaseSlider, releaseLabel, releaseAttachment, releaseId, apvts); + addAndMakeVisible (attack); + addAndMakeVisible (decay); + addAndMakeVisible (sustain); + addAndMakeVisible (release); } 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 = 5; - const auto sliderWidth = bounds.getWidth() / 4 - padding; - 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); + const auto startX = 15; + const auto startY = 55; + const auto width = sliderWidth; + const auto height = sliderHeight + 20; - 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); + attack.setBounds (startX, startY, width, height); + decay.setBounds (attack.getRight(), startY, width, height); + sustain.setBounds (decay.getRight(), startY, width, height); + release.setBounds (sustain.getRight(), startY, width, height); } -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, 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 82b41f1..e972947 100644
```diff --- a/Source/UI/AdsrComponent.h +++ b/Source/UI/AdsrComponent.h @@ -11,42 +11,27 @@ #pragma once #include +#include "CustomComponent.h" //============================================================================== /* */ -class AdsrComponent : public juce::Component +class AdsrComponent : public CustomComponent { public: 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: - using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; + SliderWithLabel attack; + SliderWithLabel decay; + SliderWithLabel sustain; + SliderWithLabel release; - 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; - - 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 }; + static constexpr int sliderWidth = 50; + static constexpr int sliderHeight = 260; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AdsrComponent) }; ```
diff --git a/Source/UI/CustomComponent.cpp b/Source/UI/CustomComponent.cpp new file mode 100644
```diff index 0000000..c8df32b --- /dev/null +++ b/Source/UI/CustomComponent.cpp @@ -0,0 +1,81 @@ +/* + ============================================================================== + + CustomComponent.cpp + Created: 20 Feb 2021 9:51:27am + Author: Joshua Hodge + + ============================================================================== +*/ + +#include +#include "CustomComponent.h" + +//============================================================================== + +using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; +using SliderStyle = juce::Slider::SliderStyle; + +SliderWithLabel::SliderWithLabel (juce::String labelName, juce::String paramId, juce::AudioProcessorValueTreeState& apvts, const int width, const int height, SliderStyle style) +{ + sliderWidth = width; + sliderHeight = height; + + slider.setSliderStyle (style); + slider.setTextBoxStyle (juce::Slider::TextBoxBelow, true, textBoxWidth, textBoxHeight); + addAndMakeVisible (slider); + + label.setFont (fontHeight); + label.setText (labelName, juce::dontSendNotification); + label.setJustificationType (juce::Justification::centred); + addAndMakeVisible (label); + + attachment = std::make_unique(apvts, paramId, slider); +} + +void SliderWithLabel::resized() +{ + const auto dialToLabelRatio = 15; + const auto labelHeight = 18; + + jassert (sliderWidth > 0); + jassert (sliderHeight > 0); + + label.setBounds (0, 0, sliderWidth, labelHeight); + slider.setBounds (0, 0 + dialToLabelRatio, sliderWidth, sliderHeight); +} + + +CustomComponent::CustomComponent() +{ + // In your constructor, you should add any child components, and + // initialise any special settings that your component needs. + +} + +CustomComponent::~CustomComponent() +{ +} + +void CustomComponent::paint (juce::Graphics& g) +{ + g.fillAll (juce::Colours::black); + auto bounds = getLocalBounds(); + g.setColour (boundsColour); + g.drawRoundedRectangle (bounds.toFloat().reduced (10.0f), 5.0f, 2.0f); + + g.setColour (juce::Colours::yellow); + g.setFont (fontHeight); + g.setFont (g.getCurrentFont().boldened()); + + jassert (name.isNotEmpty()); + g.drawText (name, 20, 15, 100, 25, juce::Justification::left); +} + +void CustomComponent::resized() +{ + // This method is where you should set the bounds of any child + // components that your component contains.. + +} + ```
diff --git a/Source/UI/CustomComponent.h b/Source/UI/CustomComponent.h new file mode 100644
```diff index 0000000..e706b78 --- /dev/null +++ b/Source/UI/CustomComponent.h @@ -0,0 +1,58 @@ +/* + ============================================================================== + + CustomComponent.h + Created: 20 Feb 2021 9:51:27am + Author: Joshua Hodge + + ============================================================================== +*/ + +#pragma once + +#include + +//============================================================================== +/* +*/ + +static constexpr float fontHeight { 15.0f }; + +class SliderWithLabel : public juce::Component +{ +public: + using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment; + using SliderStyle = juce::Slider::SliderStyle; + + SliderWithLabel (juce::String labelName, juce::String paramId, juce::AudioProcessorValueTreeState& apvts, const int width, const int height, SliderStyle style = SliderStyle::RotaryHorizontalVerticalDrag); + + void resized() override; + +private: + static constexpr int textBoxWidth { 48 }; + static constexpr int textBoxHeight { 20 }; + int sliderWidth { 0 }; + int sliderHeight { 0 }; + juce::Slider slider; + juce::Label label; + std::unique_ptr attachment; +}; + +class CustomComponent : public juce::Component +{ +public: + CustomComponent(); + ~CustomComponent() override; + + void paint (juce::Graphics&) override; + void resized() override; + + void setName (juce::String n) { name = n; } + void setBoundsColour (juce::Colour c) { boundsColour = c; } + +private: + juce::String name { "" }; + juce::Colour boundsColour { juce::Colours::white }; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CustomComponent) +}; ```
diff --git a/Source/UI/FilterComponent.cpp b/Source/UI/FilterComponent.cpp index e336eb3..805119b 100644
```diff --- a/Source/UI/FilterComponent.cpp +++ b/Source/UI/FilterComponent.cpp @@ -13,6 +13,8 @@ //============================================================================== FilterComponent::FilterComponent (juce::AudioProcessorValueTreeState& apvts, juce::String filterTypeId, juce::String cutoffId, juce::String resonanceId) +: cutoff ("Cutoff", cutoffId, apvts, dialWidth, dialHeight) +, resonance ("Resonance", resonanceId, apvts, dialWidth, dialHeight) { juce::StringArray filterTypeChoices { "Low Pass", "Band Pass", "High Pass" }; filterTypeSelector.addItemList (filterTypeChoices, 1); @@ -21,53 +23,24 @@ FilterComponent::FilterComponent (juce::AudioProcessorValueTreeState& apvts, juc filterTypeAttachment = std::make_unique(apvts, filterTypeId, filterTypeSelector); - setSliderParams (cutoffSlider, cutoffLabel, cutoffAttachment, cutoffId, apvts); - setSliderParams (resonanceSlider, resonanceLabel, resonanceAttachment, resonanceId, apvts); + addAndMakeVisible (cutoff); + addAndMakeVisible (resonance); } 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; + const auto startY = 80; + const auto width = 70; + const auto height = 88; - 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); + filterTypeSelector.setBounds (18, 40, 145, 25); + cutoff.setBounds (startX, startY, width, height); + resonance.setBounds (cutoff.getRight(), startY, width, height); } -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 index aced363..98ce759 100644
```diff --- a/Source/UI/FilterComponent.h +++ b/Source/UI/FilterComponent.h @@ -11,41 +11,28 @@ #pragma once #include +#include "CustomComponent.h" //============================================================================== /* */ -class FilterComponent : public juce::Component +class FilterComponent : public CustomComponent { 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 }; + SliderWithLabel cutoff; + SliderWithLabel resonance; + + static constexpr int dialWidth = 70; + static constexpr int dialHeight = 70; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FilterComponent) }; ```
diff --git a/Source/UI/LfoComponent.cpp b/Source/UI/LfoComponent.cpp index 34a4cd9..4882a85 100644
```diff --- a/Source/UI/LfoComponent.cpp +++ b/Source/UI/LfoComponent.cpp @@ -13,49 +13,24 @@ //============================================================================== LfoComponent::LfoComponent (juce::AudioProcessorValueTreeState& apvts, juce::String lfoFreqId, juce::String lfoDepthId) +: lfoFreq ("LFO Freq", lfoFreqId, apvts, dialWidth, dialHeight) +, lfoDepth ("LFO Depth", lfoDepthId, apvts, dialWidth, dialHeight) { - setSliderParams (lfoFreqSlider, lfoFreqLabel, lfoFreqAttachment, lfoFreqId, apvts); - setSliderParams (lfoDepthSlider, lfoDepthLabel, lfoDepthAttachment, lfoDepthId, apvts); + addAndMakeVisible (lfoFreq); + addAndMakeVisible (lfoDepth); } 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; + const auto width = 70; + const auto height = 88; + const auto startY = 40; - lfoFreqLabel.setBounds (18, 40, labelWidth, labelHeight); - lfoFreqSlider.setBounds (18, 55, dialSize, dialSize); - lfoDepthLabel.setBounds (90, 40, labelWidth, labelHeight); - lfoDepthSlider.setBounds (90, 55, dialSize, dialSize); + lfoFreq.setBounds (18, startY, width, height); + lfoDepth.setBounds (90, startY, width, height); } -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 index eea85b9..9e1ab78 100644
```diff --- a/Source/UI/LfoComponent.h +++ b/Source/UI/LfoComponent.h @@ -11,17 +11,17 @@ #pragma once #include +#include "CustomComponent.h" //============================================================================== /* */ -class LfoComponent : public juce::Component +class LfoComponent : public CustomComponent { public: LfoComponent (juce::AudioProcessorValueTreeState& apvts, juce::String lfoFreqId, juce::String lfoDepthId); ~LfoComponent() override; - void paint (juce::Graphics&) override; void resized() override; private: @@ -38,9 +38,11 @@ private: std::unique_ptr lfoFreqAttachment; std::unique_ptr lfoDepthAttachment; - static constexpr float fontHeight { 15.0f }; - static constexpr int textBoxWidth { 50 }; - static constexpr int textBoxHeight { 20 }; + SliderWithLabel lfoFreq; + SliderWithLabel lfoDepth; + + static constexpr int dialWidth = 70; + static constexpr int dialHeight = 70; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LfoComponent) }; ```
diff --git a/Source/UI/OscComponent.cpp b/Source/UI/OscComponent.cpp index f5f3109..e259170 100644
```diff --- a/Source/UI/OscComponent.cpp +++ b/Source/UI/OscComponent.cpp @@ -13,6 +13,10 @@ //============================================================================== OscComponent::OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::String oscId, juce::String gainId, juce::String pitchId, juce::String fmFreqId, juce::String fmDepthId) +: gain ("Gain", gainId, apvts, dialWidth, dialHeight) +, pitch ("Pitch", pitchId, apvts, dialWidth, dialHeight) +, fmFreq ("FM Freq", fmFreqId, apvts, dialWidth, dialHeight) +, fmDepth ("FM Depth", fmDepthId, apvts, dialWidth, dialHeight) { juce::StringArray oscChoices { "Sine", "Saw", "Square" }; oscSelector.addItemList (oscChoices, 1); @@ -21,61 +25,26 @@ OscComponent::OscComponent (juce::AudioProcessorValueTreeState& apvts, juce::Str oscSelAttachment = std::make_unique(apvts, oscId, oscSelector); - setSliderParams (gainSlider, gainLabel, gainAttachment, gainId, apvts); - setSliderParams (pitchSlider, pitchLabel, pitchAttachment, pitchId, apvts); - setSliderParams (fmFreqSlider, fmFreqLabel, fmFreqAttachment, fmFreqId, apvts); - setSliderParams (fmDepthSlider, fmDepthLabel, fmDepthAttachment, fmDepthId, apvts); + addAndMakeVisible (gain); + addAndMakeVisible (pitch); + addAndMakeVisible (fmFreq); + addAndMakeVisible (fmDepth); } 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() { - const auto dialSize = 70; - const auto labelWidth = 70; - const auto labelHeight = 18; + const auto yStart = 15; + const auto width = 70; + const auto height = 88; 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); + gain.setBounds (120, yStart, width, height); + pitch.setBounds (190, yStart, width, height); + fmFreq.setBounds (260, yStart, width, height); + fmDepth.setBounds (330, yStart, width, height); } ```
diff --git a/Source/UI/OscComponent.h b/Source/UI/OscComponent.h index c194ec3..410a7d0 100644
```diff --- a/Source/UI/OscComponent.h +++ b/Source/UI/OscComponent.h @@ -11,48 +11,30 @@ #pragma once #include +#include "CustomComponent.h" //============================================================================== /* */ -class OscComponent : public juce::Component +class OscComponent : public CustomComponent { public: 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 fmFreqAttachment; - std::unique_ptr fmDepthAttachment; - juce::String name { "" }; + SliderWithLabel gain; + SliderWithLabel pitch; + SliderWithLabel fmFreq; + SliderWithLabel fmDepth; - static constexpr float fontHeight { 15.0f }; - static constexpr int textBoxWidth { 35 }; - static constexpr int textBoxHeight { 20 }; + static constexpr int dialWidth = 70; + static constexpr int dialHeight = 70; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscComponent) }; ```
diff --git a/Source/UI/ReverbComponent.cpp b/Source/UI/ReverbComponent.cpp index c8783b6..85c407f 100644
```diff --- a/Source/UI/ReverbComponent.cpp +++ b/Source/UI/ReverbComponent.cpp @@ -13,80 +13,38 @@ //============================================================================== ReverbComponent::ReverbComponent (juce::AudioProcessorValueTreeState& apvts, juce::String sizeId, juce::String dampingId, juce::String widthId, juce::String dryId, juce::String wetId, juce::String freezeId) +: size ("Size", sizeId, apvts, dialWidth, dialHeight) +, damping ("Damping", dampingId, apvts, dialWidth, dialHeight) +, stereoWidth ("Width", widthId, apvts, dialWidth, dialHeight) +, dry ("Dry", dryId, apvts, dialWidth, dialHeight) +, wet ("Wet", wetId, apvts, dialWidth, dialHeight) +, freeze ("Freeze", freezeId, apvts, dialWidth, dialHeight) { - 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); + addAndMakeVisible (size); + addAndMakeVisible (damping); + addAndMakeVisible (stereoWidth); + addAndMakeVisible (dry); + addAndMakeVisible (wet); + addAndMakeVisible (freeze); + + setName ("Reverb"); } 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); + const auto yStart = 40; + const auto width = 67; + const auto height = 88; + + size.setBounds (10, yStart, width, height); + damping.setBounds (size.getRight(), yStart, width, height); + stereoWidth.setBounds (damping.getRight(), yStart, width, height); + dry.setBounds (stereoWidth.getRight(), yStart, width, height); + wet.setBounds (dry.getRight(), yStart, width, height); + freeze.setBounds (wet.getRight(), yStart, width, height); } -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 index 7f8c068..1597e22 100644
```diff --- a/Source/UI/ReverbComponent.h +++ b/Source/UI/ReverbComponent.h @@ -11,48 +11,29 @@ #pragma once #include +#include "CustomComponent.h" //============================================================================== /* */ -class ReverbComponent : public juce::Component +class ReverbComponent : public CustomComponent { 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 widthAttachment; - std::unique_ptr wetAttachment; - std::unique_ptr dryAttachment; - std::unique_ptr freezeAttachment; + SliderWithLabel size; + SliderWithLabel damping; + SliderWithLabel stereoWidth; + SliderWithLabel dry; + SliderWithLabel wet; + SliderWithLabel freeze; - static constexpr float fontHeight { 15.0f }; - static constexpr int textBoxWidth { 35 }; - static constexpr int textBoxHeight { 20 }; + static constexpr int dialWidth = 67; + static constexpr int dialHeight = 67; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ReverbComponent) }; ```
diff --git a/tapSynth.jucer b/tapSynth.jucer index ed5da62..8341de6 100644
```diff --- a/tapSynth.jucer +++ b/tapSynth.jucer @@ -27,6 +27,10 @@ + + Diff 11: 50b31071e6888577f0da4dddf1c4c2ede4cce0ea to b4816f92f13bccd46db0111403eaa01d74f3041b
diff --git a/Source/Data/FilterData.cpp b/Source/Data/FilterData.cpp index 646b49c..815d85b 100644
```diff --- a/Source/Data/FilterData.cpp +++ b/Source/Data/FilterData.cpp @@ -10,6 +10,11 @@ #include "FilterData.h" +FilterData::FilterData() +{ + setType (juce::dsp::StateVariableTPTFilterType::lowpass); +} + void FilterData::setParams (const int filterType, const float filterCutoff, const float filterResonance) { selectFilterType (filterType); @@ -25,11 +30,11 @@ void FilterData::setLfoParams (const float freq, const float depth) void FilterData::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels) { + resetAll(); juce::dsp::ProcessSpec spec; spec.maximumBlockSize = samplesPerBlock; spec.sampleRate = sampleRate; spec.numChannels = outputChannels; - prepare (spec); } @@ -66,3 +71,9 @@ float FilterData::processNextSample (int channel, float inputValue) { return processSample (channel, inputValue); } + +void FilterData::resetAll() +{ + reset(); + lfo.reset(); +} ```
diff --git a/Source/Data/FilterData.h b/Source/Data/FilterData.h index cecaede..67d573c 100644
```diff --- a/Source/Data/FilterData.h +++ b/Source/Data/FilterData.h @@ -17,11 +17,13 @@ class FilterData : public juce::dsp::StateVariableTPTFilter { public: + FilterData(); 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); + void resetAll(); private: void selectFilterType (const int type); ```
diff --git a/Source/Data/OscData.cpp b/Source/Data/OscData.cpp index 89fba49..57b5f17 100644
```diff --- a/Source/Data/OscData.cpp +++ b/Source/Data/OscData.cpp @@ -12,6 +12,8 @@ void OscData::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels) { + resetAll(); + juce::dsp::ProcessSpec spec; spec.maximumBlockSize = samplesPerBlock; spec.sampleRate = sampleRate; @@ -93,3 +95,10 @@ void OscData::setParams (const int oscChoice, const float oscGain, const int osc setOscPitch (oscPitch); setFmOsc (fmFreq, fmDepth); } + +void OscData::resetAll() +{ + reset(); + fmOsc.reset(); + gain.reset(); +} ```
diff --git a/Source/Data/OscData.h b/Source/Data/OscData.h index 91c829b..32fae2b 100644
```diff --- a/Source/Data/OscData.h +++ b/Source/Data/OscData.h @@ -24,6 +24,7 @@ public: 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); + void resetAll(); private: juce::dsp::Oscillator fmOsc { [](float x) { return std::sin (x); }}; ```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index ab7f055..c7fe9c7 100644
```diff --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -25,15 +25,10 @@ TapSynthAudioProcessor::TapSynthAudioProcessor() { synth.addSound (new SynthSound()); - for (int i = 0; i < numVoices; i++) + for (int i = 0; i < 5; i++) { synth.addVoice (new SynthVoice()); } - - for (int ch = 0; ch < numChannelsToProcess; ++ch) - { - filter[ch].setType (juce::dsp::StateVariableTPTFilterType::lowpass); - } } TapSynthAudioProcessor::~TapSynthAudioProcessor() @@ -121,13 +116,6 @@ void TapSynthAudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlo 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; @@ -180,18 +168,6 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc setParams(); synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples()); - - for (int ch = 0; ch < numChannelsToProcess; ++ch) - { - 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)); } @@ -268,10 +244,10 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea 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)); + params.push_back (std::make_unique("FILTERATTACK", "Filter Attack", juce::NormalisableRange { 0.0f, 1.0f, 0.01f }, 0.01f)); + params.push_back (std::make_unique("FILTERDECAY", "Filter Decay", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.1f)); + params.push_back (std::make_unique("FILTERSUSTAIN", "Filter Sustain", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 1.0f)); + params.push_back (std::make_unique("FILTERRELEASE", "Filter Release", juce::NormalisableRange { 0.0f, 3.0f, 0.1f }, 0.1f)); // Reverb params.push_back (std::make_unique("REVERBSIZE", "Reverb Size", juce::NormalisableRange { 0.0f, 1.0f, 0.1f }, 0.0f, "")); @@ -341,22 +317,16 @@ void TapSynthAudioProcessor::setFilterParams() auto& filterType = *apvts.getRawParameterValue ("FILTERTYPE"); auto& filterCutoff = *apvts.getRawParameterValue ("FILTERCUTOFF"); auto& filterResonance = *apvts.getRawParameterValue ("FILTERRESONANCE"); - + auto& adsrDepth = *apvts.getRawParameterValue ("FILTERADSRDEPTH"); 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) + + for (int i = 0; i < synth.getNumVoices(); ++i) { - lfo[ch].setFrequency (lfoFreq); - //filterCutoff = (200.0f * adsrOutput) + filterCutoff; - filterCutoff = (lfoDepth * lfoOutput[ch]) + filterCutoff; - auto cutoff = std::clamp (filterCutoff, 20.0f, 20000.0f); - - filter[ch].setParams (filterType, cutoff, filterResonance); + if (auto voice = dynamic_cast(synth.getVoice(i))) + { + voice->updateModParams (filterType, filterCutoff, filterResonance, adsrDepth, lfoFreq, lfoDepth); + } } } ```
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 4b5af6b..12372da 100644
```diff --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -11,8 +11,6 @@ #include #include "SynthVoice.h" #include "SynthSound.h" -#include "Data/FilterData.h" -#include "Data/AdsrData.h" //============================================================================== /** @@ -62,7 +60,6 @@ public: private: static constexpr int numChannelsToProcess { 2 }; juce::Synthesiser synth; - std::array filter; juce::AudioProcessorValueTreeState::ParameterLayout createParams(); void setParams(); @@ -71,8 +68,6 @@ private: 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; ```
diff --git a/Source/SynthVoice.cpp b/Source/SynthVoice.cpp index 50951da..d263045 100644
```diff --- a/Source/SynthVoice.cpp +++ b/Source/SynthVoice.cpp @@ -10,7 +10,6 @@ #include "SynthVoice.h" - bool SynthVoice::canPlaySound (juce::SynthesiserSound* sound) { return dynamic_cast(sound) != nullptr; @@ -49,6 +48,8 @@ void SynthVoice::pitchWheelMoved (int newPitchWheelValue) void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels) { + reset(); + adsr.setSampleRate (sampleRate); filterAdsr.setSampleRate (sampleRate); @@ -57,10 +58,13 @@ void SynthVoice::prepareToPlay (double sampleRate, int samplesPerBlock, int outp spec.sampleRate = sampleRate; spec.numChannels = outputChannels; - for (int i = 0; i < 2; i++) + for (int ch = 0; ch < numChannelsToProcess; ch++) { - osc1[i].prepareToPlay (sampleRate, samplesPerBlock, outputChannels); - osc2[i].prepareToPlay (sampleRate, samplesPerBlock, outputChannels); + osc1[ch].prepareToPlay (sampleRate, samplesPerBlock, outputChannels); + osc2[ch].prepareToPlay (sampleRate, samplesPerBlock, outputChannels); + filter[ch].prepareToPlay (sampleRate, samplesPerBlock, outputChannels); + lfo[ch].prepare (spec); + lfo[ch].initialise ([](float x) { return std::sin (x); }); } gain.prepare (spec); @@ -83,22 +87,31 @@ void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int synthBuffer.clear(); - for (int i = 0; i < synthBuffer.getNumChannels(); ++i) + for (int ch = 0; ch < synthBuffer.getNumChannels(); ++ch) { - auto* buffer = synthBuffer.getWritePointer (i, 0); + auto* buffer = synthBuffer.getWritePointer (ch, 0); - for (int j = 0; j < synthBuffer.getNumSamples(); ++j) + for (int s = 0; s < synthBuffer.getNumSamples(); ++s) { - buffer[j] = osc1[i].processNextSample (buffer[j]) + osc2[i].processNextSample (buffer[j]); + buffer[s] = osc1[ch].processNextSample (buffer[s]) + osc2[ch].processNextSample (buffer[s]); } } juce::dsp::AudioBlock audioBlock { synthBuffer }; - gain.process (juce::dsp::ProcessContextReplacing (audioBlock)); - adsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples()); + for (int ch = 0; ch < synthBuffer.getNumChannels(); ++ch) + { + auto* buffer = synthBuffer.getWritePointer (ch, 0); + + for (int s = 0; s < synthBuffer.getNumSamples(); ++s) + { + //lfoOutput[ch] = lfo[ch].processSample (synthBuffer.getSample (ch, s)); + buffer[s] = filter[ch].processNextSample (ch, synthBuffer.getSample (ch, s)); + } + } + for (int channel = 0; channel < outputBuffer.getNumChannels(); ++channel) { outputBuffer.addFrom (channel, startSample, synthBuffer, channel, 0, numSamples); @@ -107,3 +120,33 @@ void SynthVoice::renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int clearCurrentNote(); } } + +void SynthVoice::reset() +{ + gain.reset(); + adsr.reset(); + filterAdsr.reset(); +} + +void SynthVoice::updateModParams (const int filterType, const float filterCutoff, const float filterResonance, const float adsrDepth, const float lfoFreq, const float lfoDepth) +{ + auto cutoff = (adsrDepth * filterAdsrOutput) + filterCutoff; + cutoff = std::clamp (cutoff, 20.0f, 20000.0f); + + for (int ch = 0; ch < numChannelsToProcess; ++ch) + { + filter[ch].setParams (filterType, cutoff, filterResonance); + } + +// auto cutoff = (adsrDepth * adsr.getNextSample()) + filterCutoff; +// +// DBG (cutoff); +// +// for (int ch = 0; ch < numChannelsToProcess; ++ch) +// { +// lfo[ch].setFrequency (lfoFreq); +// //cutoff = (lfoDepth * lfoOutput[ch]) + cutoff; +// cutoff = std::clamp (cutoff, 20.0f, 20000.0f); +// filter[ch].setParams (filterType, cutoff, filterResonance); +// } +} ```
diff --git a/Source/SynthVoice.h b/Source/SynthVoice.h index 26e8963..d947a99 100644
```diff --- a/Source/SynthVoice.h +++ b/Source/SynthVoice.h @@ -13,6 +13,7 @@ #include #include "SynthSound.h" #include "Data/OscData.h" +#include "Data/FilterData.h" #include "Data/AdsrData.h" class SynthVoice : public juce::SynthesiserVoice @@ -26,19 +27,27 @@ public: void prepareToPlay (double sampleRate, int samplesPerBlock, int outputChannels); void renderNextBlock (juce::AudioBuffer< float > &outputBuffer, int startSample, int numSamples) override; + void reset(); + std::array& getOscillator1() { return osc1; } std::array& getOscillator2() { return osc2; } AdsrData& getAdsr() { return adsr; } AdsrData& getFilterAdsr() { return filterAdsr; } float getFilterAdsrOutput() { return filterAdsrOutput; } + void updateModParams (const int filterType, const float filterCutoff, const float filterResonance, const float adsrDepth, const float lfoFreq, const float lfoDepth); private: - std::array osc1; - std::array osc2; + static constexpr int numChannelsToProcess { 2 }; + std::array osc1; + std::array osc2; + std::array filter; + std::array, numChannelsToProcess> lfo; AdsrData adsr; AdsrData filterAdsr; juce::AudioBuffer synthBuffer; float filterAdsrOutput { 0.0f }; + std::array lfoOutput { 0.0f, 0.0f }; + juce::dsp::Gain gain; bool isPrepared { false }; ```

Diff 12: b4816f92f13bccd46db0111403eaa01d74f3041b to e8bccebc9afa79aa9ee138b2469365e480025819

diff --git a/Assets/tapLogo.png b/Assets/tapLogo.png new file mode 100644
```diff index 0000000..6236100 Binary files /dev/null and b/Assets/tapLogo.png differ ```
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index b162f54..263cad3 100644
```diff --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -21,6 +21,13 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess , filterAdsr (audioProcessor.apvts, "FILTERATTACK", "FILTERDECAY", "FILTERSUSTAIN", "FILTERRELEASE") , reverb (audioProcessor.apvts, "REVERBSIZE", "REVERBDAMPING", "REVERBWIDTH", "REVERBDRY", "REVERBWET", "REVERBFREEZE") { + auto tapImage = juce::ImageCache::getFromMemory (BinaryData::tapLogo_png, BinaryData::tapLogo_pngSize); + + if (tapImage.isValid()) + logo.setImage (tapImage, juce::RectanglePlacement::stretchToFit); + else + jassertfalse; + addAndMakeVisible (osc1); addAndMakeVisible (osc2); addAndMakeVisible (filter); @@ -28,6 +35,8 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess addAndMakeVisible (lfo1); addAndMakeVisible (filterAdsr); addAndMakeVisible (reverb); + addAndMakeVisible (meter); + addAndMakeVisible (logo); osc1.setName ("Oscillator 1"); osc2.setName ("Oscillator 2"); @@ -35,6 +44,7 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess lfo1.setName ("Filter LFO"); filterAdsr.setName ("Filter ADSR"); adsr.setName ("ADSR"); + meter.setName ("Meter"); auto oscColour = juce::Colour::fromRGB (247, 190, 67); auto filterColour = juce::Colour::fromRGB (246, 87, 64); @@ -57,6 +67,7 @@ TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor() void TapSynthAudioProcessorEditor::paint (juce::Graphics& g) { g.fillAll (juce::Colours::black); + } void TapSynthAudioProcessorEditor::resized() @@ -70,6 +81,8 @@ void TapSynthAudioProcessorEditor::resized() filterAdsr.setBounds (filter.getRight(), 0, 230, 360); adsr.setBounds (filterAdsr.getRight(), 0, 230, 360); reverb.setBounds (0, osc2.getBottom(), oscWidth, 150); + meter.setBounds (reverb.getRight(), osc2.getBottom(), filterAdsr.getWidth() + lfo1.getWidth(), 150); + logo.setBounds (meter.getRight(), osc2.getBottom() + 30, 250, 100); } ```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 08db2b9..07f853a 100644
```diff --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -15,6 +15,8 @@ #include "UI/AdsrComponent.h" #include "UI/LfoComponent.h" #include "UI/ReverbComponent.h" +#include "UI/MeterComponent.h" +#include "UI/Assets.h" //============================================================================== /** @@ -38,6 +40,8 @@ private: LfoComponent lfo1; AdsrComponent filterAdsr; ReverbComponent reverb; + MeterComponent meter; + juce::ImageComponent logo; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor) }; ```
diff --git a/Source/UI/Assets.cpp b/Source/UI/Assets.cpp new file mode 100644
```diff index 0000000..5c73492 --- /dev/null +++ b/Source/UI/Assets.cpp @@ -0,0 +1,766 @@ +/* (Auto-generated binary data file). */ + +#include "Assets.h" + +static const unsigned char temp1[] = {137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,4,135,0,0,1,134,8,6,0,0,0,225,21,225,153,0,0,1,129,105,67,67, + 80,115,82,71,66,32,73,69,67,54,49,57,54,54,45,50,46,49,0,0,40,145,117,145,207,43,68,81,20,199,63,51,200,196,104,132,98,97,49,105, + 88,13,141,81,19,27,101,36,212,164,105,140,50,216,204,188,249,165,230,199,235,189,145,100,171,108,21,37,54,126,45,248,11,216,42,107,165,136,148,44, + 172,172,137,13,122,206,155,153,154,73,230,220,206,61,159,251,189,247,156,238,61,23,172,225,140,146,213,235,61,144,205,21,180,208,164,223,57,31,89,112, + 54,190,96,165,141,78,108,216,162,138,174,142,5,131,1,106,218,231,61,22,51,222,246,155,181,106,159,251,215,154,227,9,93,1,139,77,120,84,81,181, + 130,240,148,112,96,181,160,154,188,35,220,161,164,163,113,225,51,97,183,38,23,20,190,51,245,88,137,95,77,78,149,248,219,100,45,28,26,7,107,171, + 176,51,85,197,177,42,86,210,90,86,88,94,142,43,155,89,81,202,247,49,95,98,79,228,230,102,37,246,136,119,163,19,98,18,63,78,166,153,96,28, + 31,131,140,200,236,163,31,47,3,178,162,70,190,167,152,63,67,94,114,21,153,85,214,208,88,38,69,154,2,110,81,87,164,122,66,98,82,244,132,140, + 12,107,102,255,255,246,85,79,14,121,75,213,237,126,104,120,54,140,247,94,104,220,134,159,45,195,248,58,50,140,159,99,168,123,130,203,92,37,63,127, + 8,195,31,162,111,85,52,215,1,56,54,224,252,170,162,197,118,225,98,19,186,30,213,168,22,45,74,117,226,214,100,18,222,78,161,37,2,237,55,208, + 180,88,234,89,121,159,147,7,8,175,203,87,93,195,222,62,244,201,121,199,210,47,240,222,103,176,216,105,238,46,0,0,0,9,112,72,89,115,0,0, + 11,19,0,0,11,19,1,0,154,156,24,0,0,32,0,73,68,65,84,120,156,236,221,119,252,35,85,189,255,241,215,97,43,11,27,134,94,133,5,68, + 41,138,168,16,81,80,17,91,4,84,20,44,87,197,30,123,187,196,138,122,245,170,215,118,141,216,245,154,43,118,84,46,34,22,52,10,250,195,134,48, + 22,122,89,64,233,189,13,67,221,122,126,127,204,124,225,187,217,153,228,156,100,242,157,148,247,243,241,200,131,221,204,204,153,207,134,239,55,153,124,230, + 156,207,7,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68, + 68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,68,198,143,41,59,0,17, + 17,17,17,25,111,214,218,141,129,71,205,122,236,14,44,28,242,105,87,2,23,3,231,164,143,243,140,49,247,14,249,156,34,34,34,19,73,201,33,17, + 17,41,133,181,54,0,158,4,236,147,62,246,6,150,14,241,148,171,129,75,129,115,211,71,104,140,185,112,136,231,195,90,123,58,176,199,0,67,28,107, + 140,249,164,199,249,46,0,182,28,224,124,223,48,198,28,51,192,241,179,99,185,14,152,95,196,88,125,88,3,220,1,220,12,92,13,156,14,252,198,24, + 115,221,176,78,104,173,125,58,240,189,97,141,95,160,115,141,49,207,40,106,48,107,173,1,222,10,124,28,216,168,168,113,251,20,1,13,99,204,113,37, + 199,33,34,34,50,118,202,186,104,19,17,145,41,102,173,61,2,248,10,176,213,28,159,122,59,224,160,89,113,124,19,56,218,24,19,13,233,124,91,48, + 216,191,113,99,207,253,183,28,240,124,149,1,142,237,180,53,48,175,192,241,124,109,11,236,153,254,249,229,0,214,218,83,129,15,27,99,206,24,194,249, + 22,49,247,63,207,253,216,188,168,129,172,181,187,1,199,1,7,22,53,230,128,2,224,27,214,218,23,1,117,99,204,213,101,7,36,34,34,50,46,54, + 40,59,0,17,17,153,30,214,218,205,173,181,63,2,78,100,52,190,72,191,10,184,208,90,123,88,217,129,200,156,120,58,240,103,107,237,143,210,101,80, + 210,39,107,237,54,192,89,140,78,98,104,182,103,0,103,166,179,19,69,68,68,196,129,146,67,34,34,50,151,126,0,188,176,236,32,58,108,7,156,108, + 173,173,150,29,136,204,153,23,2,127,177,214,46,43,59,144,49,246,37,96,211,178,131,232,98,91,224,216,178,131,16,17,17,25,23,74,14,137,136,200, + 156,176,214,190,130,100,230,198,40,154,7,252,175,181,118,65,217,129,200,156,121,4,240,75,107,237,48,235,92,77,36,107,237,243,129,35,202,142,195,193, + 43,173,181,207,44,59,8,17,17,145,113,160,228,144,136,136,12,157,181,118,43,224,179,101,199,209,195,35,129,247,150,29,132,204,169,61,128,239,148,29, + 196,24,250,80,217,1,120,248,72,217,1,136,136,136,140,3,37,135,68,68,100,46,28,5,108,86,118,16,14,142,46,59,0,153,115,135,167,157,198,196, + 129,181,118,17,15,22,250,30,7,123,91,107,203,44,140,46,34,34,50,22,148,28,18,17,145,185,240,168,178,3,112,20,88,107,119,44,59,8,153,115, + 31,46,59,128,49,178,39,227,213,237,118,49,176,123,217,65,136,136,136,140,58,37,135,68,68,100,46,140,75,114,8,96,239,178,3,144,57,247,4,107, + 237,94,101,7,49,38,198,233,119,121,198,62,101,7,32,34,34,50,234,148,28,18,17,145,161,74,139,60,143,211,157,251,113,252,242,43,131,123,70,217, + 1,140,137,157,202,14,160,15,203,202,14,64,68,68,100,212,41,57,36,34,34,195,182,57,176,176,236,32,60,108,87,118,0,82,138,167,150,29,192,152, + 48,101,7,208,7,93,239,138,136,136,244,160,15,75,17,17,25,182,113,251,50,57,110,241,74,49,150,149,29,128,136,136,136,72,89,198,169,160,160,136, + 136,136,200,176,108,89,118,0,83,228,70,224,22,199,125,119,0,54,29,98,44,34,34,34,130,146,67,34,34,34,34,144,44,127,28,196,141,192,79,251, + 56,238,185,3,156,243,18,96,185,231,49,151,15,112,190,34,196,192,30,198,152,200,101,103,107,237,238,192,197,195,13,73,68,68,68,148,28,18,17,145, + 113,112,45,240,69,224,31,93,246,217,24,120,57,240,188,57,137,72,134,237,29,192,201,14,251,109,9,188,2,120,3,131,93,215,204,27,224,88,140,49, + 127,3,14,247,61,206,90,187,134,254,151,249,255,192,24,243,145,62,143,45,203,77,174,137,33,0,99,204,37,214,218,171,129,29,135,24,147,136,136,200, + 212,83,114,72,68,68,198,193,155,140,49,63,119,216,239,100,107,237,185,168,29,253,36,184,205,24,115,149,195,126,87,1,127,179,214,238,4,60,123,200, + 49,201,224,108,31,199,172,44,60,10,17,17,17,89,135,10,82,139,136,200,168,91,9,252,202,99,255,126,150,246,200,248,251,73,217,1,136,136,136,136, + 140,43,37,135,68,68,100,212,173,54,198,172,246,216,255,190,161,69,34,163,76,255,223,69,68,68,68,250,164,228,144,136,136,136,136,136,136,136,200,20, + 83,114,72,68,68,68,68,68,68,68,100,138,41,57,36,34,34,34,34,34,34,34,50,197,148,28,18,17,17,17,17,17,17,17,153,98,74,14,137,136, + 136,136,136,136,136,136,76,49,37,135,68,68,68,68,68,68,68,68,166,152,146,67,34,34,34,34,34,34,34,34,83,76,201,33,17,17,17,17,17,17, + 17,145,41,166,228,144,136,136,136,136,136,136,136,200,20,83,114,72,68,68,68,68,68,68,68,100,138,41,57,36,34,34,34,34,34,34,34,50,197,148, + 28,18,17,17,17,17,17,17,17,153,98,74,14,137,136,136,136,136,136,136,136,76,49,37,135,68,68,68,68,68,68,68,68,166,152,146,67,34,34,34, + 34,34,34,34,34,83,76,201,33,17,17,17,17,17,17,17,145,41,166,228,144,136,136,136,136,136,136,136,200,20,83,114,72,68,68,68,68,68,68,68, + 100,138,41,57,36,34,34,34,34,34,34,34,50,197,148,28,18,17,17,17,17,17,17,17,153,98,74,14,137,136,136,136,136,136,136,136,76,49,37,135, + 68,68,68,68,68,68,68,68,166,152,146,67,34,34,34,34,34,34,34,34,83,76,201,33,17,17,17,17,17,17,17,145,41,166,228,144,136,136,136,136, + 136,136,136,200,20,83,114,72,68,68,68,68,68,68,68,100,138,205,47,59,0,17,145,97,177,214,110,5,188,112,72,195,255,194,24,115,101,199,249,30, + 7,236,215,227,184,95,27,99,46,203,218,96,173,221,25,56,180,199,241,103,27,99,254,156,113,236,54,192,145,61,142,245,113,169,49,230,55,5,142,39, + 34,34,34,34,34,35,74,201,33,17,153,100,59,1,95,28,210,216,87,1,87,118,60,119,24,240,129,30,199,189,4,200,76,14,1,123,211,59,222,207, + 2,235,37,135,128,157,29,142,245,241,67,64,201,33,17,17,17,17,145,41,160,101,101,34,34,34,34,34,34,34,34,83,76,201,33,17,17,17,17,17, + 17,17,145,41,166,228,144,136,136,136,136,136,136,136,200,20,83,114,72,68,68,68,68,68,68,68,100,138,41,57,36,34,34,34,34,34,34,34,50,197, + 148,28,18,17,17,17,17,17,17,17,153,98,106,101,47,34,147,236,50,224,136,156,109,207,2,94,219,227,248,19,129,31,228,108,251,107,191,65,149,232, + 159,192,187,29,247,189,102,152,129,136,136,136,136,136,200,232,80,114,72,68,38,150,49,38,2,78,202,218,102,173,221,206,97,136,229,198,152,204,227,199, + 84,52,97,255,30,17,17,17,17,17,41,128,150,149,137,136,136,136,136,136,136,136,76,49,205,28,18,17,145,145,103,173,221,220,99,247,37,67,11,68, + 68,68,68,68,100,2,41,57,36,34,34,163,110,9,112,107,217,65,136,136,136,136,136,76,42,45,43,19,17,17,17,17,17,17,17,153,98,74,14,137, + 136,136,136,136,136,136,136,76,49,37,135,68,68,68,68,68,68,68,68,166,152,146,67,34,34,34,34,34,34,34,34,83,76,201,33,17,17,17,17,17, + 17,17,145,41,166,228,144,136,136,136,136,136,136,136,200,20,83,114,72,68,68,68,68,68,68,68,100,138,205,47,59,0,17,145,217,26,173,246,134,192, + 86,233,99,107,160,2,152,244,65,198,159,207,106,214,107,151,204,117,156,34,34,50,185,26,173,246,39,128,3,211,191,218,89,255,181,25,207,245,250,239, + 40,30,211,207,248,26,67,99,88,96,45,112,7,112,43,112,91,250,223,123,154,245,218,204,62,34,50,166,148,28,18,145,57,215,104,181,119,0,14,2, + 30,15,108,199,131,137,160,173,128,165,158,195,189,25,80,114,72,68,68,138,180,39,15,38,135,68,164,187,149,141,86,251,86,224,6,224,31,192,95,211, + 199,5,205,122,109,117,169,145,137,136,51,37,135,68,100,232,102,37,131,102,30,187,150,24,142,136,136,136,136,20,103,33,201,205,190,237,128,199,2,245, + 244,249,251,26,173,246,217,36,137,162,211,129,118,179,94,187,191,148,8,69,164,39,37,135,68,164,112,141,86,219,0,79,3,94,72,146,12,122,104,169, + 1,201,140,237,172,181,31,235,177,207,121,198,152,19,230,36,26,17,17,17,153,100,27,2,79,72,31,111,7,238,106,180,218,39,3,63,4,78,107,214, + 107,43,203,12,78,68,214,165,228,144,136,20,166,209,106,111,12,28,5,188,21,216,163,228,112,70,213,123,172,181,47,207,217,182,213,144,207,189,45,240, + 254,30,251,252,8,80,114,72,68,68,68,138,182,148,228,58,241,40,224,246,70,171,125,18,240,3,224,255,169,102,145,72,249,148,28,18,145,129,53,90, + 237,157,73,106,255,188,22,216,164,228,112,70,221,163,210,135,184,91,9,188,193,99,255,67,129,35,134,20,139,136,136,136,12,110,51,146,235,198,215,2, + 231,52,90,237,143,1,63,105,214,107,107,203,13,75,100,122,41,57,36,34,125,107,180,218,79,2,142,6,158,195,131,29,196,68,138,182,218,24,243,77, + 215,157,173,181,219,160,228,144,136,136,200,184,216,7,56,17,184,176,209,106,255,23,112,66,179,94,91,83,114,76,34,83,71,201,33,17,241,214,104,181, + 183,2,142,5,94,82,118,44,34,34,34,34,50,17,246,2,142,7,254,179,209,106,127,28,248,158,186,157,137,204,157,13,202,14,64,68,198,71,163,213, + 54,141,86,251,213,36,173,227,149,24,18,17,17,17,145,162,237,6,124,19,56,163,209,106,63,178,236,96,68,166,133,102,14,137,76,161,56,12,54,0, + 22,164,143,249,179,254,108,129,27,43,213,104,189,162,128,141,86,251,225,192,215,72,186,143,13,211,26,224,102,224,70,224,38,32,74,227,154,121,208,241, + 231,229,67,142,71,68,68,166,207,49,192,103,120,112,201,180,233,248,179,235,127,71,241,152,126,198,215,24,26,99,230,191,11,128,45,128,173,211,199,102, + 12,207,126,192,63,210,89,68,31,111,214,107,43,134,120,46,145,169,167,228,144,200,132,138,195,192,0,139,129,141,128,37,233,127,55,34,105,43,58,47, + 227,144,21,192,185,157,137,161,70,171,189,16,120,15,240,1,96,97,129,33,94,7,156,6,252,1,184,154,36,17,116,35,112,155,138,17,14,85,175,215, + 86,175,189,136,76,189,102,189,118,97,217,49,136,140,131,244,58,113,75,146,68,209,50,224,64,224,41,36,205,55,138,168,71,57,31,248,15,224,200,70, + 171,253,154,102,189,118,102,1,99,138,72,6,37,135,68,38,68,28,6,11,129,77,129,0,216,152,36,17,228,186,116,244,62,146,196,208,253,179,159,108, + 180,218,59,0,63,39,41,20,56,168,59,129,223,1,191,37,73,10,93,58,165,109,75,207,1,174,207,217,182,53,240,216,33,158,251,239,198,152,125,135, + 56,190,136,136,136,76,145,102,189,182,146,228,134,223,117,192,63,128,147,0,26,173,246,102,192,19,73,18,69,7,49,120,167,214,61,73,150,153,125,14, + 120,111,122,222,117,196,97,176,5,201,204,242,251,129,251,42,213,72,55,188,68,60,40,57,36,50,166,226,48,152,79,146,8,218,52,125,44,233,115,168, + 123,72,18,67,235,124,200,54,90,237,125,128,83,128,237,6,8,243,62,146,53,227,223,1,254,174,162,130,0,124,218,24,243,131,172,13,214,218,231,2, + 39,207,113,60,34,34,34,34,133,106,214,107,183,3,63,77,31,52,90,237,135,2,111,4,94,77,114,253,218,15,3,252,59,240,152,70,171,125,68,179, + 94,187,173,99,251,61,36,55,52,23,1,196,97,176,146,228,90,244,254,244,191,247,1,113,165,26,221,215,231,249,69,38,154,146,67,34,99,36,14,131, + 69,36,179,75,182,36,153,29,52,232,116,221,24,56,191,82,141,86,205,126,178,209,106,31,2,252,40,61,71,63,110,1,190,4,124,165,89,175,221,58, + 88,136,34,34,34,34,50,206,154,245,218,229,64,163,209,106,127,16,248,55,224,205,192,163,251,28,238,201,192,89,141,86,251,176,102,189,118,201,204,147, + 149,106,116,95,28,6,231,164,227,46,156,245,216,100,246,193,105,210,232,206,89,143,187,179,234,109,138,76,27,37,135,68,70,92,58,67,104,102,45,119, + 191,119,90,178,220,1,92,80,169,70,107,102,63,217,104,181,223,72,146,216,233,167,155,225,165,64,19,248,110,179,94,211,93,25,17,17,17,17,121,64, + 179,94,187,23,248,70,163,213,62,14,216,31,56,26,56,178,143,161,118,5,206,108,180,218,71,54,235,181,211,102,158,156,149,32,218,135,252,90,153,51, + 117,146,182,76,255,190,38,14,131,153,68,209,237,149,106,116,87,31,241,136,140,61,37,135,68,70,80,218,77,108,51,146,132,208,230,244,151,168,233,38, + 34,153,49,244,192,90,236,70,171,189,1,240,41,224,157,125,140,119,7,240,14,224,123,42,38,45,34,34,34,34,221,164,117,39,255,2,188,160,209,106, + 31,4,124,153,164,174,144,143,77,128,118,163,213,126,107,179,94,251,234,204,147,149,106,116,175,67,130,104,182,121,36,215,221,155,1,59,199,97,112,47, + 73,231,220,155,43,213,232,94,207,152,68,198,150,146,67,34,35,36,14,131,121,36,53,126,118,32,93,47,61,4,43,128,139,58,18,67,243,128,227,129, + 23,246,49,222,201,192,27,155,245,218,141,69,4,215,104,181,231,3,91,145,36,198,182,33,153,45,181,1,249,109,86,207,154,61,165,88,68,68,100,80, + 141,86,251,19,36,93,151,32,41,112,59,243,95,155,241,92,175,255,142,226,49,253,140,175,49,70,123,140,185,58,255,106,146,242,1,55,165,143,59,6, + 189,49,216,172,215,78,79,107,93,190,29,248,48,73,83,21,87,243,128,175,52,90,237,160,89,175,125,98,230,201,52,65,116,46,73,130,104,129,103,72, + 75,72,58,175,45,139,195,224,110,30,76,20,221,223,245,40,145,49,167,228,144,200,8,72,59,141,109,159,62,134,249,123,185,150,100,41,217,3,197,167, + 27,173,182,1,62,135,127,98,232,118,224,45,192,15,251,233,58,150,158,119,15,224,105,192,193,36,211,131,183,33,153,41,229,83,75,233,205,128,146,67, + 34,178,105,217,1,200,68,217,147,7,147,67,34,146,111,117,163,213,190,25,184,2,248,61,112,58,112,70,179,94,187,199,103,144,102,189,182,10,248,76, + 163,213,254,33,73,137,2,223,235,210,143,55,90,237,91,155,245,90,107,230,137,74,53,186,39,157,65,244,24,146,36,82,63,54,78,31,187,196,97,16, + 3,55,2,55,170,19,154,76,34,37,135,68,74,20,135,193,134,192,67,72,146,34,69,47,29,203,114,73,198,58,234,183,147,36,121,124,156,4,188,169, + 89,175,221,228,115,80,163,213,222,142,36,25,52,243,216,214,243,188,34,211,198,183,232,252,160,69,234,199,217,191,149,29,128,136,200,20,154,79,50,235, + 125,59,224,0,224,24,146,132,81,72,146,40,58,161,89,175,157,235,58,88,179,94,187,22,120,81,163,213,254,62,240,61,96,169,71,44,95,107,180,218, + 119,52,235,181,19,103,158,72,19,68,203,241,95,178,150,165,146,62,150,197,97,112,29,112,93,165,26,169,19,175,76,12,37,135,68,74,144,206,20,218, + 133,100,233,212,92,125,153,187,166,82,141,110,158,253,68,163,213,62,28,248,172,231,56,239,2,154,62,179,133,26,173,246,126,233,113,71,48,55,73,48, + 145,73,113,144,181,118,158,49,102,77,175,29,173,181,123,243,96,113,205,73,240,104,107,237,157,14,251,109,5,188,2,120,226,144,227,145,98,120,205,240, + 178,214,46,33,185,129,34,34,227,99,62,240,132,244,113,76,163,213,254,51,240,21,224,196,102,189,182,178,235,145,169,102,189,246,179,70,171,189,63,240, + 51,146,217,229,46,54,0,190,223,104,181,163,142,34,213,55,199,97,176,25,197,189,151,44,4,118,6,118,140,195,224,6,224,90,45,57,147,73,160,228, + 144,200,28,74,11,77,239,0,236,68,255,211,91,251,113,59,240,175,217,79,164,9,155,227,113,79,78,173,6,94,221,172,215,190,235,178,115,90,224,250, + 80,146,2,215,79,114,15,85,68,102,121,2,112,141,181,214,165,251,223,214,195,14,102,142,29,157,62,230,202,170,65,14,182,214,62,5,248,223,62,14, + 29,36,97,254,14,107,237,43,60,143,57,223,24,115,248,0,231,28,212,150,214,218,83,232,248,76,234,98,111,146,37,29,34,50,190,14,72,31,199,54, + 90,237,22,240,213,102,189,118,93,175,131,154,245,218,69,141,86,251,113,192,9,36,37,8,92,44,4,78,110,180,218,7,55,235,181,112,214,243,151,145, + 204,250,89,226,23,122,87,243,72,174,235,183,143,195,224,102,224,234,74,53,242,90,78,39,50,74,148,28,18,153,35,113,24,108,65,114,231,99,195,57, + 62,245,221,36,5,168,31,152,233,211,104,181,119,6,126,225,17,203,189,192,145,205,122,237,87,46,59,55,90,237,26,112,44,176,187,103,172,34,147,198, + 187,30,87,134,185,92,126,89,68,188,227,234,150,1,143,223,136,100,70,232,92,218,20,255,90,75,209,48,2,241,116,72,217,1,136,72,41,182,2,222, + 15,28,221,104,181,63,10,124,182,89,175,173,232,118,64,179,94,187,45,189,174,252,44,238,101,16,54,2,78,105,180,218,123,55,235,181,27,0,42,213, + 104,77,28,6,23,145,212,31,42,122,22,187,33,185,65,179,117,28,6,183,0,255,212,76,34,25,71,90,222,33,50,100,113,24,108,20,135,193,163,128, + 71,48,55,137,161,85,36,95,114,150,3,103,86,170,209,223,102,175,135,110,180,218,75,129,83,72,62,160,93,220,14,60,213,37,49,212,104,181,131,70, + 171,125,28,240,43,148,24,18,1,184,190,236,0,60,93,91,118,0,37,26,52,57,36,34,34,110,54,4,62,14,156,215,104,181,159,214,107,231,102,189, + 182,170,89,175,189,21,120,163,199,57,182,0,142,75,27,160,0,80,169,70,119,3,255,244,13,214,211,150,64,53,14,131,101,233,138,1,145,177,161,31, + 88,145,33,137,195,192,196,97,176,12,216,151,225,118,209,89,11,220,65,50,69,255,239,192,25,149,106,116,97,165,26,221,144,115,215,226,19,36,93,194, + 92,220,0,28,216,172,215,206,236,181,99,163,213,62,12,184,16,120,149,227,216,34,211,224,252,178,3,240,116,94,217,1,148,232,210,178,3,16,17,153, + 50,15,3,78,109,180,218,39,52,90,237,237,123,237,220,172,215,190,6,252,187,199,248,53,146,174,182,15,168,84,163,235,128,91,189,162,244,183,1,176, + 140,36,73,52,73,181,0,101,194,41,57,36,50,4,105,23,178,71,147,124,48,12,163,224,180,5,110,34,249,34,247,231,74,53,58,183,82,141,174,174, + 84,163,187,102,47,31,235,212,104,181,15,0,222,228,120,142,21,192,115,155,245,218,197,221,118,106,180,218,75,26,173,246,183,129,159,147,116,170,16,145, + 7,93,80,118,0,158,198,45,153,85,164,83,203,14,96,76,116,93,2,50,162,198,49,102,145,105,242,2,224,156,70,171,221,179,177,64,179,94,251,28, + 240,105,143,177,255,187,209,106,119,118,42,91,78,50,179,119,216,239,13,139,129,189,226,48,120,84,28,6,27,13,249,92,34,3,83,114,72,164,96,113, + 24,108,67,50,91,168,50,132,225,239,39,153,33,244,151,74,53,186,184,82,141,110,175,84,163,158,93,140,0,26,173,246,34,160,133,123,178,234,213,205, + 122,237,175,61,198,220,28,56,13,120,185,227,152,34,211,102,156,146,67,119,24,99,166,121,89,217,175,203,14,96,76,92,82,118,0,125,184,176,236,0, + 68,164,167,45,128,223,54,90,237,215,58,236,251,94,224,59,142,227,46,38,233,96,182,112,230,137,74,53,90,85,169,70,151,86,170,209,95,72,102,221, + 95,5,12,179,144,244,166,192,190,113,24,236,170,165,102,50,202,84,144,90,164,32,113,24,44,32,153,30,59,140,233,163,183,145,220,225,184,189,219,204, + 160,30,142,193,125,57,217,39,155,245,218,241,221,118,104,180,218,15,33,249,50,229,58,166,143,136,228,174,206,117,192,74,146,153,82,51,15,58,254,188, + 124,8,231,159,84,21,107,237,179,28,247,189,222,24,115,238,80,163,153,14,23,145,220,153,92,84,118,32,14,254,81,118,0,37,58,217,24,115,117,217, + 65,140,137,113,124,95,232,39,230,47,2,39,167,127,158,125,83,165,215,159,125,246,45,235,207,101,142,161,152,71,251,220,46,251,46,6,30,66,210,121, + 183,232,107,222,5,64,171,209,106,63,2,120,103,179,94,91,157,181,83,179,94,179,105,18,105,43,146,165,99,189,236,3,124,20,120,79,231,134,74,53, + 186,11,184,11,184,34,157,249,191,57,73,162,42,232,239,159,144,203,144,188,110,155,197,97,112,97,165,26,221,91,240,248,34,3,83,114,72,164,0,113, + 24,108,2,236,73,177,95,0,87,145,212,252,185,161,82,141,92,218,88,231,74,63,100,223,231,184,251,47,128,15,244,24,111,47,160,77,210,190,115,80, + 119,0,255,7,156,69,146,232,185,20,184,181,89,175,77,115,215,164,97,217,13,248,165,227,190,63,4,254,109,136,177,76,5,99,204,189,214,218,143,3, + 255,89,118,44,61,172,37,233,32,51,141,44,240,225,178,131,24,35,87,144,124,145,90,90,118,32,142,110,237,103,70,92,179,94,59,109,24,193,136,76, + 146,70,171,189,17,176,35,73,162,104,55,224,112,224,41,12,94,82,225,237,192,158,141,86,251,121,205,122,45,115,70,79,179,94,91,213,104,181,95,0, + 252,63,146,25,251,61,195,109,180,218,173,102,189,118,121,222,14,233,245,246,181,192,181,113,24,44,1,182,7,182,33,105,89,95,148,141,72,102,17,93, + 86,169,70,55,20,56,174,200,192,148,28,18,25,80,28,6,91,147,116,230,42,170,182,208,26,146,233,173,215,86,170,209,90,207,88,54,5,22,207,254, + 176,105,180,218,243,128,255,37,185,27,211,203,197,192,75,155,245,90,238,82,181,70,171,253,24,146,165,100,131,20,217,94,69,210,49,237,187,192,41,189, + 218,152,138,140,185,79,2,47,34,73,32,143,170,47,24,99,206,42,59,136,146,124,78,179,228,220,25,99,172,181,246,15,192,161,101,199,226,232,247,101, + 7,32,50,169,210,196,205,197,233,163,13,124,177,209,106,111,71,242,153,247,18,220,146,54,121,158,14,156,208,104,181,15,111,214,107,171,114,206,127,119, + 163,213,126,9,73,13,206,197,61,198,155,7,124,8,56,202,229,228,233,204,158,203,226,48,184,130,36,65,180,61,197,117,29,222,0,120,120,122,221,126, + 233,236,174,194,34,101,210,154,71,145,1,196,97,176,51,201,178,170,162,18,67,55,0,103,165,197,165,157,18,67,113,24,44,137,195,96,223,56,12,94, + 3,188,141,36,241,50,91,29,120,156,195,80,107,129,151,52,235,181,56,111,135,70,171,189,13,240,51,250,79,12,221,67,178,188,109,219,102,189,246,188, + 102,189,118,146,18,67,50,233,140,49,43,73,126,15,71,117,54,220,21,76,239,172,161,211,128,119,149,29,196,24,250,119,146,26,120,163,238,46,224,232, + 178,131,16,153,38,205,122,237,250,102,189,118,108,179,94,219,15,120,56,240,223,172,127,109,234,234,16,146,101,102,185,215,217,205,122,237,50,122,204,120, + 159,229,165,233,236,119,103,149,106,180,186,82,141,174,5,66,146,166,13,183,251,28,223,195,86,36,179,136,134,81,167,84,196,155,102,14,137,244,33,45, + 38,183,59,201,155,122,17,34,224,242,74,53,186,219,241,252,243,73,234,27,237,77,50,141,119,102,186,235,242,74,53,122,160,61,103,163,213,158,79,198, + 250,234,28,95,110,214,107,231,228,109,76,11,90,255,132,228,206,73,63,126,68,178,126,124,154,11,222,202,148,50,198,156,97,173,61,134,164,230,193,40, + 125,246,222,0,188,204,24,51,141,181,15,126,10,28,101,140,113,42,234,47,15,50,198,92,102,173,253,48,201,172,184,81,246,30,213,146,18,41,79,179, + 94,187,20,120,119,163,213,254,6,240,21,224,224,62,134,121,5,73,221,205,99,186,236,243,57,224,133,64,181,199,88,134,100,153,247,145,190,65,164,53, + 63,111,3,110,139,195,96,41,240,80,96,19,223,113,50,44,6,30,29,135,193,21,149,106,164,247,43,41,149,102,14,137,120,74,11,79,63,138,98,18, + 67,247,3,23,86,170,209,57,189,18,67,113,24,152,56,12,150,197,97,240,28,224,157,36,31,130,187,179,238,58,232,51,58,14,59,2,88,230,16,199, + 77,192,127,228,109,76,239,216,124,13,216,223,97,172,78,23,3,79,107,214,107,47,86,98,72,166,153,49,230,147,36,23,174,103,151,29,11,201,44,166, + 175,3,123,24,99,58,223,55,38,221,189,36,119,153,159,103,140,185,171,236,96,198,88,147,164,214,199,168,250,25,201,231,150,136,148,172,89,175,45,7, + 158,6,188,12,184,185,143,33,222,215,104,181,223,214,101,252,53,192,171,73,154,152,244,114,68,90,34,225,1,113,24,108,146,118,18,115,90,9,80,169, + 70,119,85,170,209,217,36,157,16,7,170,11,154,50,192,46,113,24,60,204,53,6,145,97,80,114,72,196,67,218,197,224,49,12,126,167,96,13,73,75, + 250,176,82,141,110,233,113,206,121,113,24,236,15,188,3,120,101,122,254,172,117,213,215,3,15,220,113,72,19,58,174,203,37,222,213,172,215,162,46,219, + 103,206,237,235,155,192,62,205,122,237,183,125,28,43,50,113,140,49,103,147,36,136,222,75,50,99,112,174,89,146,218,12,79,54,198,188,222,24,115,103, + 9,49,148,229,22,224,179,192,46,198,152,255,50,198,140,234,50,191,177,96,140,89,77,242,101,175,65,49,95,142,138,18,3,111,0,14,215,255,99,145, + 209,209,172,215,108,179,94,251,62,201,82,179,255,233,99,136,99,27,173,118,238,204,160,102,189,118,33,240,17,199,177,58,247,187,27,120,62,240,182,56, + 12,158,152,206,12,234,41,189,134,255,43,112,57,80,68,221,160,237,128,189,212,238,94,202,50,74,83,219,69,70,90,28,6,139,73,90,97,14,218,145, + 236,46,146,217,66,93,235,53,164,119,14,246,32,185,248,222,204,97,220,51,58,218,220,63,25,120,172,195,113,127,0,190,151,183,177,209,106,63,146,100, + 189,184,175,15,3,31,81,215,49,33,105,229,62,72,231,31,223,47,158,87,12,120,190,139,7,56,182,167,244,75,245,167,128,79,89,107,119,0,30,153, + 62,30,65,82,244,178,200,187,134,171,128,203,128,11,72,106,37,92,56,135,179,101,78,163,216,14,47,62,214,144,212,133,184,5,184,134,100,134,203,63, + 134,156,44,184,133,193,126,238,230,202,165,69,13,100,140,89,11,124,214,90,251,51,224,211,192,19,128,173,139,26,223,211,245,36,159,103,239,54,198,92, + 83,82,12,34,210,67,122,51,242,13,141,86,251,10,252,150,166,110,0,28,215,104,181,31,219,165,94,229,167,129,23,147,124,158,118,115,104,163,213,126, + 76,179,94,251,7,64,165,26,173,137,195,224,124,146,25,242,79,5,158,18,135,193,114,224,239,192,191,186,213,1,77,183,93,27,135,193,77,36,93,219, + 182,103,176,207,241,45,128,71,197,97,112,65,165,26,245,91,171,73,164,47,154,182,38,226,96,86,98,168,87,39,132,94,174,165,199,135,76,122,190,29, + 128,103,144,180,7,117,17,1,95,152,61,110,163,213,254,5,189,187,201,172,33,153,217,115,65,214,198,116,246,209,31,128,3,29,227,128,228,206,73,189, + 89,175,125,203,227,152,57,103,173,125,52,73,226,173,155,191,24,99,254,228,49,230,1,36,95,142,186,249,185,49,230,146,156,227,31,10,60,175,199,241, + 127,53,198,156,158,113,236,118,192,75,93,226,116,116,177,49,230,23,5,142,39,34,83,192,90,187,53,201,210,235,61,128,133,67,62,221,74,224,34,224, + 28,99,76,215,89,184,34,50,122,26,173,246,219,73,234,5,249,248,104,179,94,235,86,10,225,165,116,185,233,57,203,231,155,245,218,59,102,254,18,135, + 193,54,36,179,14,59,69,192,159,128,179,43,213,168,103,141,186,116,214,209,30,192,18,135,24,186,185,7,56,175,82,141,212,184,69,230,140,146,67,34, + 61,196,97,176,136,36,49,52,72,251,202,213,36,197,162,123,45,33,219,148,228,142,69,175,59,30,157,78,175,84,163,211,103,254,210,104,181,247,36,89, + 7,221,203,9,205,122,237,69,121,27,27,173,246,203,129,111,123,196,177,2,120,78,179,94,251,141,199,49,34,34,34,34,50,133,26,173,246,235,241,171, + 15,182,26,216,183,89,175,157,155,51,222,2,146,210,13,59,244,24,231,38,96,135,102,189,246,192,114,176,56,12,222,64,50,131,55,203,237,36,179,80, + 47,232,152,169,191,158,56,12,230,1,187,208,127,19,151,25,43,72,18,68,247,12,56,142,136,19,173,103,20,233,162,160,196,208,93,192,223,187,37,134, + 226,48,216,48,14,131,103,0,111,193,63,49,4,208,57,19,197,181,117,239,151,242,54,52,90,237,0,255,229,100,175,83,98,72,68,68,68,68,92,52, + 235,181,255,33,169,107,217,117,86,253,44,243,129,111,166,29,121,179,198,91,5,124,193,97,156,173,89,191,123,90,102,194,41,181,25,73,163,151,215,199, + 97,176,91,183,194,209,149,106,180,166,82,141,46,35,89,206,237,82,36,59,207,34,146,78,102,106,117,47,115,66,201,33,145,28,179,186,146,13,146,24, + 186,142,100,26,106,102,205,148,56,12,54,72,139,77,191,141,100,57,82,63,245,57,34,146,187,31,0,52,90,237,133,64,238,108,160,89,206,39,153,38, + 155,231,163,248,117,100,251,66,179,94,251,142,199,254,34,34,34,34,50,229,154,245,218,183,233,210,53,55,195,163,73,186,246,230,105,145,20,153,238,165, + 115,57,254,121,244,78,82,109,147,30,247,202,56,12,30,210,109,199,74,53,186,141,164,96,245,173,14,177,228,153,15,60,50,14,131,65,151,169,137,244, + 164,228,144,72,134,244,110,192,94,244,191,94,120,13,73,209,233,203,242,234,11,165,111,242,47,3,106,12,150,128,90,222,49,189,245,0,96,99,135,227, + 190,152,87,44,186,209,106,111,79,246,186,235,60,191,7,222,233,177,191,136,136,136,136,200,140,79,1,103,121,236,223,72,107,99,174,39,45,122,253,191, + 14,99,60,191,209,106,63,112,13,158,46,223,186,206,241,252,59,1,175,137,195,224,176,56,12,114,235,171,85,170,209,170,74,53,186,128,164,25,64,191, + 77,17,22,0,123,167,43,26,68,134,70,201,33,145,108,187,1,65,159,199,174,6,206,233,177,140,108,91,224,117,36,235,145,7,213,185,164,236,16,135, + 99,34,224,248,46,219,95,135,123,55,195,235,128,23,166,211,120,69,68,68,68,68,188,164,181,127,94,142,123,135,210,199,144,116,230,205,243,5,122,207, + 2,218,24,120,118,199,115,87,57,158,127,198,190,192,27,227,48,216,169,219,78,149,106,116,61,201,204,164,126,91,222,47,38,153,65,164,110,227,50,52, + 74,14,137,116,136,195,96,123,96,187,62,15,95,69,146,24,202,109,21,29,135,193,62,192,107,232,63,249,52,219,125,192,213,29,207,61,203,225,184,111, + 52,235,181,204,226,118,233,178,180,215,123,196,208,104,214,107,55,123,236,47,34,34,34,34,178,142,102,189,118,41,240,46,143,67,114,107,108,54,235,181, + 43,72,10,72,247,114,100,199,223,175,244,56,255,140,77,73,150,153,61,51,45,75,145,169,82,141,238,0,206,38,41,52,221,143,141,129,71,196,97,160, + 239,240,50,20,250,193,18,153,37,237,22,246,208,62,15,95,73,146,24,202,92,227,28,135,193,188,56,12,14,5,14,199,125,86,78,47,151,205,110,171, + 217,104,181,31,66,178,28,174,151,239,118,217,246,124,146,34,125,46,254,14,252,159,227,190,34,34,34,34,34,221,124,21,183,142,187,0,207,110,180,218, + 15,239,178,253,20,135,49,246,239,248,251,53,244,183,252,203,0,143,39,41,88,157,219,165,44,93,186,246,15,220,106,34,101,9,128,61,186,21,196,22, + 233,151,146,67,34,169,56,12,54,36,73,172,244,243,102,59,147,24,202,156,141,147,118,25,120,37,176,95,223,1,174,127,190,211,89,255,67,207,101,214, + 208,173,36,197,168,243,188,197,35,142,119,55,235,53,215,238,18,34,34,34,34,34,185,210,235,74,159,110,185,221,106,100,254,210,225,248,135,52,90,237, + 109,103,254,82,169,70,43,128,27,60,206,223,105,11,146,90,68,79,204,75,224,164,231,56,27,184,189,207,115,108,73,82,2,67,164,80,74,14,137,144, + 116,13,3,246,164,191,25,61,43,72,58,146,221,155,51,246,78,36,53,124,186,118,52,112,180,22,8,129,47,84,170,209,233,233,135,203,108,46,245,134, + 78,207,75,232,52,90,237,135,145,20,180,118,241,235,102,189,246,59,199,125,69,68,68,68,68,92,252,0,247,194,208,79,239,178,237,82,224,159,14,99, + 116,222,188,245,173,59,212,105,3,224,169,192,115,227,48,200,236,68,156,206,252,63,159,254,19,81,219,165,53,76,69,10,163,228,144,72,98,39,96,105, + 31,199,221,79,247,86,245,85,224,21,184,117,15,235,229,2,224,75,149,106,244,203,172,165,107,141,86,123,1,201,7,81,47,221,18,58,79,243,136,231, + 253,30,251,138,136,136,136,136,244,212,172,159,124,244,23,0,0,32,0,73,68,65,84,215,86,2,199,58,238,190,87,163,213,222,38,103,28,139,219,236, + 161,106,199,223,151,147,116,30,30,212,62,192,81,233,234,132,245,84,170,145,173,84,163,229,192,141,125,142,255,80,181,184,151,34,41,57,36,83,47,93, + 242,181,99,31,135,206,36,134,238,207,25,119,63,146,153,60,131,254,158,93,1,124,189,82,141,78,172,84,163,110,211,79,31,130,91,18,170,91,114,232, + 41,142,49,45,39,89,47,45,34,34,34,34,82,180,22,201,181,182,139,110,215,175,127,118,56,126,157,228,80,165,26,93,9,124,9,184,200,241,252,221, + 44,3,94,27,135,193,230,93,246,89,78,82,246,193,215,60,146,250,67,250,78,47,133,208,15,146,76,181,116,170,231,30,248,215,25,90,3,156,159,177, + 172,107,102,220,189,112,91,226,213,205,173,36,133,163,191,147,182,191,236,101,153,195,62,55,144,76,177,93,79,163,213,222,0,56,200,49,182,147,210,187, + 49,34,34,34,34,34,133,106,214,107,49,240,71,199,221,187,205,156,119,41,110,189,95,122,29,252,128,74,53,186,163,82,141,78,0,190,69,255,51,123, + 102,108,78,146,32,202,108,119,95,169,70,150,36,17,117,71,31,99,47,197,237,59,128,72,79,74,14,201,180,219,21,200,156,234,217,195,242,46,197,167, + 119,33,233,248,53,72,23,129,11,129,86,165,26,253,51,253,192,112,177,204,97,159,211,187,36,117,30,65,82,68,207,197,73,142,251,137,136,136,136,136, + 244,227,84,199,253,186,37,135,46,5,86,247,56,62,0,118,201,218,144,206,34,250,58,240,51,32,243,218,223,209,134,192,203,211,27,200,89,231,89,75, + 82,66,226,174,62,198,222,49,237,184,44,50,16,37,135,100,106,197,97,176,25,176,93,31,135,94,83,169,70,55,231,140,185,29,240,98,146,105,158,253, + 88,11,180,129,19,243,102,37,117,177,204,97,159,229,93,182,185,46,41,187,134,164,133,189,136,136,136,136,200,176,184,38,135,150,53,90,237,204,18,17, + 105,253,162,110,215,191,51,50,235,22,65,146,184,169,84,163,127,0,95,0,206,160,191,86,247,144,124,63,56,34,14,131,204,78,99,105,145,234,243,128, + 204,38,55,61,236,30,135,193,130,62,227,18,1,148,28,146,41,149,174,205,237,167,5,228,29,192,191,114,198,220,28,120,41,176,176,207,176,238,2,190, + 85,169,70,103,246,154,45,20,135,193,161,113,24,44,235,120,186,243,239,89,186,117,68,216,211,225,120,128,159,105,73,153,136,136,136,136,12,217,121,192, + 45,142,251,118,171,31,122,129,195,241,61,103,207,87,170,209,138,74,53,250,13,240,125,32,179,25,141,131,13,128,23,117,89,98,182,10,56,151,164,27, + 178,143,69,192,195,251,140,73,4,232,175,109,183,200,36,216,1,255,229,100,247,3,23,101,37,110,226,48,88,10,28,5,108,212,103,60,87,146,204,22, + 90,175,11,89,199,121,182,6,62,15,188,136,245,147,65,157,127,207,210,109,205,116,238,29,147,14,23,59,238,151,201,90,187,152,254,103,86,21,229,94, + 99,204,156,38,184,172,181,253,254,108,20,22,130,49,166,159,59,81,67,147,38,84,159,8,60,22,216,151,100,105,99,63,201,213,191,3,47,235,81,176, + 125,246,121,63,69,242,251,218,207,207,225,191,128,191,165,143,51,42,213,232,178,62,198,40,132,181,118,71,96,111,146,215,109,230,177,13,131,45,105,29, + 196,83,140,49,69,20,239,196,90,187,33,229,222,192,178,192,125,115,253,62,225,194,90,187,136,225,94,191,173,48,198,244,90,130,81,40,107,237,66,160, + 159,59,222,171,141,49,190,95,160,250,98,173,93,64,255,55,127,10,125,77,7,248,25,152,203,215,171,223,255,167,0,247,27,99,138,232,20,53,19,203, + 176,127,103,124,13,244,239,179,214,46,161,188,247,121,152,195,235,137,102,189,182,182,209,106,255,21,183,58,158,91,119,217,118,181,195,241,174,165,21,168, + 84,163,203,227,48,248,58,240,66,160,159,118,242,243,129,151,196,97,240,237,172,186,162,149,106,180,34,14,131,139,72,186,157,249,252,191,222,34,14,131, + 109,43,213,168,219,205,96,145,92,163,244,70,41,50,39,226,48,88,72,210,186,222,199,90,224,194,52,155,223,57,222,98,224,101,36,235,149,251,241,39, + 224,119,233,90,227,76,113,24,24,224,213,192,103,102,157,231,182,142,221,150,57,156,171,219,135,133,235,135,219,53,142,251,229,57,19,120,212,128,99,12, + 234,30,107,237,69,36,119,146,102,30,191,31,214,69,179,181,118,99,250,91,67,94,116,28,183,2,231,207,122,252,205,24,115,78,25,177,196,97,240,50, + 146,233,217,69,172,145,127,22,201,5,218,215,28,206,187,35,240,238,1,206,181,21,176,127,250,103,27,135,193,231,129,247,87,170,209,156,37,222,172,181, + 155,2,159,5,94,57,87,231,116,84,228,116,246,11,129,157,11,28,175,31,119,89,107,47,224,193,223,151,115,129,63,149,144,88,62,8,120,18,201,151, + 132,125,72,222,235,135,249,197,112,181,181,246,18,146,127,239,185,192,169,115,240,62,241,21,224,53,125,28,119,5,57,117,66,134,224,255,128,231,246,121, + 236,43,129,111,23,23,10,223,3,142,236,227,184,115,73,126,134,230,66,27,247,229,234,157,158,7,156,92,96,44,63,7,158,94,224,120,131,58,12,56, + 101,128,227,175,7,54,41,40,150,190,88,107,35,146,107,167,153,247,199,179,141,49,103,14,233,116,174,137,142,110,201,161,200,225,120,231,228,16,36,5, + 171,227,48,56,14,56,148,254,126,175,22,1,47,139,195,224,155,149,106,180,222,236,168,74,53,186,51,14,131,126,222,227,30,26,135,193,29,121,221,148, + 69,186,81,114,72,166,209,46,248,207,24,184,180,82,141,214,251,114,159,174,237,253,55,186,127,32,229,89,5,156,84,169,70,93,103,226,164,235,146,191, + 206,186,157,196,86,49,171,40,94,163,213,94,0,108,239,112,206,34,102,14,13,154,28,26,5,27,1,251,165,143,25,151,88,107,235,198,152,63,149,20, + 211,92,216,130,228,98,253,129,11,118,107,237,247,128,183,27,99,156,102,221,12,42,14,131,173,128,255,1,14,47,120,104,215,228,108,165,192,115,26,224, + 29,192,33,113,24,188,178,82,141,254,82,224,216,153,172,181,207,7,190,140,251,239,171,244,111,41,240,248,244,49,227,116,107,237,107,141,49,255,28,246, + 201,173,181,155,147,180,82,126,241,176,207,213,97,62,15,206,68,123,41,176,214,90,251,121,224,253,198,152,126,151,81,12,203,206,214,218,189,140,49,46, + 221,136,250,150,206,26,58,120,152,231,152,35,143,178,214,238,96,140,185,118,152,39,73,111,136,28,48,204,115,72,233,2,224,192,244,1,128,181,246,20, + 224,245,198,152,235,10,62,151,107,167,176,110,159,139,119,58,28,191,78,171,249,56,12,14,4,86,87,170,81,110,210,171,82,141,86,197,97,240,83,224, + 90,146,27,85,190,223,47,150,144,20,169,62,174,82,141,178,58,149,93,67,242,90,111,230,49,230,60,146,155,43,3,205,244,151,233,164,154,67,50,85, + 226,48,168,224,255,165,234,134,74,53,202,251,96,170,225,63,11,9,146,153,72,39,116,75,12,197,97,48,47,14,131,247,145,220,145,57,168,99,243,125, + 29,203,219,150,224,246,251,124,83,214,147,141,86,219,48,93,201,161,44,187,3,127,176,214,126,217,90,187,180,236,96,230,208,203,128,139,210,164,195,80, + 197,97,48,31,248,21,197,39,134,202,246,48,224,119,113,24,60,108,152,39,177,214,126,6,248,49,74,12,149,233,32,224,60,107,237,59,172,181,67,187, + 134,178,214,30,74,50,123,106,174,19,67,89,54,0,254,157,228,223,253,248,94,59,151,224,176,57,56,199,1,36,201,194,73,48,23,175,215,193,244,191, + 4,79,198,215,161,192,133,214,218,215,22,60,110,17,51,135,92,146,67,157,51,135,182,2,254,28,135,193,199,210,85,7,153,42,213,200,86,170,209,223, + 72,90,222,119,45,15,145,99,41,201,12,162,69,89,99,3,151,0,43,61,199,220,58,14,131,141,251,136,69,166,156,146,67,50,109,118,245,220,127,21, + 249,5,168,119,33,169,149,210,143,159,116,171,85,18,135,193,18,146,41,236,31,39,153,118,218,169,243,67,194,229,34,236,142,180,99,67,150,77,113,91, + 18,114,63,235,47,103,155,36,6,120,19,112,174,181,182,200,25,38,163,110,107,224,199,214,218,15,14,249,60,199,0,143,25,242,57,202,178,24,248,86, + 28,6,67,169,167,149,126,41,63,122,24,99,139,183,37,192,177,192,15,135,49,184,181,246,161,36,239,255,253,204,72,29,166,135,2,63,77,103,52,141, + 146,185,72,118,212,230,224,28,115,229,217,115,112,142,73,122,189,196,207,38,64,203,90,219,115,153,183,7,215,153,67,69,39,135,238,35,249,174,252,126, + 224,172,56,12,30,209,237,224,74,53,186,134,100,249,104,63,203,204,55,7,14,75,203,72,116,142,187,18,184,8,255,14,105,190,223,121,68,148,28,146, + 233,17,135,193,166,248,175,209,190,34,167,206,208,66,224,57,125,134,242,171,74,53,58,63,111,99,186,236,230,119,36,107,238,243,116,22,50,116,249,66, + 218,237,174,131,235,29,190,59,167,164,83,217,206,192,39,203,14,162,4,31,176,214,238,62,140,129,227,48,216,7,248,192,48,198,30,33,67,73,224,88, + 107,231,147,212,83,42,179,0,169,172,239,5,214,218,126,107,208,116,243,117,252,27,38,204,149,45,129,207,149,29,68,135,199,207,65,194,106,146,146,29, + 7,167,5,141,135,105,146,94,47,233,207,235,172,181,79,42,104,44,215,154,141,221,26,127,184,212,223,89,220,241,247,217,69,228,247,1,254,30,135,193, + 59,187,221,4,74,107,7,125,23,255,78,99,0,143,4,30,157,51,110,4,92,229,57,222,166,113,24,248,44,71,19,81,114,72,166,74,183,22,151,89, + 238,38,127,42,235,211,232,175,0,245,239,43,213,232,172,188,141,113,24,60,156,164,96,243,227,250,24,91,138,245,6,107,237,19,202,14,98,142,45,36, + 41,12,59,12,13,138,45,88,60,170,222,151,117,231,111,64,111,39,233,74,38,163,231,11,69,118,34,180,214,190,134,254,139,248,206,149,151,89,107,159, + 85,118,16,179,204,35,169,245,49,20,214,218,109,41,191,137,66,145,22,3,79,29,214,224,214,218,135,83,126,49,121,41,159,1,190,154,214,235,26,212, + 168,220,148,92,8,252,55,201,50,242,135,228,237,148,118,10,251,62,201,234,3,95,135,164,55,137,179,92,133,127,115,147,93,134,112,77,34,19,76,201, + 33,153,10,105,173,33,223,174,72,151,230,180,173,223,9,168,246,17,198,95,129,211,243,54,198,97,240,68,224,47,232,162,106,84,24,224,235,5,93,216, + 140,147,167,88,107,143,26,194,184,251,14,97,204,81,180,41,197,255,14,191,189,224,241,164,56,59,2,31,42,112,188,87,23,56,214,48,141,90,156,195, + 92,90,54,137,179,96,244,122,201,92,216,147,228,198,208,164,121,18,73,45,162,220,58,131,149,106,116,53,240,3,214,159,233,223,203,124,224,5,89,53, + 142,210,239,36,151,123,142,183,49,73,237,36,17,39,74,14,201,180,240,45,26,125,99,165,26,197,157,79,166,221,201,250,89,70,112,1,240,203,172,100, + 83,58,238,147,129,83,41,166,173,183,20,103,47,214,47,6,62,13,222,80,228,96,113,24,44,37,41,218,60,45,250,173,69,182,158,180,56,122,238,29, + 74,25,9,175,47,98,16,107,173,33,89,86,48,14,70,109,38,205,51,211,229,151,195,48,137,201,142,67,135,56,246,36,190,94,210,191,66,175,39,70, + 200,67,128,63,196,97,144,59,171,183,82,141,254,69,82,63,110,173,231,216,91,2,135,228,140,121,39,57,205,101,186,216,57,14,3,125,231,23,39,250, + 65,145,137,151,86,235,247,169,71,176,154,156,34,212,36,29,56,124,215,239,94,78,82,128,58,47,49,180,23,112,50,217,133,167,165,124,123,149,29,64, + 9,246,40,120,188,71,51,93,159,55,69,206,146,218,179,192,177,100,56,42,214,218,29,10,24,103,103,198,167,35,214,174,115,80,183,198,71,0,60,177, + 232,65,173,181,243,72,150,145,79,154,237,173,181,133,55,7,176,214,46,6,158,92,244,184,50,214,118,44,114,233,237,136,217,26,56,61,14,131,220,213, + 4,149,106,116,9,201,53,190,175,125,226,48,200,187,89,240,47,252,102,36,45,6,182,239,35,6,153,66,211,116,177,46,211,203,183,214,208,149,105,103, + 128,117,196,97,176,3,176,191,231,88,119,3,63,174,84,163,204,55,241,56,12,182,35,105,237,221,79,253,34,153,27,93,187,83,76,168,77,211,58,27, + 69,233,236,0,50,233,138,252,247,42,57,52,30,138,248,255,52,46,179,134,32,185,126,28,181,120,135,177,84,170,138,255,13,161,113,49,140,215,235,32, + 70,183,152,186,148,195,80,252,13,167,81,178,41,240,219,116,5,64,166,74,53,58,143,164,180,132,175,103,230,180,183,95,1,92,237,57,214,78,195,234, + 166,42,147,69,201,33,153,104,233,50,176,45,61,14,185,7,184,46,99,28,67,50,197,211,183,168,219,175,42,213,232,190,156,216,150,2,167,160,37,35, + 163,110,26,103,14,193,244,254,187,71,141,146,67,227,161,136,223,23,159,207,170,81,48,106,73,223,97,36,59,70,169,240,118,209,134,241,122,105,73,153, + 100,153,244,235,137,141,129,118,28,6,221,222,47,126,3,220,210,199,184,121,51,34,175,1,50,191,95,228,152,143,106,15,137,3,37,135,100,210,109,141, + 95,66,231,234,156,229,95,59,3,219,121,158,123,57,112,81,214,134,52,105,117,34,73,107,204,126,44,239,243,56,241,183,87,90,11,100,218,140,91,82, + 226,86,199,253,238,24,106,20,197,219,186,236,0,196,201,184,253,190,76,162,135,89,107,119,43,120,204,73,78,118,236,107,173,221,166,224,49,39,249,245, + 146,254,77,195,251,227,98,224,167,113,24,60,41,107,99,165,26,173,2,126,140,127,129,234,199,103,181,163,175,84,163,181,192,63,61,199,242,253,30,35, + 83,72,201,33,153,116,62,75,99,86,146,159,213,63,208,243,188,43,128,83,242,234,12,1,31,7,158,225,57,230,140,83,25,110,49,73,89,215,82,166, + 179,30,212,40,205,10,184,2,184,164,203,227,251,192,9,46,3,85,170,209,117,192,251,128,11,187,140,183,28,255,11,184,97,153,198,196,228,56,26,165, + 223,151,105,246,236,162,6,178,214,110,65,129,197,229,71,144,161,192,107,9,107,237,50,224,225,69,141,39,19,101,90,222,31,23,0,63,140,195,32,243, + 166,78,165,26,221,8,156,230,57,230,60,224,233,57,227,221,138,95,107,251,165,233,170,5,145,92,195,234,236,32,82,186,244,13,208,167,8,222,13,105, + 38,190,115,156,237,129,93,60,79,127,90,86,183,179,116,188,3,233,191,181,231,15,129,87,100,213,68,154,50,174,157,31,12,147,243,229,218,167,219,197, + 36,253,187,95,84,169,70,78,137,31,87,149,106,244,73,224,147,221,246,73,107,140,93,0,108,82,228,185,101,206,216,244,225,74,55,203,38,195,97,192, + 103,11,26,235,25,76,254,207,197,97,192,55,10,26,107,146,151,224,249,190,159,184,140,87,38,189,63,14,207,182,192,241,113,24,60,35,167,222,232,153, + 192,67,129,93,61,198,220,35,14,131,157,43,213,232,138,140,109,215,1,187,123,198,231,147,80,146,41,163,228,144,76,50,159,89,67,22,184,62,103,155, + 239,172,161,171,129,191,101,109,136,195,96,35,224,91,244,247,197,253,139,192,59,178,18,88,83,230,39,198,152,231,187,236,104,173,13,72,102,90,21,217, + 61,170,44,135,27,99,126,238,178,99,218,254,252,84,224,113,195,13,105,232,238,33,105,3,59,231,42,213,232,218,56,12,78,5,142,44,227,252,5,123, + 15,112,238,28,156,39,175,203,99,25,62,96,140,249,184,203,142,105,11,244,111,1,47,29,106,68,115,227,255,128,207,3,55,116,217,103,25,240,46,38, + 115,9,208,129,214,218,77,140,49,119,22,48,214,36,39,59,102,60,205,90,187,200,24,179,162,128,177,38,241,231,105,198,183,141,49,175,42,59,136,2, + 189,201,24,243,53,151,29,173,181,139,72,202,32,12,163,70,213,168,187,142,100,22,177,111,33,231,131,129,15,1,255,209,185,161,82,141,108,28,6,39, + 3,111,4,124,58,62,214,226,48,248,159,140,239,0,55,147,36,154,22,56,142,179,117,28,6,255,204,107,148,35,162,228,144,76,164,180,34,191,79,225, + 181,91,211,234,255,157,227,108,129,95,70,126,13,240,243,46,203,201,62,137,223,221,130,25,31,0,62,222,101,92,215,15,133,169,98,140,137,172,181,63, + 99,50,146,67,206,140,49,119,89,107,127,192,248,39,135,214,116,249,153,159,11,171,74,60,119,81,46,48,198,124,186,236,32,70,153,49,102,181,181,246, + 56,198,63,57,116,7,112,148,195,23,253,127,89,107,207,35,185,33,50,105,159,29,11,128,103,226,184,204,52,79,90,103,174,223,165,223,227,100,99,146, + 14,99,191,30,100,16,107,237,2,146,47,196,50,97,140,49,43,172,181,223,102,58,146,67,235,188,31,86,170,209,69,113,24,28,5,124,15,255,217,83, + 31,136,195,224,207,149,106,180,222,239,86,165,26,221,21,135,193,175,128,35,60,198,219,154,164,78,233,63,58,198,90,27,135,193,13,184,119,102,158,249, + 126,212,237,6,130,76,49,77,19,148,73,181,25,126,201,207,245,58,148,165,14,192,111,150,207,31,42,213,40,179,110,81,28,6,7,3,111,241,24,107, + 198,235,42,213,232,191,242,190,36,55,90,109,3,124,162,143,113,167,69,17,119,68,199,209,253,101,7,32,35,225,154,178,3,24,19,147,240,251,242,123, + 215,25,32,198,152,91,129,191,15,57,158,178,20,81,119,232,49,76,79,103,159,34,94,175,3,73,18,77,50,153,38,225,253,209,197,147,26,173,246,58, + 51,224,42,213,232,7,192,203,240,91,218,15,201,119,135,239,199,97,144,215,145,248,2,224,70,207,49,247,79,187,39,119,202,91,249,144,199,103,101,133, + 76,25,37,135,100,82,109,238,177,239,61,149,106,20,117,62,25,135,65,5,216,219,99,156,8,248,83,214,134,116,57,217,113,30,99,205,248,112,165,26, + 181,122,236,243,6,198,255,142,183,136,136,12,102,245,144,247,31,23,207,178,214,14,122,125,59,201,75,164,58,21,81,148,122,26,150,224,201,228,51,192, + 247,27,173,246,78,179,159,76,19,68,71,225,159,32,218,156,164,64,245,122,239,71,233,13,223,223,121,142,183,21,201,178,224,206,177,238,7,110,243,24, + 167,18,135,129,146,185,146,73,201,33,153,56,105,86,125,189,182,143,93,228,205,26,218,31,191,117,198,97,151,53,188,111,2,118,202,217,150,231,231,192, + 71,187,237,208,104,181,171,192,231,28,199,155,246,90,69,34,34,50,249,54,7,30,63,224,24,211,148,28,90,102,173,125,196,128,99,76,211,235,37,227, + 201,245,26,120,51,224,255,26,173,246,58,93,106,43,213,232,120,224,229,248,23,19,127,2,240,194,156,109,151,225,63,187,183,154,243,124,222,119,153,60, + 154,61,36,153,148,28,146,73,180,20,88,232,184,239,26,224,166,206,39,211,4,147,207,172,161,85,192,217,89,27,210,174,105,239,241,24,11,146,15,140, + 163,186,21,159,110,180,218,155,147,20,31,117,253,183,118,77,52,137,136,136,76,136,190,235,163,164,141,12,6,77,46,141,155,65,94,175,237,128,71,22, + 24,139,200,48,252,129,156,217,253,25,246,3,142,237,124,178,82,141,190,15,124,164,143,115,127,44,14,131,245,174,213,211,217,67,191,245,28,107,247,56, + 12,178,186,168,222,1,220,231,49,206,86,57,75,212,100,202,41,57,36,147,200,103,73,217,173,57,179,125,182,199,111,253,252,57,149,106,148,247,166,252, + 22,207,152,86,0,207,175,84,163,220,110,43,141,86,123,3,146,2,121,174,5,232,190,15,56,117,166,16,17,17,25,115,131,212,209,121,26,254,221,137, + 198,221,32,175,151,102,13,201,200,107,214,107,171,128,23,145,113,67,56,199,27,27,173,118,86,201,134,143,226,159,208,217,21,120,109,214,134,74,53,186, + 18,248,167,199,88,134,36,121,213,57,142,197,111,246,208,2,96,35,143,253,101,74,40,57,36,147,200,39,17,115,123,206,243,62,29,202,0,194,172,39, + 211,186,69,239,244,28,235,147,149,106,116,65,143,125,222,137,251,5,217,133,192,235,155,245,90,183,169,176,79,112,28,75,68,68,100,212,237,101,173,93, + 214,231,177,211,152,236,216,223,90,235,115,237,52,219,52,190,94,50,154,246,104,180,218,155,230,109,108,214,107,215,3,47,198,125,137,217,215,27,173,246, + 30,179,159,72,111,40,191,20,255,98,210,255,145,214,31,205,226,155,108,122,76,28,6,89,77,119,110,196,175,132,68,238,107,37,211,75,201,33,153,40, + 113,24,44,194,111,198,207,29,57,207,63,220,99,140,127,230,117,40,3,222,134,95,253,163,203,73,218,221,231,74,11,229,125,216,113,188,187,129,35,154, + 245,218,61,93,198,219,149,254,138,101,139,136,136,140,170,126,151,74,61,179,208,40,198,195,6,192,33,190,7,89,107,231,1,79,47,62,28,145,190,108, + 15,124,39,157,93,159,169,89,175,157,14,28,227,56,222,18,224,7,141,86,123,241,236,39,43,213,232,38,252,146,76,144,180,162,127,71,214,134,74,53, + 186,30,88,238,49,214,18,96,189,58,97,149,106,180,26,200,93,117,144,65,201,33,89,143,146,67,50,105,42,30,251,222,93,169,70,43,59,159,140,195, + 96,115,96,75,143,113,206,202,122,50,237,4,208,240,24,7,224,205,105,215,129,110,62,7,108,232,56,222,171,154,245,90,238,7,78,163,213,222,16,56, + 17,200,90,191,156,229,90,199,253,68,68,68,202,228,157,28,74,11,51,239,48,132,88,198,65,63,201,180,253,129,160,232,64,68,58,248,92,123,30,70, + 239,58,159,159,6,126,230,56,222,163,128,255,236,124,178,82,141,126,15,52,61,226,2,120,119,250,29,35,75,102,221,210,46,246,202,121,62,239,166,119, + 150,77,178,58,169,201,116,211,15,132,76,26,159,228,80,222,27,168,207,146,178,136,164,120,116,150,231,225,119,209,244,227,74,53,250,77,183,29,26,173, + 246,33,192,225,142,227,125,174,89,175,157,216,99,159,47,2,251,56,142,7,112,130,199,190,34,34,34,101,57,200,90,235,219,174,121,154,91,178,63,211, + 90,187,192,243,24,45,41,147,161,107,214,107,23,147,148,72,112,245,177,70,171,253,148,46,227,89,224,21,192,191,28,199,59,186,115,121,217,204,121,128, + 155,61,226,170,0,239,206,217,118,25,126,5,165,151,229,44,45,139,60,198,152,135,223,247,38,153,2,74,14,201,164,153,235,228,208,165,105,17,184,44, + 47,243,24,7,122,183,173,223,144,36,153,227,226,12,242,63,128,102,198,123,21,240,26,199,241,32,153,62,171,228,144,136,136,140,131,69,248,47,121,154, + 230,100,199,38,192,19,61,143,153,230,215,75,230,214,241,30,251,110,0,252,176,209,106,111,159,183,67,179,94,139,128,35,73,154,192,244,50,31,248,98, + 163,213,94,167,187,87,165,26,197,192,7,60,226,2,120,69,28,6,235,21,188,79,107,25,245,170,55,58,219,2,178,103,57,222,5,172,246,24,71,51, + 255,100,29,74,14,201,196,72,91,50,46,117,220,125,45,25,217,245,116,41,152,207,148,242,204,14,3,113,24,108,75,210,241,196,213,175,43,213,232,220, + 30,251,188,7,216,197,97,172,91,129,23,166,157,25,50,53,90,237,125,128,175,120,196,7,240,193,102,189,118,165,231,49,34,34,34,101,113,94,42,101, + 173,221,8,56,112,136,177,140,3,159,215,107,75,224,177,67,140,69,100,182,99,129,115,60,246,223,10,248,81,163,213,206,157,13,215,172,215,206,6,222, + 236,56,222,83,129,23,100,60,127,28,112,158,71,92,91,3,7,231,108,235,245,61,160,211,174,157,79,164,55,172,125,102,15,169,238,144,172,67,201,33, + 153,36,27,227,254,51,125,103,165,26,101,21,146,123,24,73,155,72,23,107,129,43,115,182,189,216,35,22,128,79,117,219,152,22,141,126,175,227,88,111, + 111,214,107,185,237,44,27,173,118,64,82,103,104,113,222,62,25,126,65,143,66,217,34,34,34,35,230,16,107,173,235,103,250,193,192,194,97,6,51,6, + 124,234,14,61,19,247,235,37,145,129,52,235,181,251,72,102,250,248,20,92,62,128,222,215,174,199,1,167,56,142,247,233,70,171,189,206,82,174,116,198, + 207,191,123,196,4,240,146,156,231,175,3,110,243,24,103,189,228,80,202,39,57,84,201,154,201,36,211,75,201,33,153,36,69,44,41,243,153,53,116,77, + 165,26,229,77,71,245,89,82,246,55,224,244,30,251,124,129,100,138,124,47,255,15,248,65,222,198,116,74,236,183,200,255,64,201,114,37,240,242,102,189, + 230,211,149,65,68,68,164,108,219,0,251,58,238,171,37,82,176,155,181,246,97,142,251,234,245,146,57,213,172,215,254,73,82,43,200,199,209,141,86,251, + 200,46,99,90,224,77,36,221,125,123,217,137,140,186,159,149,106,244,59,32,244,136,233,136,56,12,214,107,44,147,206,250,241,153,133,180,109,28,6,75, + 50,158,247,41,74,109,208,210,50,153,69,201,33,153,36,27,121,236,155,215,218,221,167,75,89,222,146,178,61,128,199,120,140,243,141,46,117,139,104,180, + 218,143,197,173,197,236,42,224,205,233,7,93,158,119,1,207,245,136,109,37,112,100,179,94,243,249,160,17,17,17,25,21,207,118,220,111,154,139,81,207, + 214,243,245,74,103,99,61,99,14,98,17,89,71,179,94,251,41,73,183,49,31,199,53,90,237,220,164,103,179,94,187,26,248,144,227,88,153,237,232,129, + 175,122,196,179,148,252,235,122,159,228,144,1,118,238,124,178,82,141,238,33,185,126,119,165,162,212,242,0,37,135,100,146,184,182,119,135,140,142,0,105, + 205,162,173,60,198,200,235,114,224,83,0,115,45,112,82,143,125,94,235,56,214,103,210,142,14,153,26,173,246,147,129,79,184,6,150,122,107,179,94,251, + 187,231,49,34,34,34,163,162,231,82,169,116,182,204,122,95,178,166,148,203,210,178,199,226,119,51,77,164,72,239,7,126,239,177,255,82,224,199,141,86, + 187,219,77,228,47,3,215,56,140,117,64,163,213,222,47,227,249,31,225,55,99,39,115,105,89,165,26,221,129,95,7,180,188,149,0,62,177,100,205,62, + 146,41,165,228,144,76,18,215,228,144,5,238,207,120,126,41,110,75,183,72,143,191,62,103,219,227,29,199,0,248,125,165,26,229,126,8,52,90,237,69, + 192,139,28,198,185,138,164,165,102,222,56,91,144,44,55,243,249,157,255,14,208,242,216,95,68,68,100,212,60,218,90,155,219,181,40,165,37,82,15,58, + 208,90,187,73,143,125,52,203,74,74,211,172,215,86,147,212,246,188,193,227,176,71,208,229,6,105,179,94,91,1,124,216,113,172,183,119,62,81,169,70, + 247,145,148,109,112,117,104,28,6,121,203,185,174,245,24,103,215,244,230,118,39,159,218,76,74,14,201,3,148,28,146,137,16,135,193,6,184,23,88,94, + 145,83,140,218,103,214,208,53,57,99,0,60,193,99,156,19,123,108,63,20,183,78,2,31,108,214,107,247,102,109,72,235,12,125,3,216,214,35,174,243, + 129,55,246,88,162,38,34,34,50,14,14,237,177,93,201,161,7,205,167,247,235,161,215,75,74,213,172,215,110,36,185,121,186,198,227,176,55,55,90,237, + 71,119,217,254,29,96,185,195,56,207,206,233,130,246,53,143,88,22,1,79,201,217,230,147,28,218,132,236,101,97,235,173,144,232,98,195,156,4,147,76, + 33,37,135,100,82,248,116,222,202,123,195,244,153,34,157,57,93,51,14,131,237,129,29,61,198,57,185,199,246,151,59,140,113,15,221,151,166,189,14,120, + 142,115,68,16,3,71,228,37,155,68,68,68,198,76,110,29,29,107,237,98,224,160,185,11,101,44,116,123,189,2,224,113,115,24,139,72,166,102,189,246, + 71,220,59,249,66,242,189,247,43,141,86,59,243,251,111,58,35,233,131,14,227,84,200,248,29,168,84,163,75,241,171,25,148,151,168,242,73,14,65,118, + 65,233,172,21,18,121,124,110,176,203,132,83,114,72,38,133,79,189,161,188,164,135,207,204,161,188,181,188,62,75,202,174,170,84,163,188,165,105,51,75, + 202,158,233,48,206,201,205,122,45,179,192,118,163,213,222,20,248,148,71,76,0,175,106,214,107,151,121,30,35,210,205,180,125,214,168,45,172,200,104,121, + 170,181,54,239,58,225,201,248,93,67,76,131,154,181,54,239,125,236,233,232,61,78,70,71,19,248,137,199,254,251,3,175,234,178,253,199,228,52,156,233, + 144,55,123,238,52,143,88,242,146,67,183,2,121,221,144,179,100,45,3,245,57,30,180,180,76,82,211,118,193,46,147,107,160,98,212,169,129,103,14,225, + 151,28,250,107,143,237,251,226,150,201,63,190,203,182,119,145,253,161,145,167,217,172,215,122,21,200,22,241,53,109,133,94,119,42,59,0,17,89,199,134, + 192,193,57,219,180,68,106,125,155,147,127,61,163,122,67,50,50,210,242,7,175,2,46,247,56,236,83,121,197,169,155,245,218,90,224,23,14,99,228,221, + 188,61,213,35,142,204,228,80,90,182,34,247,230,113,134,245,174,243,211,49,124,58,150,41,57,36,128,146,67,50,57,178,214,254,230,201,235,84,230,147, + 28,138,114,158,239,182,150,185,83,216,99,251,147,28,198,184,149,156,15,162,70,171,189,37,25,69,243,186,248,19,240,62,143,253,69,92,237,89,118,0, + 115,108,218,254,189,34,227,32,175,11,151,146,67,217,242,94,47,151,25,205,34,115,166,89,175,221,9,28,137,251,82,170,205,233,94,135,236,20,135,49, + 30,155,94,103,119,250,35,176,202,49,142,237,227,48,200,91,181,224,179,180,44,175,176,181,207,210,50,215,134,60,50,225,148,28,146,73,49,223,99,223, + 172,55,203,197,248,189,49,230,205,28,242,41,250,220,107,230,144,75,114,232,132,102,189,150,247,33,244,108,220,239,4,220,15,188,164,203,88,34,131,152, + 182,100,201,86,113,24,108,81,118,16,34,178,142,245,146,29,214,218,157,128,221,75,136,101,28,172,87,119,200,90,187,55,176,93,9,177,136,116,213,172, + 215,206,5,254,195,227,144,110,157,128,255,64,82,207,179,27,3,60,163,243,201,74,53,186,7,56,195,35,142,34,234,14,229,173,16,240,73,14,249,220, + 100,151,9,166,228,144,76,10,159,228,80,86,151,49,159,55,197,123,43,213,40,111,45,239,54,30,227,92,212,99,123,213,97,140,110,5,173,215,251,208, + 234,226,171,205,122,237,26,143,253,69,156,196,97,48,31,216,163,236,56,74,176,119,217,1,116,99,173,221,204,14,223,57,101,255,59,69,102,217,193,90, + 187,79,199,115,90,34,149,111,79,107,109,231,146,96,205,178,146,81,246,37,220,151,99,29,218,104,181,179,186,124,205,180,181,119,89,30,182,111,206,243, + 103,58,198,0,249,201,161,219,60,198,200,75,14,249,212,29,242,249,30,37,19,76,201,33,153,20,131,38,135,124,142,207,92,82,22,135,193,98,242,167, + 118,118,90,67,178,36,44,83,163,213,94,2,108,230,48,206,165,57,199,207,35,41,26,233,226,94,252,139,86,139,184,122,13,176,180,236,32,74,240,142, + 178,3,16,153,18,89,159,233,121,58,103,15,185,38,59,108,250,24,119,62,175,21,172,255,122,185,38,211,124,207,35,50,176,102,189,118,31,240,17,199, + 221,23,209,189,147,239,175,29,198,216,37,231,121,151,130,214,51,242,146,67,62,173,232,131,156,86,244,154,57,36,222,148,28,146,73,225,147,220,201,186, + 192,243,57,62,175,219,153,207,172,161,155,211,98,113,121,92,150,167,173,6,174,203,217,182,27,110,201,37,128,47,53,235,181,155,28,247,21,113,22,135, + 193,18,224,67,101,199,81,146,103,199,97,112,96,217,65,136,76,129,63,123,236,251,64,178,195,90,187,128,252,34,213,157,254,138,127,247,159,81,116,55, + 126,173,182,103,191,94,27,3,7,56,30,247,71,159,160,68,10,116,28,240,47,199,125,187,37,135,93,198,216,53,231,121,159,228,80,222,50,205,251,112, + 79,72,47,32,187,49,143,207,123,150,146,67,2,104,10,153,76,142,185,92,86,150,199,39,57,116,99,143,237,46,107,250,175,105,214,107,171,115,182,249, + 212,62,250,31,143,125,69,124,188,3,191,159,197,73,243,169,56,12,14,172,84,163,73,152,113,32,50,170,78,2,158,232,184,111,213,90,187,149,49,230, + 102,146,68,135,235,172,198,19,25,241,165,162,30,126,140,251,191,229,32,107,237,198,198,152,187,129,167,226,126,173,116,34,240,228,126,130,27,81,143,181, + 214,126,98,128,227,47,48,198,124,191,176,104,36,87,179,94,91,213,104,181,127,8,28,227,176,123,183,206,162,46,203,211,118,105,180,218,38,237,152,54, + 155,107,114,10,32,179,107,90,165,26,173,141,195,224,126,220,187,49,111,194,250,55,175,125,102,14,41,39,32,128,126,16,100,114,204,243,216,119,208,153, + 67,89,83,55,193,47,57,212,107,166,142,203,23,234,43,187,108,203,235,126,208,233,254,30,227,136,244,37,14,131,157,128,119,151,29,71,201,158,0,188, + 20,248,94,217,129,136,76,176,147,128,99,29,247,53,36,93,138,190,137,95,253,156,31,227,190,92,101,212,157,4,252,167,227,190,11,73,234,23,158,132, + 251,235,181,150,164,30,226,23,253,67,27,89,143,76,31,253,58,9,80,114,104,238,244,170,233,57,99,199,46,219,110,112,56,126,67,96,11,224,150,142, + 231,175,37,153,221,239,242,221,162,91,227,152,251,112,79,14,45,204,120,206,231,198,212,130,56,12,140,110,102,137,150,149,201,52,26,180,230,80,30,159, + 223,167,149,61,182,187,204,28,186,178,203,182,173,29,227,184,172,89,175,169,54,128,20,42,14,131,69,36,119,142,243,138,36,78,147,175,197,97,48,109, + 221,218,68,230,140,49,230,106,224,111,30,135,204,44,149,114,173,159,115,142,49,198,103,38,192,72,51,198,92,64,78,189,194,28,51,175,151,107,114,232, + 79,244,190,1,38,50,76,174,201,161,237,211,26,157,89,110,167,247,181,58,192,198,157,79,84,170,81,183,178,15,157,50,103,14,165,242,202,88,100,89, + 147,241,92,222,205,108,145,92,74,14,201,164,112,206,116,231,100,197,125,150,149,229,189,217,250,20,143,203,202,240,207,230,50,115,232,170,46,219,92,103, + 14,249,92,32,138,184,58,134,252,46,30,211,102,35,224,219,113,24,232,243,86,100,120,78,242,216,247,233,214,218,101,184,47,173,58,209,63,156,145,247, + 19,143,125,15,177,214,238,1,44,115,220,127,18,95,47,25,47,203,113,251,94,48,143,156,235,237,116,169,152,203,236,161,188,228,206,42,135,99,187,29, + 15,74,14,73,9,116,177,42,147,194,53,57,148,55,75,166,136,153,67,62,111,226,139,122,108,95,236,48,70,220,101,91,183,15,155,217,186,37,152,68, + 188,197,97,176,5,112,116,217,113,140,152,125,129,231,151,29,132,200,4,251,177,199,190,75,1,159,250,49,147,152,236,240,121,189,182,6,254,195,113,95, + 139,95,162,78,164,112,205,122,237,94,114,58,11,103,232,118,51,245,118,135,227,123,93,207,247,210,109,89,217,156,38,135,180,164,76,64,201,33,153,28, + 131,190,161,249,212,44,202,123,179,245,121,19,239,53,115,200,101,22,210,150,93,182,221,230,24,199,14,142,251,137,184,58,128,140,105,214,194,51,202,14, + 64,100,82,25,99,46,197,125,41,9,192,139,29,247,187,208,24,179,188,143,144,70,154,49,230,175,192,53,30,135,184,190,94,103,26,99,92,151,211,136, + 12,69,163,213,222,4,216,212,113,247,110,215,203,46,55,118,144,63,16,0,0,32,0,73,68,65,84,106,93,150,158,117,179,40,14,131,188,239,32,62, + 223,109,178,110,126,107,230,144,120,83,114,72,166,205,6,113,24,100,205,18,242,73,236,228,241,89,86,214,171,192,156,75,151,132,135,12,120,60,128,106, + 161,72,209,118,41,59,128,17,149,215,242,86,68,138,225,51,27,166,204,49,71,133,207,210,50,87,147,252,122,201,248,216,205,113,191,53,116,175,13,180, + 153,195,24,46,179,139,122,201,155,237,239,90,140,26,6,159,57,164,89,67,2,40,57,36,147,195,231,77,45,107,214,206,93,30,199,231,221,73,240,73, + 48,117,235,144,0,110,133,236,138,72,14,61,188,209,106,171,107,161,20,73,119,141,179,93,91,118,0,34,19,110,24,203,153,38,57,217,161,215,75,38, + 213,195,28,247,187,166,89,175,173,206,218,208,104,181,13,110,201,161,59,114,158,119,77,204,172,37,255,251,195,92,38,135,68,0,37,135,100,114,100,189, + 41,230,25,52,57,180,89,28,6,89,111,184,221,106,0,117,218,34,14,131,160,203,118,151,228,206,160,45,56,33,41,196,253,80,199,125,69,92,156,73, + 210,194,85,214,245,167,178,3,16,153,100,198,152,115,128,34,187,138,93,102,140,57,175,192,241,70,205,31,129,155,11,28,239,239,198,152,43,11,28,79, + 164,95,174,179,226,175,236,178,109,91,122,55,171,89,65,198,170,129,244,59,66,183,210,15,179,221,144,118,55,203,226,178,172,109,134,146,67,82,8,37, + 135,100,82,248,172,249,205,74,14,221,75,126,177,234,78,139,200,206,230,223,6,220,234,17,71,183,105,175,46,201,161,29,26,173,118,222,239,240,37,184, + 207,100,122,165,227,126,50,94,182,41,227,164,149,106,116,53,240,141,50,206,61,194,254,5,124,187,236,32,164,171,82,126,95,164,112,69,46,149,154,232, + 89,48,198,152,181,192,79,11,28,114,162,95,175,41,55,54,239,143,141,86,123,67,224,213,142,187,95,217,101,219,227,29,142,191,49,237,106,214,105,115, + 160,226,24,67,183,89,197,115,57,115,200,181,187,154,76,56,45,39,145,73,225,243,166,182,94,114,168,82,141,108,28,6,119,1,155,56,142,177,25,29, + 201,151,116,140,11,128,131,28,199,216,13,248,107,206,54,151,153,63,11,72,186,44,220,216,185,161,89,175,173,104,180,218,167,1,207,113,24,231,109,141, + 86,251,243,205,122,205,117,182,145,148,163,102,173,221,206,113,223,10,240,246,97,6,211,195,251,128,103,226,222,250,120,146,173,5,94,83,169,70,131,22, + 173,44,218,42,224,255,101,60,191,27,253,23,170,255,61,235,38,217,47,239,115,156,34,236,103,173,125,189,227,190,243,81,135,189,73,113,18,208,40,104, + 172,105,72,118,156,4,212,11,26,107,18,187,186,77,170,39,90,107,93,203,49,44,6,222,53,204,96,10,246,122,114,218,211,103,248,93,151,109,46,201, + 161,75,114,158,247,169,49,56,204,228,144,207,36,16,37,135,4,80,114,72,38,199,160,51,135,0,238,198,61,57,180,41,217,111,232,62,201,161,189,129, + 227,179,54,52,235,181,149,141,86,251,102,186,183,216,132,164,69,246,47,114,182,253,18,183,228,208,134,192,7,128,55,59,236,43,229,121,83,217,1,184, + 170,84,163,59,226,48,120,1,240,103,122,119,230,155,116,31,168,84,163,211,203,14,162,147,49,230,46,224,224,206,231,173,181,95,0,222,218,231,176,135, + 24,99,138,40,238,95,132,195,211,135,76,151,191,144,204,188,117,77,164,231,185,210,24,243,183,2,226,25,117,191,5,238,196,253,218,39,207,249,198,152, + 203,10,136,71,230,198,75,210,199,68,105,180,218,75,128,247,58,238,126,31,112,114,151,237,79,112,24,35,175,147,161,79,185,134,204,228,80,218,60,167, + 215,178,182,25,247,84,170,81,86,114,199,231,250,107,212,110,96,73,73,180,172,76,38,69,17,201,33,175,186,67,57,207,95,232,49,198,179,123,108,63, + 211,97,140,23,116,217,246,43,143,88,94,215,104,181,213,77,73,102,139,6,57,184,82,141,254,6,124,181,160,88,198,213,37,192,127,151,29,132,204,137, + 59,203,14,64,192,24,99,233,254,133,207,213,48,138,53,143,28,99,204,42,242,111,48,249,152,134,89,86,210,191,129,174,39,60,188,21,216,218,113,223, + 159,53,235,181,204,235,254,70,171,189,8,120,172,195,24,127,204,121,190,136,153,67,62,245,134,110,201,121,222,103,230,145,102,14,9,160,228,144,76,14, + 159,228,80,94,38,222,39,57,180,105,206,243,23,120,140,177,103,28,6,221,238,46,252,198,97,140,231,166,31,98,235,105,214,107,87,3,167,57,198,50, + 31,56,62,111,44,153,74,87,22,48,198,199,240,43,212,62,105,142,233,82,104,82,38,203,21,101,7,32,15,40,34,177,51,77,75,164,244,122,201,176, + 93,57,236,19,52,90,237,125,129,143,120,28,146,57,115,63,245,24,122,207,186,89,9,252,58,103,219,222,30,113,228,37,135,242,190,103,100,201,171,119, + 186,196,99,12,205,28,18,64,203,202,100,114,248,188,169,229,189,89,22,49,115,200,39,57,4,240,92,160,153,179,45,239,67,103,182,77,128,167,1,167, + 228,108,127,127,186,221,69,53,141,229,45,142,251,143,11,215,105,185,178,174,129,191,236,86,170,209,173,113,24,124,6,191,11,182,73,113,38,197,204,96, + 144,241,112,101,217,1,0,155,90,107,247,243,216,127,227,161,69,82,174,223,147,52,136,216,188,207,227,175,195,109,230,238,164,104,147,212,80,244,249,34, + 57,219,114,99,140,207,172,233,113,116,60,253,47,183,5,125,241,30,106,242,188,209,106,111,78,146,160,116,93,70,117,51,201,207,125,158,67,29,198,56, + 61,107,230,81,28,6,11,129,167,59,198,1,249,201,33,159,165,177,121,51,135,124,126,167,53,115,72,0,205,28,146,201,177,94,43,201,46,54,76,215, + 242,118,186,201,99,140,109,226,48,152,215,249,100,165,26,69,192,89,30,227,60,55,111,67,179,94,187,28,183,182,188,185,75,203,154,245,90,136,95,55, + 146,55,55,90,237,81,95,135,62,223,90,187,212,241,177,35,112,68,217,1,143,169,162,46,230,62,11,220,95,208,88,227,228,19,149,106,228,90,240,83, + 198,223,40,204,28,122,42,16,122,60,246,41,39,204,225,50,198,172,6,126,62,192,16,63,73,151,167,77,133,180,78,88,183,47,202,189,76,195,146,178, + 149,198,152,219,7,120,220,93,246,63,160,100,67,123,127,76,187,246,126,23,216,201,227,176,119,55,235,181,204,132,93,163,213,222,8,120,163,195,24,63, + 203,121,254,64,220,59,149,173,2,206,201,217,182,189,227,24,144,49,115,40,14,131,13,240,91,154,54,237,9,76,73,41,57,36,19,33,45,196,230,243, + 198,182,52,227,185,107,60,142,95,8,236,152,179,45,239,3,35,203,1,113,24,108,217,101,187,203,236,161,195,27,173,118,183,187,37,31,4,124,46,116, + 91,141,86,123,79,143,253,231,218,179,73,150,42,185,60,174,2,30,93,78,152,99,237,12,99,76,33,53,2,42,213,232,30,242,139,54,78,178,105,40, + 102,43,137,91,72,146,45,50,58,6,73,88,76,227,18,169,65,150,150,77,227,235,37,238,254,5,92,60,196,241,143,1,158,229,177,255,31,129,239,116, + 217,254,42,242,87,7,204,150,151,128,62,204,35,150,51,42,213,40,47,113,232,147,28,202,154,57,228,83,111,8,58,58,48,203,244,82,114,72,38,137, + 207,27,219,122,89,253,74,53,186,151,100,170,169,171,188,130,115,62,201,161,13,232,254,65,226,186,180,236,165,121,27,155,245,218,249,192,151,60,98,90, + 2,252,184,209,106,103,37,208,166,145,37,187,69,232,36,251,108,193,227,93,84,240,120,163,238,78,224,134,178,131,144,57,243,85,99,204,52,206,142,27, + 101,167,226,183,84,124,198,205,228,23,153,157,100,191,160,191,153,3,87,24,99,206,46,58,24,153,40,159,55,198,172,29,198,192,141,86,251,105,248,45, + 91,95,3,188,169,89,175,101,222,48,109,180,218,243,129,163,29,198,57,55,173,235,153,197,39,57,116,106,214,147,113,24,108,136,251,178,216,21,100,191, + 215,249,38,135,238,241,220,95,38,148,146,67,50,73,124,222,216,242,18,31,87,121,140,145,87,76,250,66,252,166,208,190,162,203,182,223,226,246,239,250, + 175,116,42,108,158,99,240,251,183,237,78,50,131,200,120,28,51,169,174,76,59,186,76,139,43,41,190,86,206,164,215,163,232,116,145,150,148,77,141,21, + 192,151,203,14,66,214,101,140,89,1,252,178,143,67,79,30,214,23,217,81,102,140,185,19,248,93,31,135,78,195,146,50,233,223,157,255,191,189,59,15, + 147,165,46,15,61,254,45,56,236,80,20,155,202,102,84,56,184,129,40,72,161,198,68,19,141,182,201,227,114,93,192,37,94,189,106,199,68,18,53,182, + 102,119,201,114,147,104,108,247,45,118,92,110,220,53,46,224,214,198,45,40,168,41,5,4,36,42,135,229,136,172,30,129,162,224,176,29,56,125,255,168, + 26,156,51,83,213,83,213,221,51,211,211,243,253,60,207,60,100,106,249,213,15,226,84,87,191,245,254,222,23,120,255,114,12,220,233,245,15,3,62,6, + 52,121,78,125,115,183,221,26,86,27,244,169,192,189,107,140,83,250,18,56,75,162,163,128,141,13,230,83,26,28,162,89,189,161,95,86,60,111,52,169, + 55,116,155,205,51,52,199,224,144,102,73,147,224,80,213,122,224,38,1,148,123,100,73,180,168,160,103,113,147,110,146,61,244,168,44,137,30,83,182,163, + 219,110,221,68,254,225,183,148,131,129,87,86,237,44,198,121,38,205,10,206,157,204,120,5,24,103,197,122,10,108,220,9,188,44,8,130,73,103,74,109, + 154,240,120,211,238,162,213,158,128,86,204,107,131,32,104,146,113,170,149,51,202,82,169,245,188,68,202,255,94,154,180,87,45,71,189,165,162,148,194,167, + 128,3,27,156,118,54,121,153,133,170,49,3,224,85,53,199,170,90,82,118,82,131,249,92,15,156,85,177,111,172,122,67,133,38,153,67,46,41,211,93, + 12,14,105,150,52,9,14,237,154,37,81,89,219,246,170,52,209,42,85,75,203,154,102,94,252,99,150,68,85,111,63,234,46,9,251,179,78,175,95,249, + 182,161,219,110,125,15,232,52,156,87,183,211,235,63,188,225,57,179,102,61,5,135,94,28,4,65,147,192,102,93,235,237,141,212,122,202,52,91,207,222, + 18,4,193,235,87,123,18,170,244,37,154,21,195,191,14,248,230,50,205,101,45,248,28,205,150,80,255,28,107,109,169,218,107,130,32,232,45,211,216,255, + 2,60,172,193,241,215,3,79,239,182,91,195,238,7,207,1,30,90,99,172,115,41,169,41,152,37,209,158,192,75,27,204,233,235,97,156,86,253,189,85, + 125,183,40,83,181,132,189,73,230,144,75,202,116,23,131,67,154,37,77,111,110,101,117,135,50,242,15,145,186,170,110,224,223,162,89,1,190,24,120,82, + 217,142,110,187,117,46,245,50,145,246,4,222,188,196,49,239,0,62,222,96,94,27,128,79,118,122,253,97,69,179,103,221,176,20,228,89,177,13,120,69, + 16,4,239,91,237,137,72,107,196,191,82,175,54,133,86,73,145,177,240,159,13,78,57,181,232,116,182,46,5,65,176,5,56,163,193,41,159,89,79,93, + 221,84,219,118,224,245,65,16,252,253,114,12,222,233,245,79,166,89,16,6,224,185,221,118,171,178,220,67,241,98,245,237,53,199,250,219,138,154,69,47, + 4,154,60,43,87,213,27,10,169,110,120,83,230,194,146,49,118,162,186,124,70,25,131,67,186,139,193,33,205,140,98,189,108,147,244,213,73,212,29,58, + 162,184,9,47,156,203,118,224,45,13,198,1,248,135,44,137,118,174,216,87,153,10,187,192,73,197,7,103,169,226,3,173,13,252,164,193,188,14,3,62, + 218,233,245,171,230,54,203,182,3,223,95,237,73,44,163,1,240,73,224,254,65,16,44,21,88,148,148,7,254,31,22,4,193,31,250,197,120,77,104,178, + 84,202,250,57,254,247,210,120,250,192,67,130,32,248,139,229,24,188,211,235,223,15,248,183,134,167,253,99,183,221,250,226,144,49,3,160,7,68,53,198, + 250,50,37,43,3,178,36,218,149,250,75,210,230,84,53,156,57,154,250,117,148,126,17,198,233,117,37,219,35,154,125,199,55,56,164,187,24,28,210,172, + 105,210,126,187,170,19,64,147,224,208,94,192,253,43,246,125,8,184,182,193,88,71,147,215,5,90,164,219,110,157,7,124,162,230,56,239,238,244,250,247, + 170,218,89,212,31,122,26,205,62,12,30,11,188,182,193,241,179,226,93,65,16,204,114,27,246,151,5,65,112,114,16,4,23,175,246,68,164,53,224,189, + 65,16,60,42,8,130,255,94,237,137,168,182,211,168,183,172,53,163,186,56,236,122,242,25,242,151,6,75,185,26,56,115,153,231,162,181,229,245,65,16, + 60,33,8,130,243,150,99,240,78,175,191,55,121,64,114,81,173,207,33,190,1,188,102,137,99,94,2,252,110,141,177,110,3,94,90,145,53,244,28,224, + 240,6,243,58,45,140,211,170,239,26,71,55,24,167,234,249,180,110,167,51,200,151,146,142,210,217,81,51,202,224,144,102,77,147,37,97,123,101,73,84, + 214,225,235,34,242,140,145,186,78,44,219,24,198,233,45,192,187,26,140,3,240,119,69,11,203,50,175,161,94,171,217,253,128,255,232,244,250,187,87,29, + 208,109,183,254,135,60,131,168,137,87,119,122,253,170,14,109,179,232,114,242,46,111,211,226,100,242,165,144,243,127,238,59,230,152,135,141,59,41,105,74, + 253,45,139,255,94,66,242,186,50,163,106,242,240,175,41,16,4,193,245,192,127,213,56,244,243,65,16,140,210,202,125,166,4,65,112,57,37,245,84,74, + 124,118,61,118,117,155,33,47,103,241,189,241,0,198,171,151,183,220,207,19,29,224,1,13,142,191,18,120,86,183,221,170,172,163,213,233,245,79,100,233, + 114,12,115,94,223,109,183,22,53,155,40,50,254,255,178,193,188,0,222,88,182,49,75,162,3,104,214,169,172,42,56,180,127,131,49,110,176,187,170,230, + 51,56,164,89,115,3,245,222,122,205,89,180,62,56,140,211,27,41,89,195,59,196,61,179,36,58,184,98,223,187,168,23,208,153,115,31,42,214,61,119, + 219,173,11,129,215,213,28,231,120,150,88,214,214,109,183,62,70,243,22,204,77,58,49,172,117,167,4,65,48,77,111,83,110,9,130,224,198,5,63,23, + 82,93,140,176,142,38,5,29,165,181,228,246,146,191,151,27,169,247,197,183,202,137,131,193,160,73,219,100,77,135,58,203,159,92,34,245,43,254,247,154, + 125,183,149,220,31,175,3,198,201,250,89,182,231,137,98,233,215,179,26,156,114,7,112,82,183,221,170,236,36,217,233,245,15,36,239,120,182,75,141,241, + 46,5,254,185,98,223,31,210,172,125,125,66,117,109,175,38,89,67,91,129,43,22,110,44,94,48,55,233,84,214,100,197,133,214,1,131,67,154,41,35, + 212,29,186,91,197,246,170,246,146,85,170,178,135,174,6,62,216,112,172,23,102,73,244,130,138,125,255,66,253,238,32,47,238,244,250,207,93,226,152,14, + 208,100,137,196,51,26,28,187,86,221,6,252,245,50,117,237,90,14,227,212,68,122,232,96,48,216,48,177,153,72,211,111,156,238,74,251,3,71,77,106, + 34,90,49,159,99,120,54,240,86,242,90,41,202,45,85,119,232,151,212,203,198,210,218,51,206,253,241,136,193,96,176,92,205,75,30,72,179,76,233,87, + 117,219,173,202,101,143,69,13,205,143,80,63,27,244,79,186,237,214,45,11,55,102,73,180,17,120,67,131,121,1,188,177,44,83,167,232,88,124,76,131, + 113,126,90,145,241,211,100,73,25,24,28,210,2,6,135,52,139,154,220,232,246,172,88,90,118,49,121,22,82,93,199,100,73,84,181,14,250,111,26,206, + 9,224,157,89,18,61,100,225,198,110,187,117,7,240,124,242,0,70,29,255,218,233,245,43,63,108,186,237,214,109,228,217,64,117,107,35,85,101,72,205, + 138,111,3,15,14,130,224,31,87,123,34,13,140,19,28,218,19,56,118,82,19,145,214,128,113,11,204,63,124,34,179,208,138,9,130,224,106,224,187,67, + 14,249,82,16,4,139,190,248,173,87,65,16,108,98,120,151,206,83,131,32,104,210,242,94,107,199,180,222,31,155,60,123,126,10,120,235,18,199,188,26, + 120,92,205,241,78,45,43,104,93,44,39,251,32,205,90,198,95,74,117,240,117,35,112,96,131,177,38,177,164,204,122,67,90,196,224,144,102,81,147,34, + 208,80,146,61,84,116,27,59,167,193,24,59,147,47,229,90,36,140,211,45,212,239,54,54,103,119,224,63,178,36,90,212,61,161,219,110,253,152,250,181, + 112,246,0,62,221,233,245,195,170,3,186,237,214,101,228,1,167,245,106,43,249,82,147,23,3,143,10,130,160,73,39,183,105,48,206,155,62,240,203,174, + 214,151,89,254,123,249,47,224,209,13,126,206,95,185,169,173,186,97,217,48,46,145,90,204,255,94,235,211,90,191,63,94,6,188,168,162,104,52,0,157, + 94,191,197,210,69,170,231,220,72,94,159,169,204,43,129,71,52,155,30,111,10,227,116,81,96,181,200,26,122,84,131,113,182,1,151,148,140,179,51,245, + 186,174,205,177,222,144,22,113,57,129,102,209,13,228,153,53,187,213,60,254,32,242,104,254,66,231,144,223,172,235,214,152,56,33,75,162,51,202,110,252, + 192,123,200,11,64,63,168,230,88,144,215,31,250,247,44,137,158,82,4,171,230,123,51,240,155,192,147,107,140,179,17,120,127,167,215,127,198,144,15,204, + 113,234,112,172,180,77,228,29,104,198,177,5,184,160,248,217,188,198,91,82,143,251,166,239,97,192,59,38,49,17,105,218,5,65,112,245,96,48,184,156, + 209,139,167,174,246,151,159,97,126,25,4,193,233,117,15,30,12,6,77,178,99,215,186,207,0,221,146,237,183,2,149,109,174,215,177,207,80,254,5,58, + 5,190,182,194,115,209,202,249,49,121,105,134,38,29,193,230,91,237,58,134,23,118,219,173,172,106,103,167,215,191,39,249,114,178,186,207,245,47,232,182, + 91,155,23,110,204,146,232,24,224,239,26,206,237,58,224,3,21,251,142,0,14,109,48,214,197,97,156,150,21,15,111,218,194,190,233,203,116,173,3,6, + 135,52,115,194,56,29,100,73,180,133,250,15,255,123,102,73,180,119,24,167,59,212,42,10,227,244,134,44,137,54,81,191,198,196,222,228,181,135,190,83, + 50,167,59,178,36,250,99,224,91,53,199,154,243,68,224,31,178,36,250,235,249,209,253,110,187,53,232,244,250,207,39,15,234,28,81,99,156,167,145,191, + 253,168,219,149,97,154,253,40,8,130,87,174,246,36,166,69,16,4,215,15,6,131,139,128,81,59,201,77,243,151,93,105,57,36,140,30,28,122,224,96, + 48,216,103,202,138,213,107,9,65,16,108,30,12,6,143,6,118,93,176,43,11,130,160,73,157,194,117,33,8,130,115,7,131,193,99,89,252,69,243,250, + 32,8,198,233,104,165,41,22,4,193,246,193,96,112,22,205,178,88,230,59,97,48,24,236,60,141,203,14,59,189,254,110,228,75,206,234,46,187,122,115, + 183,221,250,143,133,27,139,140,254,143,176,248,94,178,148,215,133,113,186,181,100,188,128,60,147,179,137,170,90,161,77,235,13,109,105,120,188,214,1,151, + 149,105,86,85,118,40,168,112,143,138,237,77,11,83,63,58,75,162,125,203,118,132,113,250,109,224,195,13,199,131,188,69,230,219,179,36,218,225,239,181, + 219,110,165,192,211,201,223,124,214,241,103,35,92,91,107,195,56,217,67,247,25,12,6,85,133,217,165,89,52,206,223,203,78,64,60,169,137,104,229,4, + 65,112,122,16,4,95,93,240,211,164,33,195,186,18,4,193,215,75,254,123,173,165,44,99,141,102,156,251,227,94,52,203,144,95,73,191,65,253,123,247, + 153,192,159,47,220,88,212,40,253,2,205,10,71,67,222,157,172,170,59,240,189,105,246,178,226,74,96,115,201,220,54,0,119,111,48,78,26,198,105,147, + 110,202,90,39,12,14,105,38,133,113,154,81,63,104,2,112,112,150,68,101,237,44,55,209,172,88,219,174,192,19,134,236,127,41,229,75,216,150,114,10, + 240,145,44,137,118,120,83,209,109,183,126,8,188,164,230,24,182,96,158,93,107,189,78,128,180,146,252,123,145,164,114,179,122,127,172,251,12,252,11,224, + 164,110,187,181,67,134,92,150,68,187,145,47,183,252,245,134,215,189,21,120,97,73,121,136,81,106,13,1,156,81,81,39,232,96,242,250,167,117,153,53, + 164,82,6,135,52,203,154,220,248,118,166,36,114,95,220,204,191,221,240,186,247,203,146,168,116,41,90,24,167,215,147,103,251,212,237,54,54,223,51,129, + 211,22,118,87,235,182,91,31,0,222,55,194,120,154,29,147,168,59,36,173,23,103,1,227,212,25,155,214,47,63,146,52,174,245,252,60,177,29,120,102, + 183,221,186,114,254,198,34,43,231,163,212,239,112,54,223,107,195,56,189,176,98,223,3,128,95,107,48,214,181,192,162,166,41,69,144,169,73,205,162,1, + 6,135,84,193,224,144,102,217,213,13,143,63,180,168,244,191,208,89,52,47,218,246,187,21,153,72,132,113,122,54,121,6,209,40,30,15,124,61,75,162, + 133,235,138,95,59,226,120,154,13,231,0,119,140,113,190,95,118,181,110,4,65,112,3,80,245,176,94,199,137,147,154,139,36,77,147,32,8,54,51,94, + 224,96,45,63,79,156,222,109,183,190,57,127,67,81,210,161,7,60,117,132,241,126,0,188,169,108,71,150,68,123,2,191,219,112,188,239,148,101,32,145, + 55,214,217,189,193,56,46,41,83,37,131,67,154,89,69,225,183,235,27,156,178,1,56,164,100,156,59,129,175,55,188,124,68,222,77,172,74,15,248,80, + 195,49,231,156,8,124,105,193,182,169,43,254,167,149,19,4,193,205,228,157,215,70,117,194,96,48,104,146,142,44,173,117,227,44,157,56,96,48,24,212, + 109,84,32,73,107,205,56,217,67,71,14,6,131,3,39,54,147,149,181,195,179,116,177,148,236,195,192,243,71,24,235,118,224,5,97,156,86,189,184,107, + 145,215,104,170,235,38,224,220,138,125,77,27,44,52,173,203,170,117,196,224,144,102,221,21,13,143,63,124,97,225,231,194,143,129,159,55,28,235,215,179, + 36,42,45,244,91,172,23,254,35,224,252,134,99,206,105,146,134,170,245,97,156,135,185,61,153,222,34,146,210,114,24,119,233,196,90,126,59,46,73,195, + 172,231,165,101,243,253,38,240,172,17,207,125,94,24,167,165,207,248,89,18,109,164,249,51,215,247,202,2,77,89,18,133,64,216,96,156,59,48,56,164, + 33,12,14,105,214,93,11,220,210,224,248,93,41,233,92,86,4,115,254,179,225,181,119,2,78,202,146,168,52,213,179,200,108,122,2,112,113,195,113,165, + 50,179,90,68,82,90,14,254,189,72,82,57,239,143,227,249,203,48,78,63,94,182,163,200,70,122,98,195,241,110,35,95,162,86,166,105,214,208,85,197, + 138,8,169,148,193,33,205,180,34,168,51,74,246,208,162,174,6,97,156,254,156,60,131,168,137,3,129,167,87,100,35,17,198,233,21,192,111,3,63,107, + 56,174,180,144,153,16,82,125,63,4,182,45,121,84,181,89,121,51,46,73,11,249,60,49,186,30,240,250,33,251,127,135,102,153,62,0,73,24,167,139, + 58,48,23,47,159,15,106,48,206,40,223,137,180,206,24,28,210,122,112,53,205,106,242,236,65,222,18,178,204,87,104,222,105,236,72,242,15,131,82,97, + 156,94,6,60,6,111,216,26,207,143,104,150,37,183,208,122,126,152,211,58,19,4,193,109,192,121,99,12,113,244,96,48,216,123,82,243,145,164,105,17, + 4,193,22,96,243,24,67,172,215,58,134,95,1,78,169,104,53,79,150,68,15,6,30,218,112,204,235,128,111,85,236,59,20,88,244,50,123,136,107,203, + 130,76,210,124,6,135,52,243,138,53,186,77,3,47,247,201,146,104,215,146,177,82,224,203,35,76,227,225,89,18,29,87,181,51,140,211,139,201,3,68, + 215,140,48,182,68,16,4,119,144,119,45,27,213,17,131,193,160,201,27,40,173,13,77,30,28,71,57,126,45,27,231,237,248,206,64,60,169,137,72,210, + 148,25,231,254,184,55,112,244,164,38,178,70,156,7,156,20,198,105,105,70,106,150,68,135,211,124,57,25,192,231,203,198,44,178,134,154,180,175,7,184, + 124,132,235,107,157,49,56,164,245,226,50,154,45,33,216,64,158,241,83,230,92,154,47,47,3,248,189,44,137,42,11,73,135,113,250,83,224,183,128,75, + 71,24,91,130,241,235,4,184,84,102,71,179,16,40,57,126,48,24,236,82,231,192,193,96,16,0,199,47,243,124,166,137,117,53,36,169,156,247,199,250, + 190,11,60,38,140,211,172,108,103,150,68,17,240,76,242,151,10,77,156,21,198,105,213,119,130,163,104,246,61,126,107,241,130,91,26,106,195,106,79,64, + 90,9,97,156,222,145,37,209,165,228,55,211,186,238,150,37,209,53,97,156,94,187,96,172,65,150,68,95,0,14,39,127,59,82,215,206,192,201,89,18, + 189,63,140,211,95,86,204,243,199,89,18,157,0,124,146,188,22,145,212,196,36,234,4,124,126,18,19,153,144,48,75,162,7,134,113,122,193,74,95,56, + 75,162,157,129,19,87,250,186,203,224,32,224,127,6,131,193,37,53,142,189,39,112,191,101,158,207,52,177,174,134,164,58,30,48,24,12,94,54,193,241, + 78,13,130,96,243,4,199,91,14,147,184,63,190,103,18,19,153,114,159,3,158,29,198,105,233,178,254,98,21,194,179,104,214,182,30,32,3,190,90,49, + 230,65,192,254,13,199,243,197,179,106,49,56,164,245,228,42,224,16,154,5,116,142,202,146,40,89,88,217,63,140,211,173,89,18,157,6,60,187,225,28, + 246,4,94,144,37,209,135,195,56,189,178,236,128,48,78,175,205,146,232,241,192,27,129,73,62,140,104,246,205,226,155,190,111,101,73,244,121,134,215,250, + 58,11,120,95,221,14,28,89,18,253,54,240,116,170,223,226,5,228,45,108,239,221,100,162,83,236,72,170,51,33,215,179,255,1,110,162,217,103,194,124, + 179,16,60,148,180,180,152,201,46,35,189,136,241,106,250,172,132,179,128,237,140,190,202,100,26,159,39,38,237,157,192,203,170,158,61,138,230,54,79,5, + 238,62,194,216,95,168,40,66,189,1,216,216,112,172,27,170,94,74,75,11,25,28,210,186,81,100,252,92,12,28,219,224,180,221,200,191,32,94,84,50, + 222,133,89,18,157,69,243,101,24,123,2,207,203,146,232,227,85,233,162,69,157,164,151,103,73,116,46,249,155,151,69,245,143,164,133,130,32,184,104,48, + 24,92,15,236,55,226,16,39,12,6,131,157,131,32,152,166,54,167,251,3,207,171,113,220,22,224,179,75,29,84,164,119,127,17,216,125,204,121,105,141, + 11,130,96,251,96,48,56,155,60,16,56,138,3,7,131,193,198,32,8,54,77,114,94,146,180,218,130,32,184,105,48,24,252,24,120,224,136,67,108,28, + 12,6,7,4,65,112,237,210,135,174,73,127,1,188,97,72,241,233,0,248,61,70,203,198,61,63,140,211,11,43,246,221,155,230,223,9,22,125,135,145, + 170,88,115,72,235,74,24,167,215,3,77,163,231,135,102,73,180,79,197,190,47,147,215,51,106,106,55,224,247,179,36,26,250,161,17,198,233,7,128,71, + 146,119,162,146,234,24,39,21,124,47,224,152,73,77,100,133,221,183,230,113,135,177,182,2,67,77,187,35,174,150,237,140,215,26,126,181,204,98,182,157, + 36,77,130,117,12,23,203,128,103,134,113,250,250,33,129,161,157,128,167,208,188,51,25,192,86,42,26,223,20,223,69,154,22,161,190,38,140,211,27,71, + 152,135,214,41,131,67,90,143,46,6,74,111,232,21,2,224,190,197,205,126,7,69,134,207,39,128,235,71,152,199,92,13,162,135,12,59,40,140,211,239, + 3,199,1,127,5,216,130,82,75,177,142,202,108,169,122,123,56,109,46,13,130,96,45,6,135,252,123,145,164,114,222,31,119,244,53,224,232,48,78,63, + 81,117,64,81,175,240,233,52,91,165,48,223,151,195,56,189,185,100,220,128,250,47,193,230,108,199,90,67,106,200,224,144,214,157,162,104,92,211,118,142, + 123,83,81,179,35,140,211,173,192,71,24,45,112,19,0,79,206,146,232,55,138,27,127,169,48,78,183,133,113,250,79,228,173,65,191,54,194,117,180,126, + 76,99,38,196,214,101,24,115,154,45,122,176,27,195,138,23,227,30,209,249,171,61,129,17,249,102,92,146,202,77,227,243,196,106,184,25,120,9,240,184, + 48,78,127,94,117,80,150,68,187,144,119,37,123,192,136,215,57,35,140,211,170,149,2,135,210,188,62,222,229,101,117,139,164,97,12,14,105,189,250,25, + 205,151,64,28,146,37,209,193,101,59,138,66,111,159,32,143,210,143,226,49,228,203,204,170,150,175,205,93,231,98,224,113,64,123,196,235,104,246,77,227, + 151,221,115,151,97,204,105,118,206,4,199,90,43,193,161,53,185,244,181,232,24,180,101,140,33,142,25,12,6,163,22,180,150,164,105,118,30,227,45,109, + 62,97,48,24,172,245,239,154,103,0,199,134,113,250,238,170,101,100,112,87,87,178,103,211,188,88,244,156,243,128,175,87,140,189,59,205,27,100,220,206, + 104,101,47,180,206,173,245,63,88,105,36,197,114,176,58,173,157,23,218,152,37,81,88,49,230,165,192,23,198,152,214,17,192,31,213,168,67,52,8,227, + 116,154,218,141,107,138,4,65,112,53,205,51,227,230,219,56,24,12,14,156,212,124,0,194,56,189,26,184,98,146,99,78,185,31,76,112,172,159,177,54, + 50,175,214,100,112,168,48,206,210,137,157,129,19,38,53,17,73,154,22,197,82,225,31,142,49,196,62,228,25,239,107,85,2,60,58,140,211,161,5,157, + 139,214,242,109,70,239,112,122,9,112,106,89,240,169,88,166,118,52,213,221,85,171,108,46,190,235,72,141,24,28,210,186,21,198,233,85,52,47,78,189, + 19,240,192,226,13,65,217,152,103,3,95,25,99,90,123,2,207,204,146,232,137,85,215,168,80,231,205,206,110,99,158,15,222,51,214,138,113,235,4,44, + 71,246,208,36,3,38,211,236,86,242,22,233,19,17,4,193,128,241,238,41,43,97,43,240,237,213,158,196,24,172,171,33,73,229,166,225,254,56,236,249, + 117,190,97,157,86,235,116,232,222,33,56,19,198,233,13,85,109,234,231,100,73,244,32,224,15,128,131,106,140,95,230,26,224,147,67,174,115,20,205,151, + 147,221,4,92,53,226,124,180,206,249,69,79,235,221,79,128,91,26,158,179,27,121,128,168,244,239,39,140,211,239,146,183,202,30,199,241,192,139,179,36, + 58,164,230,241,41,176,212,27,130,168,211,235,87,125,56,222,64,189,37,113,251,116,122,125,239,27,211,111,26,235,4,124,104,25,198,156,70,31,91,134, + 183,117,127,202,116,103,15,253,77,16,4,87,174,246,36,198,48,141,127,47,146,52,13,166,225,254,120,88,205,227,174,27,178,111,255,26,231,215,94,66, + 151,37,209,46,89,18,61,9,120,42,176,75,221,243,22,184,1,248,72,85,93,160,44,137,14,3,238,222,112,204,237,192,143,135,45,129,147,134,241,75, + 158,214,181,226,75,220,255,208,188,86,208,190,192,253,171,138,72,23,29,198,78,165,89,87,180,133,14,0,218,89,18,61,37,75,162,125,135,29,216,109, + 183,6,212,171,155,177,95,197,249,219,129,107,107,156,191,59,80,55,96,165,213,51,117,153,67,97,156,126,154,188,46,215,44,187,2,120,197,164,7,13, + 130,224,50,224,117,147,30,119,66,190,15,188,109,181,39,49,166,113,255,94,78,156,200,44,36,105,250,76,195,243,196,225,53,143,27,246,28,123,64,141, + 243,107,5,135,178,36,58,0,120,17,121,39,225,81,221,10,124,56,140,211,172,226,26,17,121,185,137,166,46,42,26,229,72,35,49,56,164,117,47,140, + 211,27,129,161,235,137,43,28,4,220,111,72,128,232,28,224,51,140,94,164,26,242,110,102,15,6,254,36,75,162,199,101,73,180,199,144,99,235,4,135, + 134,213,146,169,19,28,130,209,139,237,105,229,252,128,241,2,147,241,96,48,104,186,190,189,142,83,200,83,168,103,213,139,194,56,77,151,105,236,183,48, + 125,133,189,239,0,94,20,4,193,56,247,184,85,23,4,193,22,242,218,78,163,58,104,48,24,148,118,179,148,164,53,238,167,228,25,46,163,58,106,48, + 24,212,201,218,25,166,110,230,208,184,193,161,161,207,209,89,18,109,200,146,232,145,192,139,105,158,209,51,223,157,228,89,198,165,215,43,158,245,31,72, + 254,29,160,137,45,97,156,174,229,44,94,77,1,131,67,18,80,220,76,71,249,210,122,119,134,4,75,194,56,61,31,248,20,195,215,65,215,177,1,120, + 4,240,178,44,137,30,89,180,203,92,232,23,53,198,25,22,28,170,91,127,201,224,208,148,11,130,224,6,224,194,49,134,216,155,101,40,34,25,198,233, + 181,192,255,98,188,47,226,211,232,54,224,229,97,156,246,151,235,2,65,16,220,1,60,1,56,109,185,174,209,208,197,192,227,130,32,56,111,181,39,50, + 33,211,216,229,79,146,86,85,81,247,238,172,113,134,96,252,251,227,74,101,14,13,107,83,191,17,248,35,224,177,64,147,154,160,11,109,3,62,17,198, + 105,233,115,80,150,68,187,1,199,210,124,169,218,109,140,247,220,39,1,6,135,164,249,46,4,110,30,225,188,67,178,36,170,76,253,12,227,244,199,192, + 7,128,210,212,209,134,118,39,255,96,122,105,145,114,58,223,184,193,161,186,237,156,13,14,173,13,211,80,39,96,145,162,38,215,49,64,111,194,67,111, + 171,121,220,164,235,1,125,31,120,72,24,167,111,157,240,184,139,4,65,112,85,16,4,79,38,111,151,219,180,152,254,164,220,9,188,17,56,38,8,130, + 111,174,210,28,150,195,52,20,93,149,164,105,180,218,207,19,247,170,121,220,197,67,246,213,9,14,45,234,244,154,37,209,254,89,18,61,27,120,78,205, + 49,134,185,9,248,96,24,167,165,65,156,162,17,205,177,228,207,250,77,12,200,235,12,213,125,14,146,42,213,169,220,46,173,11,97,156,222,153,37,209, + 5,228,107,136,155,46,169,57,60,75,162,65,24,167,151,84,140,125,121,150,68,239,5,158,78,253,15,185,97,246,1,14,38,47,68,61,167,78,112,103, + 88,55,133,243,201,179,58,150,114,223,26,199,12,243,31,192,127,143,113,254,217,99,94,127,53,108,3,222,59,230,24,77,179,109,62,66,243,98,235,243, + 141,147,70,62,84,177,148,243,15,178,36,250,40,240,36,224,161,192,67,104,222,145,99,206,181,212,207,168,249,41,240,93,70,127,88,189,19,184,128,124, + 233,222,153,192,191,175,116,187,216,32,8,62,54,24,12,190,6,252,49,249,131,228,49,228,45,116,155,166,160,215,181,133,252,254,112,62,240,225,32,8, + 86,162,243,220,71,25,189,251,11,52,191,79,124,25,24,103,105,216,165,99,156,59,231,199,140,119,159,104,250,118,255,84,198,235,172,55,201,12,192,211, + 25,63,195,118,185,253,27,163,103,12,252,116,146,19,1,190,202,240,226,187,85,198,249,76,104,226,78,198,251,223,242,36,254,158,230,251,226,50,140,57, + 142,113,255,118,62,64,222,221,118,84,77,255,238,63,71,189,130,206,85,70,94,234,212,233,245,143,164,126,139,248,97,247,192,58,247,247,29,50,135,178, + 36,58,1,104,209,252,59,65,153,45,228,197,167,75,151,158,103,73,180,1,120,16,163,253,255,245,178,101,92,210,174,117,102,185,30,36,165,53,43,75, + 162,187,3,247,31,241,244,171,129,11,195,56,45,173,193,145,37,209,206,228,153,63,147,120,203,252,181,48,78,207,152,251,165,211,235,255,21,240,127,151, + 56,231,109,221,118,235,101,101,59,58,189,254,227,129,58,203,98,182,2,99,236,114,195,0,0,20,155,73,68,65,84,119,239,182,91,22,188,211,196,20, + 221,255,238,197,104,95,190,54,87,117,251,168,184,86,64,254,160,56,202,3,223,207,194,56,93,169,47,88,181,13,6,131,189,201,107,20,28,194,228,62, + 219,83,224,71,65,16,212,201,74,148,36,105,162,58,189,254,171,128,55,212,56,244,138,110,187,85,90,155,168,211,235,239,10,220,200,210,207,23,27,187, + 237,214,93,53,72,179,36,250,3,38,211,132,229,82,242,165,100,85,93,201,118,38,127,209,19,142,48,246,13,192,15,237,78,166,73,49,115,72,90,32, + 140,211,107,178,36,218,155,250,107,156,231,187,7,176,91,150,68,23,148,101,19,132,113,122,39,240,149,44,137,174,36,207,152,24,181,253,37,44,78,111, + 173,243,5,238,17,67,246,253,55,121,106,234,82,95,44,247,2,158,66,158,153,34,77,68,17,80,45,205,188,91,134,107,13,128,77,43,113,173,149,18, + 4,193,77,140,151,145,39,73,210,180,169,147,209,14,195,179,69,143,166,222,139,167,43,230,254,143,226,133,213,221,106,94,123,152,31,2,159,47,158,255, + 23,41,106,136,30,195,104,129,161,219,177,109,189,38,204,154,67,82,137,48,78,47,166,100,237,113,77,251,1,15,46,138,202,85,141,127,62,121,202,245, + 101,35,94,3,22,7,135,234,116,92,123,112,167,215,47,77,89,237,182,91,41,245,83,141,127,191,230,113,146,36,73,82,35,157,94,255,16,234,103,218, + 15,171,139,116,124,141,243,47,235,182,91,243,179,130,247,99,188,36,138,109,192,151,128,83,135,4,134,246,32,47,101,49,74,96,232,78,224,188,38,89, + 211,82,29,6,135,164,10,97,156,94,196,188,183,8,13,237,13,28,87,100,32,85,141,191,133,124,221,248,151,200,163,255,77,45,12,14,125,143,188,91, + 193,48,27,200,235,187,84,249,78,205,107,63,174,211,235,143,211,198,83,146,36,73,170,82,55,107,8,242,26,106,85,234,4,135,206,88,240,251,56,207, + 184,151,3,239,9,227,52,169,202,234,201,146,104,31,242,90,139,123,140,48,254,118,224,252,48,78,111,26,99,142,82,41,131,67,210,16,97,156,110,98, + 244,66,122,187,145,7,136,42,215,43,135,113,58,8,227,52,1,222,73,243,22,148,123,101,73,116,87,71,131,110,187,117,43,121,161,221,165,12,91,90, + 246,245,154,215,222,9,120,86,205,99,37,73,146,164,90,58,189,254,110,192,43,107,30,190,9,248,209,144,253,117,178,143,190,189,224,247,81,150,148,109, + 7,190,1,188,63,140,211,107,171,14,202,146,104,127,224,193,140,86,99,113,174,51,153,5,168,181,44,12,14,73,75,219,4,92,53,226,185,59,1,71, + 101,73,244,128,162,19,65,169,48,78,111,0,62,70,222,201,171,73,161,231,133,129,167,255,170,113,206,176,15,201,83,217,177,3,218,48,175,234,244,250, + 251,212,60,86,146,36,73,170,227,197,212,239,238,251,233,110,187,85,154,161,211,233,245,143,32,239,2,182,148,133,153,67,77,131,67,191,0,122,97,156, + 126,171,170,41,13,64,150,68,135,145,215,24,26,181,3,218,166,98,229,129,180,44,12,14,73,75,40,82,66,47,36,239,68,54,170,187,1,199,23,105, + 164,149,215,9,227,244,71,192,91,129,175,81,175,229,236,125,22,252,254,205,26,231,252,70,209,185,97,145,34,251,168,110,161,233,67,128,215,212,60,86, + 146,36,73,26,170,211,235,135,192,171,27,156,242,233,33,251,158,86,227,252,29,106,110,22,29,77,15,173,121,237,109,192,233,192,123,195,56,173,124,145, + 156,37,209,134,44,137,142,38,239,148,58,106,71,209,75,195,56,29,117,53,131,84,139,193,33,169,134,34,64,244,83,224,154,49,134,217,131,124,153,217, + 61,139,15,158,170,107,221,94,180,168,127,11,121,176,103,88,29,161,133,193,161,255,6,150,42,78,183,31,240,196,33,251,223,191,196,249,243,189,188,211, + 235,63,160,193,241,146,36,73,82,149,87,2,7,214,60,246,28,224,172,33,251,159,81,99,140,51,187,237,214,252,108,159,187,1,251,46,113,206,118,224, + 251,192,219,194,56,253,102,89,135,226,57,197,139,225,135,82,255,223,169,204,21,97,156,254,108,140,243,165,90,12,14,73,53,21,1,162,159,48,94,128, + 40,32,15,232,60,52,75,162,161,221,9,194,56,189,45,140,211,211,201,131,68,223,162,188,104,245,193,89,18,221,213,125,172,219,110,221,6,156,89,99, + 30,47,24,178,239,28,224,220,26,99,64,94,224,250,109,157,94,127,212,183,32,146,36,73,18,157,94,255,94,192,43,26,156,242,134,33,75,202,238,197, + 240,38,44,115,190,177,224,247,163,150,56,254,2,224,157,97,156,126,49,140,211,27,135,29,88,44,35,59,14,216,125,216,113,75,184,134,122,29,137,165, + 177,25,28,146,26,152,23,32,218,60,230,80,123,145,103,17,29,53,172,22,81,113,205,91,194,56,253,6,240,38,224,11,236,88,32,59,0,238,189,224, + 148,58,75,203,90,157,94,191,52,101,182,248,144,237,213,24,99,206,99,128,231,54,56,94,146,36,73,186,75,167,215,223,19,248,44,249,51,114,29,155, + 201,107,117,86,169,147,53,180,29,248,248,130,109,247,173,56,246,82,242,186,66,159,26,86,112,26,242,54,245,89,18,29,203,120,203,200,0,126,14,252, + 164,170,235,153,52,105,67,191,148,74,90,172,184,65,111,206,146,232,70,224,254,140,247,119,116,8,112,96,150,68,23,3,191,24,118,243,15,227,244,86, + 224,7,192,15,178,36,58,152,188,5,230,131,200,51,145,46,152,119,104,157,224,208,78,192,243,128,127,172,216,255,111,192,159,2,71,212,24,11,160,215, + 233,245,47,237,182,91,11,187,61,72,146,36,73,149,138,12,244,247,146,119,241,170,235,77,221,118,171,116,57,87,167,215,223,5,56,165,198,24,95,239, + 182,91,119,189,116,205,146,104,47,118,172,55,180,13,56,31,248,254,176,154,66,243,206,223,9,56,28,248,53,198,79,194,184,40,140,211,203,199,28,67, + 106,196,204,33,105,68,197,91,131,179,104,214,93,172,204,174,228,65,166,227,179,36,170,181,30,57,140,211,171,194,56,253,18,208,101,199,192,16,192,247, + 168,151,126,250,130,170,229,96,197,242,180,63,171,51,151,194,174,192,169,157,94,255,254,13,206,145,36,73,146,94,6,60,167,193,241,151,1,239,27,178, + 255,36,242,0,205,82,62,180,224,247,141,228,153,62,91,128,47,1,221,48,78,79,171,25,24,218,23,56,158,60,163,127,156,239,216,219,129,31,25,24, + 210,106,48,56,36,141,33,140,211,91,128,179,201,63,68,198,181,55,112,116,150,68,199,103,73,180,127,205,235,111,11,227,244,146,249,219,138,162,122,111, + 173,113,250,17,192,111,15,217,255,89,242,90,71,117,237,7,124,185,211,235,223,163,193,57,146,36,73,90,167,58,189,254,99,129,55,54,60,237,101,221, + 118,235,230,138,241,2,234,189,224,188,153,252,89,119,190,1,240,1,224,93,97,156,38,69,214,254,80,89,18,237,150,37,209,125,201,51,250,235,46,137, + 171,178,13,248,97,24,167,191,28,115,28,105,36,6,135,164,49,133,113,122,103,24,167,23,0,151,144,127,168,140,107,31,224,65,89,18,61,36,75,162, + 253,135,117,54,27,226,131,228,173,57,151,242,207,157,94,191,244,62,80,212,30,106,82,20,16,242,183,52,95,236,244,250,7,53,60,79,146,36,73,235, + 72,167,215,63,25,248,34,176,115,131,211,190,4,156,58,100,127,139,188,236,194,82,62,219,109,183,110,154,191,33,140,211,115,195,56,253,89,157,26,63, + 89,18,237,154,37,209,145,192,137,192,193,53,174,183,148,91,128,179,195,56,205,38,48,150,52,18,131,67,210,132,132,113,122,25,249,186,228,202,118,150, + 13,237,75,254,225,22,103,73,116,120,150,68,187,212,61,177,248,176,171,83,84,250,161,192,179,135,140,115,22,121,253,161,38,142,3,126,208,233,245,143, + 111,120,158,36,73,146,102,92,167,215,15,58,189,254,43,201,139,65,239,218,224,212,219,128,151,14,233,80,22,0,127,81,115,172,166,207,183,0,100,73, + 180,33,75,162,251,144,7,133,14,99,50,223,167,51,242,192,208,45,19,24,75,26,153,193,33,105,130,194,56,189,142,188,104,116,157,172,157,186,246,32, + 95,2,246,240,44,137,238,151,37,81,88,243,188,183,3,119,214,56,238,159,138,14,17,85,94,206,226,186,70,75,185,39,112,102,167,215,127,94,195,243, + 36,73,146,52,163,58,189,254,206,192,219,128,127,25,225,244,63,239,182,91,23,15,217,255,116,224,55,107,140,115,6,112,122,147,11,103,73,180,123,17, + 20,122,24,249,115,110,147,108,167,97,174,32,95,74,182,109,66,227,73,35,27,167,181,158,164,33,178,36,186,59,121,11,203,218,25,63,13,108,37,175, + 115,244,203,48,78,111,170,58,168,211,235,127,28,56,185,198,120,175,233,182,91,127,63,100,156,251,146,7,189,246,110,58,81,224,29,64,167,219,110,221, + 62,194,185,146,36,73,154,1,157,94,127,35,240,46,224,177,35,156,254,113,224,217,67,178,134,66,224,39,212,91,226,245,59,221,118,235,107,75,29,84, + 148,118,216,143,188,131,217,1,13,230,90,199,109,192,79,139,23,203,210,84,48,56,36,45,163,98,41,216,189,201,91,214,47,151,91,129,95,22,63,55, + 204,95,39,221,233,245,31,6,124,183,198,24,55,3,27,231,183,243,92,168,211,235,159,4,124,98,196,57,94,12,252,53,240,169,162,96,182,36,73,146, + 214,129,78,175,191,23,240,87,192,43,105,182,140,108,206,5,192,195,22,214,8,90,112,141,183,2,47,173,49,214,119,128,71,86,5,153,32,175,39,4, + 220,141,60,40,180,71,195,185,214,177,5,184,208,108,33,77,27,131,67,210,10,40,150,130,29,197,104,153,55,77,92,27,198,233,249,243,55,116,122,253, + 51,129,71,212,56,247,19,192,179,134,125,88,118,122,253,191,3,94,61,198,252,206,38,79,9,94,242,109,141,36,73,146,214,174,162,6,208,211,128,55, + 1,135,143,56,76,6,156,208,109,183,46,28,114,157,227,129,132,122,37,83,30,223,109,183,254,115,209,69,146,104,55,224,64,224,32,32,26,113,174,75, + 185,3,216,20,198,233,53,203,52,190,52,150,13,171,61,1,105,61,8,227,52,203,146,232,44,242,194,117,247,98,114,235,148,23,58,32,75,162,112,65, + 167,131,63,33,255,192,92,234,154,39,3,223,4,254,117,200,49,175,37,127,227,243,231,35,206,239,56,224,171,157,94,255,107,228,5,179,191,220,109,183, + 110,28,113,44,73,146,36,77,153,78,175,127,79,242,134,39,207,5,30,48,198,80,55,146,7,115,134,5,134,118,3,222,75,189,192,208,119,129,175,206, + 223,144,37,209,33,192,61,128,186,53,61,71,149,2,63,9,227,244,214,101,190,142,52,50,51,135,164,21,86,188,153,56,146,252,205,196,114,184,46,140, + 211,243,230,111,232,244,250,255,68,189,238,13,183,3,143,40,186,148,149,42,222,2,189,129,60,53,120,92,183,145,127,72,127,22,56,173,219,110,253,114, + 2,99,74,146,36,105,133,20,207,134,7,3,191,7,252,62,245,138,66,47,229,38,224,113,221,118,171,178,60,66,113,221,30,240,194,26,227,13,200,159, + 113,191,55,183,33,75,162,253,128,99,199,157,232,18,238,4,54,3,151,207,47,253,32,77,35,131,67,210,42,201,146,104,31,242,110,7,7,50,249,191, + 197,115,195,56,189,126,238,151,78,175,191,7,240,67,242,165,109,75,185,20,56,190,219,110,93,95,117,64,241,97,252,207,192,159,141,59,209,121,182,147, + 127,120,254,124,193,207,150,98,223,124,11,63,92,7,53,247,77,219,239,211,52,151,181,52,183,133,191,79,211,92,156,219,108,95,107,220,177,230,91,120, + 223,31,246,123,147,99,151,251,247,105,154,139,115,91,123,115,91,47,255,158,179,124,237,157,128,125,200,95,116,110,44,126,142,100,178,165,19,110,34,207, + 24,250,206,176,131,58,189,254,139,129,247,212,28,243,173,221,118,235,229,115,191,100,73,180,19,240,80,96,88,199,222,113,12,128,171,128,205,97,156,218, + 148,69,107,130,193,33,105,149,101,73,180,7,249,58,236,123,80,47,37,182,142,91,128,239,135,113,122,87,80,165,211,235,63,18,248,118,205,243,63,15, + 60,101,169,226,209,157,94,255,185,228,169,188,187,143,58,81,73,146,36,169,176,25,120,90,183,221,58,123,216,65,157,94,255,225,228,237,232,235,116,5, + 222,12,28,51,191,160,117,150,68,135,3,71,140,62,205,161,182,0,151,134,113,122,243,50,141,47,45,11,131,67,210,148,40,58,35,28,70,222,217,108, + 18,245,192,54,135,113,186,121,254,134,78,175,255,14,224,148,154,231,255,27,240,226,26,1,162,135,0,159,33,175,165,36,73,146,36,141,226,43,192,115, + 186,237,214,181,195,14,234,244,250,135,0,63,160,94,219,122,88,80,132,186,40,241,16,51,249,26,160,55,0,23,47,168,253,41,173,25,147,202,82,144, + 52,166,48,78,111,15,227,244,18,224,123,228,173,223,111,27,115,200,123,22,89,73,243,253,37,112,89,205,243,95,4,188,187,211,235,15,189,79,116,219, + 173,115,200,211,114,79,109,62,69,73,146,36,137,191,7,126,175,102,96,232,27,212,15,12,253,191,146,238,100,71,48,217,192,208,86,224,252,48,78,207, + 49,48,164,181,204,204,33,105,74,21,107,161,15,2,238,6,236,199,104,193,220,178,226,212,143,7,250,13,198,120,55,112,202,176,22,247,243,198,126,18, + 240,118,242,90,74,146,36,73,210,48,223,3,94,218,109,183,190,191,212,129,157,94,255,48,242,192,208,198,154,99,255,12,56,174,219,110,93,55,183,33, + 75,162,8,120,240,40,19,45,113,35,112,5,112,141,197,166,53,11,12,14,73,107,64,150,68,27,128,3,200,131,69,251,211,44,80,116,97,24,167,87, + 206,223,208,233,245,255,156,188,160,116,93,239,36,255,224,30,186,196,172,24,123,47,224,213,64,135,201,44,143,147,36,73,210,108,185,146,188,177,201,71, + 107,190,128,188,39,240,77,224,62,53,199,191,153,188,59,217,185,115,27,178,36,218,25,56,14,216,171,249,116,239,114,39,112,13,112,101,24,167,55,45, + 117,176,180,150,24,28,146,214,152,226,131,109,46,80,116,0,75,7,138,182,3,231,132,113,122,227,220,134,162,219,216,91,128,151,54,184,244,23,128,231, + 118,219,173,180,206,193,157,94,255,222,192,43,200,219,139,46,92,222,38,73,146,164,245,103,43,240,102,224,245,243,11,68,15,211,233,245,143,4,190,74, + 179,250,150,79,239,182,91,159,158,191,33,75,162,251,3,119,111,48,198,124,55,146,7,180,126,17,198,233,157,35,142,33,77,53,131,67,210,26,86,4, + 138,246,39,15,18,133,84,183,227,188,21,56,43,140,211,109,115,27,138,90,66,31,3,78,106,112,201,77,192,83,187,237,214,143,234,158,208,233,245,15, + 4,254,184,248,57,160,193,181,36,73,146,52,27,18,242,102,39,31,239,182,91,55,46,117,240,156,78,175,255,20,224,131,192,190,13,174,245,186,110,187, + 245,183,243,55,100,73,116,24,112,100,131,49,224,87,89,66,87,205,127,201,42,205,42,131,67,210,12,41,130,69,251,204,251,217,155,95,5,140,174,5, + 126,52,127,77,116,167,215,223,13,248,50,240,91,13,46,179,21,120,65,183,221,250,100,147,185,117,122,253,221,129,199,2,79,3,158,76,94,71,73,146, + 36,73,179,233,58,224,163,64,175,219,110,157,183,212,193,243,117,122,253,93,128,127,34,47,83,208,196,167,129,147,230,151,66,40,234,12,29,75,189,239, + 190,91,129,235,139,159,212,44,33,173,39,6,135,164,25,87,212,43,218,155,60,88,148,46,124,243,209,233,245,247,5,78,39,255,208,108,226,61,192,95, + 214,93,102,182,224,154,187,0,143,6,30,7,60,132,124,253,183,193,34,73,146,164,181,235,103,192,25,197,207,153,192,5,117,234,85,46,212,233,245,15, + 5,62,1,252,122,195,83,207,0,158,48,127,185,90,209,182,254,120,96,215,138,115,110,231,87,193,160,235,195,56,29,183,91,176,180,102,25,28,146,68, + 167,215,63,24,248,14,205,214,114,3,252,130,252,141,206,71,234,20,19,28,114,253,0,248,53,242,64,209,193,228,129,162,253,139,127,238,203,226,123,85, + 217,189,171,206,182,81,207,91,137,109,211,50,143,178,109,211,50,143,178,109,211,50,143,178,109,211,50,143,178,109,211,50,143,178,109,179,60,143,178,251, + 100,157,109,163,158,183,94,198,95,141,107,174,245,241,167,229,154,235,109,219,180,204,99,156,109,119,2,91,230,253,252,162,248,231,166,110,187,117,121,201, + 249,181,117,122,253,93,129,83,128,215,0,81,195,211,191,1,60,169,219,110,109,157,219,80,116,254,125,48,121,233,5,200,255,93,110,37,207,14,186,129, + 188,171,239,214,133,3,73,235,149,193,33,73,0,116,122,253,123,1,167,1,199,140,112,250,55,129,151,116,219,173,159,76,116,82,146,36,73,154,105,197, + 75,194,39,3,255,66,243,186,64,144,151,72,120,90,183,221,186,101,254,198,44,137,14,32,207,156,191,153,60,32,116,75,24,167,141,51,153,164,245,194, + 224,144,164,187,116,122,253,125,128,15,145,127,64,55,181,13,120,63,208,237,182,91,155,38,58,49,73,146,36,205,156,78,175,127,28,208,37,47,55,48, + 138,83,129,147,187,237,150,203,193,164,49,25,28,146,180,131,162,139,217,223,1,127,61,226,16,3,224,51,192,27,186,237,86,50,177,137,73,146,36,105, + 205,43,158,53,127,27,104,3,207,96,244,239,164,31,5,158,223,109,183,182,45,121,164,164,37,25,28,146,84,170,211,235,159,76,222,58,116,247,49,134, + 57,29,248,87,224,139,221,118,43,155,196,188,36,73,146,180,246,116,122,253,95,3,158,15,252,31,242,90,147,163,186,3,120,37,240,182,113,106,94,74, + 218,145,193,33,73,149,58,189,254,241,228,233,186,135,142,57,212,237,192,127,146,183,23,61,173,219,110,93,55,238,220,36,73,146,52,221,58,189,254,225, + 192,163,128,255,13,60,150,241,191,127,94,70,190,140,236,123,227,206,77,210,142,12,14,73,26,170,232,100,246,62,224,9,19,26,242,14,242,246,166,63, + 0,206,46,126,54,117,219,173,59,39,52,190,36,73,146,86,88,167,215,223,0,28,11,60,130,188,13,253,35,128,195,39,120,137,47,2,207,235,182,91, + 215,78,112,76,73,5,131,67,146,150,52,175,139,196,91,24,47,13,184,202,86,224,92,224,231,192,245,192,117,197,63,175,39,111,53,90,214,89,98,22, + 219,200,78,203,60,202,182,77,203,60,202,182,77,203,60,202,182,77,203,60,202,182,77,203,60,202,182,205,234,60,22,170,106,121,223,244,152,113,182,173, + 245,241,87,227,154,107,125,252,105,185,230,122,219,54,45,243,24,119,219,6,224,0,224,160,146,159,251,0,123,149,156,51,174,173,192,107,129,55,119,219, + 45,187,141,73,203,196,224,144,164,218,58,189,254,158,192,95,1,175,2,118,93,229,233,72,146,36,105,118,13,200,59,225,190,186,219,110,93,181,218,147, + 145,102,157,193,33,73,141,117,122,253,141,192,219,129,199,175,246,92,36,73,146,52,115,190,1,188,162,219,110,157,187,218,19,145,214,139,157,86,123,2, + 146,214,158,110,187,181,137,188,6,209,83,129,75,86,121,58,146,36,73,154,13,63,5,158,8,60,214,192,144,180,178,204,28,146,52,150,78,175,191,51, + 121,6,209,75,128,223,197,251,138,36,73,146,154,57,23,120,15,240,190,110,187,181,109,181,39,35,173,71,126,137,147,52,49,157,94,255,222,192,31,2, + 47,36,47,86,40,73,146,36,149,73,129,143,0,239,239,182,91,103,175,246,100,164,245,206,224,144,164,137,235,244,250,187,3,207,0,78,1,78,92,229, + 233,72,146,36,105,122,124,29,120,31,240,185,110,187,117,203,106,79,70,82,206,224,144,164,101,213,233,245,15,5,30,85,252,60,26,56,106,85,39,36, + 73,146,164,149,116,9,240,29,224,76,224,43,221,118,235,210,85,158,143,164,18,6,135,36,173,168,78,175,127,48,240,155,252,42,96,244,128,213,157,145, + 36,73,146,38,100,27,112,22,191,10,6,125,215,54,244,210,218,96,112,72,210,170,234,244,250,187,144,215,39,186,91,241,115,208,130,127,30,192,226,123, + 85,217,189,171,206,182,81,207,91,137,109,211,50,143,178,109,211,50,143,178,109,211,50,143,178,109,211,50,143,178,109,211,50,143,178,109,179,60,143,65, + 201,246,58,219,70,61,111,189,140,191,26,215,92,235,227,79,203,53,221,54,61,243,168,187,109,59,112,45,176,165,234,167,219,110,221,94,50,142,36,73, + 146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146, + 36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36, + 73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73, + 146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146, + 36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36, + 73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73, + 146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146, + 36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36, + 73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73, + 146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146, + 36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36, + 73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73, + 146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146, + 36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,36,73,146,86,193,255,7,144,15,120,105,109,29,203,95,0,0,0,0,73,69,78,68,174,66, + 96,130,0,0}; +const char* Assets::tapLogo_png = (const char*) temp1; + ```
diff --git a/Source/UI/Assets.h b/Source/UI/Assets.h new file mode 100644
```diff index 0000000..e36e344 --- /dev/null +++ b/Source/UI/Assets.h @@ -0,0 +1,10 @@ +/* (Auto-generated binary data file). */ + +#pragma once + +namespace Assets +{ + extern const char* tapLogo_png; + const int tapLogo_pngSize = 30362; + +} ```
diff --git a/Source/UI/MeterComponent.cpp b/Source/UI/MeterComponent.cpp new file mode 100644
```diff index 0000000..cf40027 --- /dev/null +++ b/Source/UI/MeterComponent.cpp @@ -0,0 +1,39 @@ +/* + ============================================================================== + + MeterComponent.cpp + Created: 27 Feb 2021 2:11:36am + Author: Joshua Hodge + + ============================================================================== +*/ + +#include +#include "MeterComponent.h" + +//============================================================================== +MeterComponent::MeterComponent() +{ + // In your constructor, you should add any child components, and + // initialise any special settings that your component needs. + +} + +MeterComponent::~MeterComponent() +{ +} + +void MeterComponent::paintOverChildren (juce::Graphics& g) +{ + auto bounds = getLocalBounds().reduced (20, 35).translated (0, 10); + auto leftMeter = bounds.removeFromTop (bounds.getHeight() / 2).reduced (0, 5); + auto rightMeter = bounds.reduced (0, 5); + g.setColour (juce::Colours::white); + g.drawRoundedRectangle (leftMeter.toFloat(), 5, 2.0f); + g.drawRoundedRectangle (rightMeter.toFloat(), 5, 2.0f); +} + +void MeterComponent::resized() +{ + +} ```
diff --git a/Source/UI/MeterComponent.h b/Source/UI/MeterComponent.h new file mode 100644
```diff index 0000000..e8fd172 --- /dev/null +++ b/Source/UI/MeterComponent.h @@ -0,0 +1,30 @@ +/* + ============================================================================== + + MeterComponent.h + Created: 27 Feb 2021 2:11:36am + Author: Joshua Hodge + + ============================================================================== +*/ + +#pragma once + +#include +#include "CustomComponent.h" + +//============================================================================== +/* +*/ +class MeterComponent : public CustomComponent +{ +public: + MeterComponent(); + ~MeterComponent() override; + + void paintOverChildren (juce::Graphics& g) override; + void resized() override; + +private: + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MeterComponent) +}; ```
diff --git a/tapSynth.jucer b/tapSynth.jucer index 8341de6..b3292ac 100644
```diff --- a/tapSynth.jucer +++ b/tapSynth.jucer @@ -27,6 +27,8 @@ + + + + + + + ```

Diff 13: e8bccebc9afa79aa9ee138b2469365e480025819 to cc201efd2b1c93bd623b95d356f82fabe151cdf5

diff --git a/Source/Data/MeterData.cpp b/Source/Data/MeterData.cpp new file mode 100644
```diff index 0000000..b2257d9 --- /dev/null +++ b/Source/Data/MeterData.cpp @@ -0,0 +1,55 @@ +/* + ============================================================================== + + MeterData.cpp + Created: 27 Feb 2021 9:37:46pm + Author: Joshua Hodge + + ============================================================================== +*/ + +#include "MeterData.h" + +void MeterData::processRMS (juce::AudioBuffer& buffer) +{ + // Running total of all samples in the callback + float sum = 0; + + for (int ch = 0; ch < buffer.getNumChannels(); ++ch) + { + auto output = buffer.getReadPointer (ch); + + for (int s = 0; s < buffer.getNumSamples(); ++s) + { + auto squaredSample = output[s] * output[s]; + sum+=squaredSample; + } + + auto avg = sum / buffer.getNumSamples(); + rms.store (std::sqrt (avg)); + } +} + +void MeterData::processPeak (juce::AudioBuffer& buffer) +{ + auto max = 0.0f; + + for (int ch = 0; ch < buffer.getNumChannels(); ++ch) + { + auto output = buffer.getReadPointer (ch); + + for (int s = 0; s < buffer.getNumSamples(); ++s) + { + auto abs = std::abs (output[s]); + + if (abs > max) + max = abs; + } + + peak.store (max); + } +} + + + + ```
diff --git a/Source/Data/MeterData.h b/Source/Data/MeterData.h new file mode 100644
```diff index 0000000..ff79491 --- /dev/null +++ b/Source/Data/MeterData.h @@ -0,0 +1,26 @@ +/* + ============================================================================== + + MeterData.h + Created: 27 Feb 2021 9:37:46pm + Author: Joshua Hodge + + ============================================================================== +*/ + +#pragma once +#include + +class MeterData +{ +public: + void processRMS (juce::AudioBuffer& buffer); + void processPeak (juce::AudioBuffer& buffer); + + const std::atomic& getRMS() { return rms; } + const std::atomic& getPeak() { return peak; } + +private: + std::atomic rms { 0.0f }; + std::atomic peak { 0.0f }; +}; ```
diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index 263cad3..157b498 100644
```diff --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -20,6 +20,7 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess , lfo1 (audioProcessor.apvts, "LFO1FREQ", "LFO1DEPTH") , filterAdsr (audioProcessor.apvts, "FILTERATTACK", "FILTERDECAY", "FILTERSUSTAIN", "FILTERRELEASE") , reverb (audioProcessor.apvts, "REVERBSIZE", "REVERBDAMPING", "REVERBWIDTH", "REVERBDRY", "REVERBWET", "REVERBFREEZE") +, meter (audioProcessor) { auto tapImage = juce::ImageCache::getFromMemory (BinaryData::tapLogo_png, BinaryData::tapLogo_pngSize); @@ -56,18 +57,20 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess filter.setBoundsColour (filterColour); lfo1.setBoundsColour (filterColour); + + startTimerHz (30); setSize (1080, 525); } TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor() { + stopTimer(); } //============================================================================== void TapSynthAudioProcessorEditor::paint (juce::Graphics& g) { g.fillAll (juce::Colours::black); - } void TapSynthAudioProcessorEditor::resized() @@ -85,4 +88,7 @@ void TapSynthAudioProcessorEditor::resized() logo.setBounds (meter.getRight(), osc2.getBottom() + 30, 250, 100); } - +void TapSynthAudioProcessorEditor::timerCallback() +{ + repaint(); +} ```
diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 07f853a..176e996 100644
```diff --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -22,6 +22,7 @@ /** */ class TapSynthAudioProcessorEditor : public juce::AudioProcessorEditor +, public juce::Timer { public: TapSynthAudioProcessorEditor (TapSynthAudioProcessor&); @@ -30,6 +31,8 @@ public: //============================================================================== void paint (juce::Graphics&) override; void resized() override; + + void timerCallback() override; private: TapSynthAudioProcessor& audioProcessor; ```
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index c7fe9c7..1f06cb8 100644
```diff --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -170,6 +170,9 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer& buffer, juc synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples()); juce::dsp::AudioBlock block { buffer }; reverb.process (juce::dsp::ProcessContextReplacing (block)); + + meter.processRMS (buffer); + meter.processPeak (buffer); } //============================================================================== ```
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index 12372da..a19641b 100644
```diff --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -11,6 +11,7 @@ #include #include "SynthVoice.h" #include "SynthSound.h" +#include "Data/MeterData.h" //============================================================================== /** @@ -55,6 +56,8 @@ public: void getStateInformation (juce::MemoryBlock& destData) override; void setStateInformation (const void* data, int sizeInBytes) override; + const std::atomic& getRMS() { return meter.getRMS(); } + const std::atomic& getPeak() { return meter.getPeak(); } juce::AudioProcessorValueTreeState apvts; private: @@ -70,6 +73,7 @@ private: static constexpr int numVoices { 5 }; juce::dsp::Reverb reverb; juce::Reverb::Parameters reverbParams; + MeterData meter; //============================================================================== JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessor) ```
diff --git a/Source/UI/MeterComponent.cpp b/Source/UI/MeterComponent.cpp index cf40027..c31d0d2 100644
```diff --- a/Source/UI/MeterComponent.cpp +++ b/Source/UI/MeterComponent.cpp @@ -12,7 +12,7 @@ #include "MeterComponent.h" //============================================================================== -MeterComponent::MeterComponent() +MeterComponent::MeterComponent (TapSynthAudioProcessor& p) : audioProcessor (p) { // In your constructor, you should add any child components, and // initialise any special settings that your component needs. @@ -28,6 +28,21 @@ void MeterComponent::paintOverChildren (juce::Graphics& g) auto bounds = getLocalBounds().reduced (20, 35).translated (0, 10); auto leftMeter = bounds.removeFromTop (bounds.getHeight() / 2).reduced (0, 5); auto rightMeter = bounds.reduced (0, 5); + + g.setColour (juce::Colour::fromRGB (247, 190, 67)); + const std::atomic& rms = audioProcessor.getRMS(); + auto rmsLevel = juce::jmap (rms.load(), 0.0f, 1.0f, 0.1f, leftMeter.getWidth()); + + g.fillRoundedRectangle (leftMeter.getX(), leftMeter.getY(), rmsLevel, leftMeter.getHeight(), 5); + g.fillRoundedRectangle (rightMeter.getX(), rightMeter.getY(), rmsLevel, rightMeter.getHeight(), 5); + + g.setColour (juce::Colour::fromRGB (246, 87, 64).withAlpha (0.5f)); + const std::atomic& peak = audioProcessor.getPeak(); + auto peakLevel = juce::jmap (peak.load(), 0.0f, 1.0f, 0.1f, leftMeter.getWidth()); + + g.fillRoundedRectangle (leftMeter.getX(), leftMeter.getY(), peakLevel, leftMeter.getHeight(), 5); + g.fillRoundedRectangle (rightMeter.getX(), rightMeter.getY(), peakLevel, rightMeter.getHeight(), 5); + g.setColour (juce::Colours::white); g.drawRoundedRectangle (leftMeter.toFloat(), 5, 2.0f); g.drawRoundedRectangle (rightMeter.toFloat(), 5, 2.0f); ```
diff --git a/Source/UI/MeterComponent.h b/Source/UI/MeterComponent.h index e8fd172..95acd3b 100644
```diff --- a/Source/UI/MeterComponent.h +++ b/Source/UI/MeterComponent.h @@ -11,6 +11,7 @@ #pragma once #include +#include "../PluginProcessor.h" #include "CustomComponent.h" //============================================================================== @@ -19,12 +20,14 @@ class MeterComponent : public CustomComponent { public: - MeterComponent(); + MeterComponent (TapSynthAudioProcessor& p); ~MeterComponent() override; void paintOverChildren (juce::Graphics& g) override; void resized() override; private: + TapSynthAudioProcessor& audioProcessor; + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MeterComponent) }; ```
diff --git a/tapSynth.jucer b/tapSynth.jucer index b3292ac..5588642 100644
```diff --- a/tapSynth.jucer +++ b/tapSynth.jucer @@ -20,6 +20,8 @@ + + ```