dev/juce/ TapSynthGitDiff
This is from tapSynth by The Audio Programmer
Diff 0: e7a154de0b83dab2332d8dc06a3bace88794bb46 to 97731b4dae33477bc2711462cd53a16576b6f890
diff –git a/Source/[PluginProcessor](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index acffd7f..d69180f 100644
--- 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<SynthVoice*>(synth.getVoice(i)))
+ {
+ voice->prepareToPlay (sampleRate, samplesPerBlock, getTotalNumOutputChannels());
+ }
+ }
}
void TapSynthAudioProcessor::releaseResources()
diff –git a/Source/[SynthVoice](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index 72b6997..9be4d2f 100644
--- 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<float> audioBlock { outputBuffer };
+ osc.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
+ gain.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
+ adsr.applyEnvelopeToBuffer (outputBuffer, startSample, numSamples);
}
diff –git a/Source/[SynthVoice](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index a606b19..1048347 100644
--- 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<float> osc { [](float x) { return std::sin (x); }};
+ juce::dsp::Gain<float> gain;
+ bool isPrepared { false };
+
+ // return std::sin (x); //Sine Wave
+ // return x / MathConstants<float>::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
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<JUCERPROJECT id="cNKSGD" name="tapSynth" projectType="audioplug" useAppConfig="0"
- addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1">
+ addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn">
<MAINGROUP id="KMptmw" name="tapSynth">
<GROUP id="{3C4B8AF0-CC1B-B776-74F0-C1D0B8E63F93}" name="Source">
<FILE id="cCsQG0" name="PluginProcessor.cpp" compile="1" resource="0"
@@ -36,6 +36,7 @@
<MODULEPATH id="juce_graphics" path="../../../../../../Applications/JUCE/modules"/>
<MODULEPATH id="juce_gui_basics" path="../../../../../../Applications/JUCE/modules"/>
<MODULEPATH id="juce_gui_extra" path="../../../../../../Applications/JUCE/modules"/>
+ <MODULEPATH id="juce_dsp" path="../../../../../../Applications/JUCE/modules"/>
</MODULEPATHS>
</XCODE_MAC>
</EXPORTFORMATS>
@@ -49,6 +50,7 @@
<MODULE id="juce_audio_utils" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_core" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
+ <MODULE id="juce_dsp" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_events" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_graphics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="1"/>
Diff 1: 97731b4dae33477bc2711462cd53a16576b6f890 to b2530760f0a925df7c3098ba9151e8c657723fed
diff –git a/Source/[PluginEditor](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index 889bd56..3214a71 100644
--- 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<SliderAttachment>(audioProcessor.apvts, "ATTACK", attackSlider);
+ decayAttachment = std::make_unique<SliderAttachment>(audioProcessor.apvts, "DECAY", decaySlider);
+ sustainAttachment = std::make_unique<SliderAttachment>(audioProcessor.apvts, "SUSTAIN", sustainSlider);
+ releaseAttachment = std::make_unique<SliderAttachment>(audioProcessor.apvts, "RELEASE", releaseSlider);
+
+ oscSelAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(audioProcessor.apvts, "OSC", oscSelector);
}
TapSynthAudioProcessorEditor::~TapSynthAudioProcessorEditor()
diff –git a/Source/[PluginEditor](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index 9f34431..82b7c57 100644
--- 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<SliderAttachment> attackAttachment;
+ std::unique_ptr<SliderAttachment> decayAttachment;
+ std::unique_ptr<SliderAttachment> sustainAttachment;
+ std::unique_ptr<SliderAttachment> releaseAttachment;
+ std::unique_ptr<juce::AudioProcessorValueTreeState::ComboBoxAttachment> oscSelAttachment;
+
TapSynthAudioProcessor& audioProcessor;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
diff –git a/Source/[PluginProcessor](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index d69180f..9bc772f 100644
--- 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<std::unique_ptr<juce::RangedAudioParameter>> params;
+
+ // OSC select
+ params.push_back (std::make_unique<juce::AudioParameterChoice> ("OSC", "Oscillator", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+
+ // ADSR
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("ATTACK", "Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, }, 0.1f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("DECAY", "Decay", juce::NormalisableRange<float> { 0.1f, 1.0f, }, 0.1f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("SUSTAIN", "Sustain", juce::NormalisableRange<float> { 0.1f, 1.0f, }, 1.0f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("RELEASE", "Release", juce::NormalisableRange<float> { 0.1f, 3.0f, }, 0.4f));
+
+ return { params.begin(), params.end() };
+}
diff –git a/Source/[PluginProcessor](PluginProcessor).h b/Source/[PluginProcessor](PluginProcessor).h
index 9c4296a..68c34b4 100644
--- 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](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index 9bc772f..e8eb9dc 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -157,6 +157,10 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer<float>& 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](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index 9be4d2f..169aa3a 100644
--- 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<float> audioBlock { outputBuffer };
+ if (! isVoiceActive())
+ return;
+
+ synthBuffer.setSize (outputBuffer.getNumChannels(), numSamples, false, false, true);
+ synthBuffer.clear();
+
+ juce::dsp::AudioBlock<float> audioBlock { synthBuffer };
osc.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
gain.process (juce::dsp::ProcessContextReplacing<float> (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](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index 1048347..1ee7b23 100644
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -27,6 +27,7 @@ public:
private:
juce::ADSR adsr;
juce::ADSR::Parameters adsrParams;
+ juce::AudioBuffer<float> synthBuffer;
juce::dsp::Oscillator<float> osc { [](float x) { return std::sin (x); }};
juce::dsp::Gain<float> gain;
Diff 3: 38ff9b2a20ffed9481f264b549499c9591a12a62 to be86a6aca2a09395ab10c3c820a5424a263862b8
diff –git a/Source/[PluginEditor](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index 3214a71..f535bbf 100644
--- a/Source/PluginEditor.cpp
+++ b/Source/PluginEditor.cpp
@@ -23,6 +23,11 @@ TapSynthAudioProcessorEditor::TapSynthAudioProcessorEditor (TapSynthAudioProcess
releaseAttachment = std::make_unique<SliderAttachment>(audioProcessor.apvts, "RELEASE", releaseSlider);
oscSelAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(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](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index 82b7c57..cfe4fca 100644
--- 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](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index e8eb9dc..538500b 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -146,21 +146,23 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer<float>& 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<juce::SynthesiserVoice*>(synth.getVoice(i)))
+ if (auto voice = dynamic_cast<SynthVoice*>(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](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index 169aa3a..a1a2e74 100644
--- 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](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index 1ee7b23..6f43fad 100644
--- 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<float> synthBuffer;
- juce::dsp::Oscillator<float> osc { [](float x) { return std::sin (x); }};
+ juce::dsp::Oscillator<float> osc { [](float x) { return x / juce::MathConstants<float>::pi; }};
juce::dsp::Gain<float> gain;
bool isPrepared { false };
Diff 4: be86a6aca2a09395ab10c3c820a5424a263862b8 to fed9a4238122b2ce7b3ddb5e05819c90a42ca965
diff –git a/Source/Data/[AdsrData](AdsrData).cpp b/Source/Data/[AdsrData](AdsrData).cpp
new file mode 100644
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](AdsrData).h b/Source/Data/[AdsrData](AdsrData).h
new file mode 100644
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 <JuceHeader.h>
+
+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](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index f535bbf..e4ba37c 100644
--- 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<SliderAttachment>(audioProcessor.apvts, "ATTACK", attackSlider);
- decayAttachment = std::make_unique<SliderAttachment>(audioProcessor.apvts, "DECAY", decaySlider);
- sustainAttachment = std::make_unique<SliderAttachment>(audioProcessor.apvts, "SUSTAIN", sustainSlider);
- releaseAttachment = std::make_unique<SliderAttachment>(audioProcessor.apvts, "RELEASE", releaseSlider);
-
oscSelAttachment = std::make_unique<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(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](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index cfe4fca..9f78696 100644
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -10,6 +10,7 @@
#include <JuceHeader.h>
#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<SliderAttachment> attackAttachment;
- std::unique_ptr<SliderAttachment> decayAttachment;
- std::unique_ptr<SliderAttachment> sustainAttachment;
- std::unique_ptr<SliderAttachment> releaseAttachment;
std::unique_ptr<juce::AudioProcessorValueTreeState::ComboBoxAttachment> oscSelAttachment;
-
TapSynthAudioProcessor& audioProcessor;
+ AdsrComponent adsr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
};
diff –git a/Source/[PluginProcessor](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index 538500b..9163db2 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -159,7 +159,7 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer<float>& 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](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index a1a2e74..90a8185 100644
--- 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](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index 6f43fad..972ec6e 100644
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -12,6 +12,7 @@
#include <JuceHeader.h>
#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<float> synthBuffer;
juce::dsp::Oscillator<float> osc { [](float x) { return x / juce::MathConstants<float>::pi; }};
diff –git a/Source/[UI](UI)/[AdsrComponent](AdsrComponent).cpp b/Source/[UI](UI)/[AdsrComponent](AdsrComponent).cpp
new file mode 100644
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 <JuceHeader.h>
+#include "AdsrComponent.h"
+
+//==============================================================================
+AdsrComponent::AdsrComponent (juce::AudioProcessorValueTreeState& apvts)
+{
+ using SliderAttachment = juce::AudioProcessorValueTreeState::SliderAttachment;
+
+ attackAttachment = std::make_unique<SliderAttachment>(apvts, "ATTACK", attackSlider);
+ decayAttachment = std::make_unique<SliderAttachment>(apvts, "DECAY", decaySlider);
+ sustainAttachment = std::make_unique<SliderAttachment>(apvts, "SUSTAIN", sustainSlider);
+ releaseAttachment = std::make_unique<SliderAttachment>(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](UI)/[AdsrComponent](AdsrComponent).h b/Source/[UI](UI)/[AdsrComponent](AdsrComponent).h
new file mode 100644
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 <JuceHeader.h>
+
+//==============================================================================
+/*
+*/
+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<SliderAttachment> attackAttachment;
+ std::unique_ptr<SliderAttachment> decayAttachment;
+ std::unique_ptr<SliderAttachment> sustainAttachment;
+ std::unique_ptr<SliderAttachment> releaseAttachment;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AdsrComponent)
+};
diff –git a/tapSynth.jucer b/tapSynth.jucer
index 8ea6bb5..a8f0f5c 100644
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -14,6 +14,15 @@
<FILE id="CrioMH" name="SynthVoice.cpp" compile="1" resource="0" file="Source/SynthVoice.cpp"/>
<FILE id="xUSl58" name="SynthVoice.h" compile="0" resource="0" file="Source/SynthVoice.h"/>
<FILE id="UardPm" name="SynthSound.h" compile="0" resource="0" file="Source/SynthSound.h"/>
+ <GROUP id="{F763CC91-BD2D-7AF9-546F-8878966BB954}" name="Data">
+ <FILE id="LoPzV0" name="AdsrData.cpp" compile="1" resource="0" file="Source/Data/AdsrData.cpp"/>
+ <FILE id="jhGYSk" name="AdsrData.h" compile="0" resource="0" file="Source/Data/AdsrData.h"/>
+ </GROUP>
+ <GROUP id="{06C8E4FF-1273-B489-1569-324B81AFEB5C}" name="UI">
+ <FILE id="Gh5xhC" name="AdsrComponent.cpp" compile="1" resource="0"
+ file="Source/UI/AdsrComponent.cpp"/>
+ <FILE id="xSj7wT" name="AdsrComponent.h" compile="0" resource="0" file="Source/UI/AdsrComponent.h"/>
+ </GROUP>
</GROUP>
</MAINGROUP>
<JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1" JUCE_VST3_CAN_REPLACE_VST2="0"/>
Diff 5: fed9a4238122b2ce7b3ddb5e05819c90a42ca965 to fa658b7c0faee65d2f4b191dd8089a5c602c6a40
diff –git a/Source/Data/[AdsrData](AdsrData).cpp b/Source/Data/[AdsrData](AdsrData).cpp
index d0ef3c4..0d89597 100644
--- 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](AdsrData).h b/Source/Data/[AdsrData](AdsrData).h
index e4b9605..d7087b8 100644
--- 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](OscData).cpp b/Source/Data/[OscData](OscData).cpp
new file mode 100644
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<float>::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<float>& audioBlock)
+{
+ jassert (audioBlock.getNumSamples() > 0);
+ process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
+ gain.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
+}
diff –git a/Source/Data/[OscData](OscData).h b/Source/Data/[OscData](OscData).h
new file mode 100644
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 <JuceHeader.h>
+
+class OscData : public juce::dsp::Oscillator<float>
+{
+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<float>& audioBlock);
+
+private:
+ juce::dsp::Gain<float> gain;
+ int pitchVal { 0 };
+ int lastMidiNote { 0 };
+};
+
+// return std::sin (x); //Sine Wave
+// return x / MathConstants<float>::pi; // Saw Wave
+// return x < 0.0f ? -1.0f : 1.0f; // Square Wave
diff –git a/Source/[PluginEditor](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index e4ba37c..07f155c 100644
--- 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(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](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index 9f78696..d789924 100644
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -10,6 +10,7 @@
#include <JuceHeader.h>
#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<juce::AudioProcessorValueTreeState::ComboBoxAttachment> oscSelAttachment;
TapSynthAudioProcessor& audioProcessor;
+ OscComponent osc1;
+ OscComponent osc2;
AdsrComponent adsr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TapSynthAudioProcessorEditor)
diff –git a/Source/[PluginProcessor](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index 9163db2..8297f48 100644
--- 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<float>& buffer, juc
{
if (auto voice = dynamic_cast<SynthVoice*>(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<std::unique_ptr<juce::RangedAudioParameter>> params;
// OSC select
- params.push_back (std::make_unique<juce::AudioParameterChoice> ("OSC", "Oscillator", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+ params.push_back (std::make_unique<juce::AudioParameterChoice> ("OSC1", "Oscillator 1", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+ params.push_back (std::make_unique<juce::AudioParameterChoice> ("OSC2", "Oscillator 2", juce::StringArray { "Sine", "Saw", "Square" }, 0));
+
+ // OSC Gain
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC1GAIN", "Oscillator 1 Gain", juce::NormalisableRange<float> { -40.0f, 0.2f, }, 0.1f, "dB"));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC2GAIN", "Oscillator 2 Gain", juce::NormalisableRange<float> { -40.0f, 0.2f, }, 0.1f, "dB"));
+
+ // OSC Pitch val
+ params.push_back (std::make_unique<juce::AudioParameterInt>("OSC1PITCH", "Oscillator 1 Pitch", -48, 48, 0));
+ params.push_back (std::make_unique<juce::AudioParameterInt>("OSC2PITCH", "Oscillator 2 Pitch", -48, 48, 0));
// ADSR
params.push_back (std::make_unique<juce::AudioParameterFloat>("ATTACK", "Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, }, 0.1f));
diff –git a/Source/[SynthVoice](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index 90a8185..7bbc7f7 100644
--- 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<float> audioBlock { synthBuffer };
- osc.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
+
+ osc1.renderNextBlock (audioBlock);
+ osc2.renderNextBlock (audioBlock);
+
gain.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
adsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples());
diff –git a/Source/[SynthVoice](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index 972ec6e..afd4876 100644
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -12,6 +12,7 @@
#include <JuceHeader.h>
#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<float> synthBuffer;
- juce::dsp::Oscillator<float> osc { [](float x) { return x / juce::MathConstants<float>::pi; }};
juce::dsp::Gain<float> gain;
bool isPrepared { false };
-
- // return std::sin (x); //Sine Wave
- // return x / MathConstants<float>::pi; // Saw Wave
- // return x < 0.0f ? -1.0f : 1.0f; // Square Wave
};
diff –git a/Source/[UI](UI)/[AdsrComponent](AdsrComponent).h b/Source/[UI](UI)/[AdsrComponent](AdsrComponent).h
index 703a366..03e5071 100644
--- 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](UI)/[OscComponent](OscComponent).cpp b/Source/[UI](UI)/[OscComponent](OscComponent).cpp
new file mode 100644
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 <JuceHeader.h>
+#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<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(apvts, oscId, oscSelector);
+
+ gainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(apvts, gainId, gainSlider);
+
+ pitchAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(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](UI)/[OscComponent](OscComponent).h b/Source/[UI](UI)/[OscComponent](OscComponent).h
new file mode 100644
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 <JuceHeader.h>
+
+//==============================================================================
+/*
+*/
+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<juce::AudioProcessorValueTreeState::ComboBoxAttachment> oscSelAttachment;
+ std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> gainAttachment;
+ std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> pitchAttachment;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OscComponent)
+};
diff –git a/tapSynth.jucer b/tapSynth.jucer
index a8f0f5c..6c2d6f1 100644
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -17,11 +17,16 @@
<GROUP id="{F763CC91-BD2D-7AF9-546F-8878966BB954}" name="Data">
<FILE id="LoPzV0" name="AdsrData.cpp" compile="1" resource="0" file="Source/Data/AdsrData.cpp"/>
<FILE id="jhGYSk" name="AdsrData.h" compile="0" resource="0" file="Source/Data/AdsrData.h"/>
+ <FILE id="WYgre1" name="OscData.cpp" compile="1" resource="0" file="Source/Data/OscData.cpp"/>
+ <FILE id="Taa7Z9" name="OscData.h" compile="0" resource="0" file="Source/Data/OscData.h"/>
</GROUP>
<GROUP id="{06C8E4FF-1273-B489-1569-324B81AFEB5C}" name="UI">
<FILE id="Gh5xhC" name="AdsrComponent.cpp" compile="1" resource="0"
file="Source/UI/AdsrComponent.cpp"/>
<FILE id="xSj7wT" name="AdsrComponent.h" compile="0" resource="0" file="Source/UI/AdsrComponent.h"/>
+ <FILE id="u3IlwZ" name="OscComponent.cpp" compile="1" resource="0"
+ file="Source/UI/OscComponent.cpp"/>
+ <FILE id="HofuLH" name="OscComponent.h" compile="0" resource="0" file="Source/UI/OscComponent.h"/>
</GROUP>
</GROUP>
</MAINGROUP>
Diff 6: fa658b7c0faee65d2f4b191dd8089a5c602c6a40 to 35915fe2d7e0e8ede1372eed6062820dcddb47f7
diff –git a/Source/Data/[OscData](OscData).cpp b/Source/Data/[OscData](OscData).cpp
index ad99bd3..6c41eb3 100644
--- 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<float>& audioBlock)
{
jassert (audioBlock.getNumSamples() > 0);
process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
gain.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
}
+
+float OscData::processNextSample (float input)
+{
+ fmModulator = fmOsc.processSample (input) * fmDepth;
+ return gain.processSample (processSample (input));
+}
diff –git a/Source/Data/[OscData](OscData).h b/Source/Data/[OscData](OscData).h
index 83a6c52..5380444 100644
--- 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<float>& audioBlock);
+ float processNextSample (float input);
private:
+ juce::dsp::Oscillator<float> fmOsc { [](float x) { return std::sin (x); }};
juce::dsp::Gain<float> 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](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index 07f155c..05181ab 100644
--- 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](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index 8297f48..31dcc48 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -166,19 +166,28 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer<float>& 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<juce::AudioParameterChoice> ("OSC2", "Oscillator 2", juce::StringArray { "Sine", "Saw", "Square" }, 0));
// OSC Gain
- params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC1GAIN", "Oscillator 1 Gain", juce::NormalisableRange<float> { -40.0f, 0.2f, }, 0.1f, "dB"));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC2GAIN", "Oscillator 2 Gain", juce::NormalisableRange<float> { -40.0f, 0.2f, }, 0.1f, "dB"));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC1GAIN", "Oscillator 1 Gain", juce::NormalisableRange<float> { -40.0f, 0.2f, 0.1f }, 0.1f, "dB"));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC2GAIN", "Oscillator 2 Gain", juce::NormalisableRange<float> { -40.0f, 0.2f, 0.1f }, 0.1f, "dB"));
// OSC Pitch val
params.push_back (std::make_unique<juce::AudioParameterInt>("OSC1PITCH", "Oscillator 1 Pitch", -48, 48, 0));
params.push_back (std::make_unique<juce::AudioParameterInt>("OSC2PITCH", "Oscillator 2 Pitch", -48, 48, 0));
+ // FM Osc Freq
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC1FMFREQ", "Oscillator 1 FM Frequency", juce::NormalisableRange<float> { 0.0f, 1000.0f, 0.1f }, 0.0f, "Hz"));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC2FMFREQ", "Oscillator 1 FM Frequency", juce::NormalisableRange<float> { 0.0f, 1000.0f, 0.1f }, 0.0f, "Hz"));
+
+ // FM Osc Depth
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC1FMDEPTH", "Oscillator 1 FM Depth", juce::NormalisableRange<float> { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC2FMDEPTH", "Oscillator 2 FM Depth", juce::NormalisableRange<float> { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+
// ADSR
- params.push_back (std::make_unique<juce::AudioParameterFloat>("ATTACK", "Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, }, 0.1f));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("DECAY", "Decay", juce::NormalisableRange<float> { 0.1f, 1.0f, }, 0.1f));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("SUSTAIN", "Sustain", juce::NormalisableRange<float> { 0.1f, 1.0f, }, 1.0f));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("RELEASE", "Release", juce::NormalisableRange<float> { 0.1f, 3.0f, }, 0.4f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("ATTACK", "Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("DECAY", "Decay", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("SUSTAIN", "Sustain", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 1.0f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("RELEASE", "Release", juce::NormalisableRange<float> { 0.1f, 3.0f, 0.1f }, 0.4f));
return { params.begin(), params.end() };
}
diff –git a/Source/[SynthVoice](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index 7bbc7f7..893ec30 100644
--- 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<float> audioBlock { synthBuffer };
- osc1.renderNextBlock (audioBlock);
- osc2.renderNextBlock (audioBlock);
-
gain.process (juce::dsp::ProcessContextReplacing<float> (audioBlock));
-
+
adsr.applyEnvelopeToBuffer (synthBuffer, 0, synthBuffer.getNumSamples());
for (int channel = 0; channel < outputBuffer.getNumChannels(); ++channel)
diff –git a/Source/[SynthVoice](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index afd4876..ff8924f 100644
--- 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<OscData, 2>& getOscillator1() { return osc1; }
+ std::array<OscData, 2>& getOscillator2() { return osc2; }
AdsrData& getAdsr() { return adsr; }
private:
- OscData osc1;
- OscData osc2;
+// OscData osc1;
+// OscData osc2;
+ std::array<OscData, 2> osc1;
+ std::array<OscData, 2> osc2;
AdsrData adsr;
juce::AudioBuffer<float> synthBuffer;
diff –git a/Source/[UI](UI)/[AdsrComponent](AdsrComponent).cpp b/Source/[UI](UI)/[AdsrComponent](AdsrComponent).cpp
index 3147550..e03f69a 100644
--- 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<SliderAttachment>(apvts, "ATTACK", attackSlider);
- decayAttachment = std::make_unique<SliderAttachment>(apvts, "DECAY", decaySlider);
- sustainAttachment = std::make_unique<SliderAttachment>(apvts, "SUSTAIN", sustainSlider);
- releaseAttachment = std::make_unique<SliderAttachment>(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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
}
diff –git a/Source/[UI](UI)/[AdsrComponent](AdsrComponent).h b/Source/[UI](UI)/[AdsrComponent](AdsrComponent).h
index 03e5071..82b41f1 100644
--- 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<SliderAttachment>& 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<SliderAttachment> attackAttachment;
std::unique_ptr<SliderAttachment> decayAttachment;
std::unique_ptr<SliderAttachment> sustainAttachment;
std::unique_ptr<SliderAttachment> 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](UI)/[OscComponent](OscComponent).cpp b/Source/[UI](UI)/[OscComponent](OscComponent).cpp
index ff59131..f5f3109 100644
--- 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(apvts, oscId, oscSelector);
- gainAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(apvts, gainId, gainSlider);
-
- pitchAttachment = std::make_unique<juce::AudioProcessorValueTreeState::SliderAttachment>(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<sliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
}
diff –git a/Source/[UI](UI)/[OscComponent](OscComponent).h b/Source/[UI](UI)/[OscComponent](OscComponent).h
index 1efbedf..c194ec3 100644
--- 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<sliderAttachment>&, 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment> oscSelAttachment;
- std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> gainAttachment;
- std::unique_ptr<juce::AudioProcessorValueTreeState::SliderAttachment> pitchAttachment;
+ std::unique_ptr<sliderAttachment> gainAttachment;
+ std::unique_ptr<sliderAttachment> pitchAttachment;
+ std::unique_ptr<sliderAttachment> fmFreqAttachment;
+ std::unique_ptr<sliderAttachment> 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](FilterData).cpp b/Source/Data/[FilterData](FilterData).cpp
new file mode 100644
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](FilterData).h b/Source/Data/[FilterData](FilterData).h
new file mode 100644
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 <JuceHeader.h>
+
+
+class FilterData : public juce::dsp::StateVariableTPTFilter<float>
+{
+public:
+ void setParams (const int filterType, const float filterCutoff, const float filterResonance);
+private:
+ void selectFilterType (const int type);
+};
diff –git a/Source/Data/[OscData](OscData).cpp b/Source/Data/[OscData](OscData).cpp
index 6c41eb3..a23d61d 100644
--- 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](OscData).h b/Source/Data/[OscData](OscData).h
index 5380444..48abba3 100644
--- 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<float>& 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<float> fmOsc { [](float x) { return std::sin (x); }};
diff –git a/Source/[PluginEditor](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index 05181ab..670af14 100644
--- 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](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index d789924..ba61702 100644
--- a/Source/PluginEditor.h
+++ b/Source/PluginEditor.h
@@ -11,6 +11,7 @@
#include <JuceHeader.h>
#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](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index 31dcc48..af7d686 100644
--- 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<float>& 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<SynthVoice*>(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<float> block { buffer };
+ filter.process (juce::dsp::ProcessContextReplacing<float>(block));
}
//==============================================================================
@@ -251,6 +223,12 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC1FMDEPTH", "Oscillator 1 FM Depth", juce::NormalisableRange<float> { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC2FMDEPTH", "Oscillator 2 FM Depth", juce::NormalisableRange<float> { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+ //Filter
+ params.push_back (std::make_unique<juce::AudioParameterChoice>("FILTERTYPE", "Filter Type", juce::StringArray { "Low Pass", "Band Pass", "High Pass" }, 0));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERCUTOFF", "Filter Cutoff", juce::NormalisableRange<float> { 20.0f, 20000.0f, 0.1f, 0.6f }, 20000.0f, "Hz"));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERRESONANCE", "Filter Resonance", juce::NormalisableRange<float> { 0.1f, 2.0f, 0.1f }, 0.1f, ""));
+
+
// ADSR
params.push_back (std::make_unique<juce::AudioParameterFloat>("ATTACK", "Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("DECAY", "Decay", juce::NormalisableRange<float> { 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<SynthVoice*>(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](PluginProcessor).h b/Source/[PluginProcessor](PluginProcessor).h
index 68c34b4..f31821c 100644
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -11,6 +11,7 @@
#include <JuceHeader.h>
#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](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index ff8924f..17c3850 100644
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -31,8 +31,6 @@ public:
AdsrData& getAdsr() { return adsr; }
private:
-// OscData osc1;
-// OscData osc2;
std::array<OscData, 2> osc1;
std::array<OscData, 2> osc2;
AdsrData adsr;
diff –git a/Source/[UI](UI)/[FilterComponent](FilterComponent).cpp b/Source/[UI](UI)/[FilterComponent](FilterComponent).cpp
new file mode 100644
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 <JuceHeader.h>
+#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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
+}
diff –git a/Source/[UI](UI)/[FilterComponent](FilterComponent).h b/Source/[UI](UI)/[FilterComponent](FilterComponent).h
new file mode 100644
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 <JuceHeader.h>
+
+//==============================================================================
+/*
+*/
+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<SliderAttachment>& 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment> filterTypeAttachment;
+ std::unique_ptr<SliderAttachment> cutoffAttachment;
+ std::unique_ptr<SliderAttachment> 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
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -17,6 +17,8 @@
<GROUP id="{F763CC91-BD2D-7AF9-546F-8878966BB954}" name="Data">
<FILE id="LoPzV0" name="AdsrData.cpp" compile="1" resource="0" file="Source/Data/AdsrData.cpp"/>
<FILE id="jhGYSk" name="AdsrData.h" compile="0" resource="0" file="Source/Data/AdsrData.h"/>
+ <FILE id="aAQb1V" name="FilterData.cpp" compile="1" resource="0" file="Source/Data/FilterData.cpp"/>
+ <FILE id="Htzwpx" name="FilterData.h" compile="0" resource="0" file="Source/Data/FilterData.h"/>
<FILE id="WYgre1" name="OscData.cpp" compile="1" resource="0" file="Source/Data/OscData.cpp"/>
<FILE id="Taa7Z9" name="OscData.h" compile="0" resource="0" file="Source/Data/OscData.h"/>
</GROUP>
@@ -24,6 +26,10 @@
<FILE id="Gh5xhC" name="AdsrComponent.cpp" compile="1" resource="0"
file="Source/UI/AdsrComponent.cpp"/>
<FILE id="xSj7wT" name="AdsrComponent.h" compile="0" resource="0" file="Source/UI/AdsrComponent.h"/>
+ <FILE id="E50Kbx" name="FilterComponent.cpp" compile="1" resource="0"
+ file="Source/UI/FilterComponent.cpp"/>
+ <FILE id="g6fDCL" name="FilterComponent.h" compile="0" resource="0"
+ file="Source/UI/FilterComponent.h"/>
<FILE id="u3IlwZ" name="OscComponent.cpp" compile="1" resource="0"
file="Source/UI/OscComponent.cpp"/>
<FILE id="HofuLH" name="OscComponent.h" compile="0" resource="0" file="Source/UI/OscComponent.h"/>
Diff 8: 8d9a9945d63503d9d6864fb59aed85b8f31c3541 to 6d1eb19d65eaf8e29bfe7217c2d9d00173303027
diff –git a/Source/Data/[FilterData](FilterData).cpp b/Source/Data/[FilterData](FilterData).cpp
index 28b0961..1c31eba 100644
--- 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<float>& buffer)
+{
+ juce::dsp::AudioBlock<float> block { buffer };
+ process (juce::dsp::ProcessContextReplacing<float>(block));
+}
+
+float FilterData::processNextSample (int channel, float inputValue)
+{
+ return processSample (channel, inputValue);
+}
diff –git a/Source/Data/[FilterData](FilterData).h b/Source/Data/[FilterData](FilterData).h
index b51e220..0733654 100644
--- a/Source/Data/FilterData.h
+++ b/Source/Data/FilterData.h
@@ -10,13 +10,19 @@
#pragma once
+#include "OscData.h"
#include <JuceHeader.h>
class FilterData : public juce::dsp::StateVariableTPTFilter<float>
{
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<float>& buffer);
+ float processNextSample (int channel, float inputValue);
private:
void selectFilterType (const int type);
+ OscData lfo;
};
diff –git a/Source/Data/[OscData](OscData).cpp b/Source/Data/[OscData](OscData).cpp
index a23d61d..b99f860 100644
--- 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](OscData).h b/Source/Data/[OscData](OscData).h
index 48abba3..91c829b 100644
--- 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<float> fmOsc { [](float x) { return std::sin (x); }};
juce::dsp::Gain<float> gain;
diff –git a/Source/[PluginProcessor](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index af7d686..d280c94 100644
--- 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<float>& buffer, juc
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
- juce::dsp::AudioBlock<float> block { buffer };
- filter.process (juce::dsp::ProcessContextReplacing<float>(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<juce::AudioParameterFloat>("OSC1FMDEPTH", "Oscillator 1 FM Depth", juce::NormalisableRange<float> { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
params.push_back (std::make_unique<juce::AudioParameterFloat>("OSC2FMDEPTH", "Oscillator 2 FM Depth", juce::NormalisableRange<float> { 0.0f, 100.0f, 0.1f }, 0.0f, ""));
+ // LFO
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("LFO1FREQ", "LFO1 Frequency", juce::NormalisableRange<float> { 0.0f, 20.0f, 0.1f }, 0.0f, "Hz"));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("LFO1DEPTH", "LFO1 Depth", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+
//Filter
params.push_back (std::make_unique<juce::AudioParameterChoice>("FILTERTYPE", "Filter Type", juce::StringArray { "Low Pass", "Band Pass", "High Pass" }, 0));
params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERCUTOFF", "Filter Cutoff", juce::NormalisableRange<float> { 20.0f, 20000.0f, 0.1f, 0.6f }, 20000.0f, "Hz"));
params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERRESONANCE", "Filter Resonance", juce::NormalisableRange<float> { 0.1f, 2.0f, 0.1f }, 0.1f, ""));
-
// ADSR
params.push_back (std::make_unique<juce::AudioParameterFloat>("ATTACK", "Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("DECAY", "Decay", juce::NormalisableRange<float> { 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](PluginProcessor).h b/Source/[PluginProcessor](PluginProcessor).h
index f31821c..b8bd1b5 100644
--- 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<FilterData, numChannelsToProcess> 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](UI)/[FilterComponent](FilterComponent).cpp b/Source/[UI](UI)/[FilterComponent](FilterComponent).cpp
index d4fa1ca..e336eb3 100644
--- 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(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](FilterData).cpp b/Source/Data/[FilterData](FilterData).cpp
index 1c31eba..646b49c 100644
--- 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](FilterData).h b/Source/Data/[FilterData](FilterData).h
index 0733654..cecaede 100644
--- 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<float>& buffer);
float processNextSample (int channel, float inputValue);
+
private:
void selectFilterType (const int type);
- OscData lfo;
+ juce::dsp::Oscillator<float> lfo { [](float x) { return std::sin (x); }};
};
diff –git a/Source/Data/[OscData](OscData).cpp b/Source/Data/[OscData](OscData).cpp
index b99f860..89fba49 100644
--- 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](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index 670af14..e0ebd83 100644
--- 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](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index ba61702..08db2b9 100644
--- 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](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index d280c94..4ddca45 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -8,6 +8,7 @@
#include "PluginProcessor.h"
#include "PluginEditor.h"
+#include <algorithm>
//==============================================================================
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<float>& 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<float>& 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<float> block { buffer };
+ reverb.process (juce::dsp::ProcessContextReplacing<float> (block));
}
//==============================================================================
@@ -233,7 +254,7 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
// LFO
params.push_back (std::make_unique<juce::AudioParameterFloat>("LFO1FREQ", "LFO1 Frequency", juce::NormalisableRange<float> { 0.0f, 20.0f, 0.1f }, 0.0f, "Hz"));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("LFO1DEPTH", "LFO1 Depth", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("LFO1DEPTH", "LFO1 Depth", juce::NormalisableRange<float> { 0.0f, 10000.0f, 0.1f, 0.3f }, 0.0f, ""));
//Filter
params.push_back (std::make_unique<juce::AudioParameterChoice>("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<juce::AudioParameterFloat>("SUSTAIN", "Sustain", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 1.0f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("RELEASE", "Release", juce::NormalisableRange<float> { 0.1f, 3.0f, 0.1f }, 0.4f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERADSRDEPTH", "Filter ADSR Depth", juce::NormalisableRange<float> { 0.0f, 10000.0f, 0.1f, 0.3f }, 10000.0f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERATTACK", "Filter Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERDECAY", "Filter Decay", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERSUSTAIN", "Filter Sustain", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 1.0f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERRELEASE", "Filter Release", juce::NormalisableRange<float> { 0.1f, 3.0f, 0.1f }, 0.4f));
+
+ // Reverb
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("REVERBSIZE", "Reverb Size", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("REVERBWIDTH", "Reverb Width", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 1.0f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("REVERBDAMPING", "Reverb Damping", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 0.5f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("REVERBDRY", "Reverb Dry", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 1.0f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("REVERBWET", "Reverb Wet", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 0.0f, ""));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("REVERBFREEZE", "Reverb Freeze", juce::NormalisableRange<float> { 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<SynthVoice*>(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<float> (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](PluginProcessor).h b/Source/[PluginProcessor](PluginProcessor).h
index b8bd1b5..4b5af6b 100644
--- 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<FilterData, numChannelsToProcess> filter;
+
juce::AudioProcessorValueTreeState::ParameterLayout createParams();
void setParams();
void setVoiceParams();
void setFilterParams();
+ void setReverbParams();
+
static constexpr int numVoices { 5 };
+ std::array<juce::dsp::Oscillator<float>, numChannelsToProcess> lfo;
+ std::array<float, numChannelsToProcess> 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](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index 893ec30..50951da 100644
--- 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](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index 17c3850..26e8963 100644
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -29,12 +29,16 @@ public:
std::array<OscData, 2>& getOscillator1() { return osc1; }
std::array<OscData, 2>& getOscillator2() { return osc2; }
AdsrData& getAdsr() { return adsr; }
+ AdsrData& getFilterAdsr() { return filterAdsr; }
+ float getFilterAdsrOutput() { return filterAdsrOutput; }
private:
std::array<OscData, 2> osc1;
std::array<OscData, 2> osc2;
AdsrData adsr;
+ AdsrData filterAdsr;
juce::AudioBuffer<float> synthBuffer;
+ float filterAdsrOutput { 0.0f };
juce::dsp::Gain<float> gain;
bool isPrepared { false };
diff –git a/Source/[UI](UI)/[LfoComponent](LfoComponent).cpp b/Source/[UI](UI)/[LfoComponent](LfoComponent).cpp
new file mode 100644
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 <JuceHeader.h>
+#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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
+}
diff –git a/Source/[UI](UI)/[LfoComponent](LfoComponent).h b/Source/[UI](UI)/[LfoComponent](LfoComponent).h
new file mode 100644
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 <JuceHeader.h>
+
+//==============================================================================
+/*
+*/
+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<SliderAttachment>& 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<SliderAttachment> lfoFreqAttachment;
+ std::unique_ptr<SliderAttachment> 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](UI)/[ReverbComponent](ReverbComponent).cpp b/Source/[UI](UI)/[ReverbComponent](ReverbComponent).cpp
new file mode 100644
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 <JuceHeader.h>
+#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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
+}
diff –git a/Source/[UI](UI)/[ReverbComponent](ReverbComponent).h b/Source/[UI](UI)/[ReverbComponent](ReverbComponent).h
new file mode 100644
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 <JuceHeader.h>
+
+//==============================================================================
+/*
+*/
+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<SliderAttachment>& 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<SliderAttachment> sizeAttachment;
+ std::unique_ptr<SliderAttachment> dampingAttachment;
+ std::unique_ptr<SliderAttachment> widthAttachment;
+ std::unique_ptr<SliderAttachment> wetAttachment;
+ std::unique_ptr<SliderAttachment> dryAttachment;
+ std::unique_ptr<SliderAttachment> 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
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<JUCERPROJECT id="cNKSGD" name="tapSynth" projectType="audioplug" useAppConfig="0"
- addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn">
+ addUsingNamespaceToJuceHeader="0" jucerFormatVersion="1" pluginCharacteristicsValue="pluginIsSynth,pluginWantsMidiIn"
+ cppLanguageStandard="17">
<MAINGROUP id="KMptmw" name="tapSynth">
<GROUP id="{3C4B8AF0-CC1B-B776-74F0-C1D0B8E63F93}" name="Source">
<FILE id="cCsQG0" name="PluginProcessor.cpp" compile="1" resource="0"
@@ -30,9 +31,16 @@
file="Source/UI/FilterComponent.cpp"/>
<FILE id="g6fDCL" name="FilterComponent.h" compile="0" resource="0"
file="Source/UI/FilterComponent.h"/>
+ <FILE id="qBZMwg" name="LfoComponent.cpp" compile="1" resource="0"
+ file="Source/UI/LfoComponent.cpp"/>
+ <FILE id="wCLCyX" name="LfoComponent.h" compile="0" resource="0" file="Source/UI/LfoComponent.h"/>
<FILE id="u3IlwZ" name="OscComponent.cpp" compile="1" resource="0"
file="Source/UI/OscComponent.cpp"/>
<FILE id="HofuLH" name="OscComponent.h" compile="0" resource="0" file="Source/UI/OscComponent.h"/>
+ <FILE id="Y1tPbu" name="ReverbComponent.cpp" compile="1" resource="0"
+ file="Source/UI/ReverbComponent.cpp"/>
+ <FILE id="Q2fbNB" name="ReverbComponent.h" compile="0" resource="0"
+ file="Source/UI/ReverbComponent.h"/>
</GROUP>
</GROUP>
</MAINGROUP>
Diff 10: 5e9bdd2f6d524047bc80c3c4a01854ecb5a859fd to 50b31071e6888577f0da4dddf1c4c2ede4cce0ea
diff –git a/Source/[PluginEditor](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index e0ebd83..b162f54 100644
--- 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](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index 4ddca45..ab7f055 100644
--- 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<float> (filterCutoff, 20.0f, 20000.0f);
filter[ch].setParams (filterType, cutoff, filterResonance);
diff –git a/Source/[UI](UI)/[AdsrComponent](AdsrComponent).cpp b/Source/[UI](UI)/[AdsrComponent](AdsrComponent).cpp
index e03f69a..d7a7292 100644
--- 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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
-}
diff –git a/Source/[UI](UI)/[AdsrComponent](AdsrComponent).h b/Source/[UI](UI)/[AdsrComponent](AdsrComponent).h
index 82b41f1..e972947 100644
--- a/Source/UI/AdsrComponent.h
+++ b/Source/UI/AdsrComponent.h
@@ -11,42 +11,27 @@
#pragma once
#include <JuceHeader.h>
+#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<SliderAttachment>& 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<SliderAttachment> attackAttachment;
- std::unique_ptr<SliderAttachment> decayAttachment;
- std::unique_ptr<SliderAttachment> sustainAttachment;
- std::unique_ptr<SliderAttachment> 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](UI)/[CustomComponent](CustomComponent).cpp b/Source/[UI](UI)/[CustomComponent](CustomComponent).cpp
new file mode 100644
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 <JuceHeader.h>
+#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<SliderAttachment>(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](UI)/[CustomComponent](CustomComponent).h b/Source/[UI](UI)/[CustomComponent](CustomComponent).h
new file mode 100644
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 <JuceHeader.h>
+
+//==============================================================================
+/*
+*/
+
+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<SliderAttachment> 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](UI)/[FilterComponent](FilterComponent).cpp b/Source/[UI](UI)/[FilterComponent](FilterComponent).cpp
index e336eb3..805119b 100644
--- 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
-}
diff –git a/Source/[UI](UI)/[FilterComponent](FilterComponent).h b/Source/[UI](UI)/[FilterComponent](FilterComponent).h
index aced363..98ce759 100644
--- a/Source/UI/FilterComponent.h
+++ b/Source/UI/FilterComponent.h
@@ -11,41 +11,28 @@
#pragma once
#include <JuceHeader.h>
+#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<SliderAttachment>& 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment> filterTypeAttachment;
- std::unique_ptr<SliderAttachment> cutoffAttachment;
- std::unique_ptr<SliderAttachment> 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](UI)/[LfoComponent](LfoComponent).cpp b/Source/[UI](UI)/[LfoComponent](LfoComponent).cpp
index 34a4cd9..4882a85 100644
--- 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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
-}
diff –git a/Source/[UI](UI)/[LfoComponent](LfoComponent).h b/Source/[UI](UI)/[LfoComponent](LfoComponent).h
index eea85b9..9e1ab78 100644
--- a/Source/UI/LfoComponent.h
+++ b/Source/UI/LfoComponent.h
@@ -11,17 +11,17 @@
#pragma once
#include <JuceHeader.h>
+#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<SliderAttachment> lfoFreqAttachment;
std::unique_ptr<SliderAttachment> 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](UI)/[OscComponent](OscComponent).cpp b/Source/[UI](UI)/[OscComponent](OscComponent).cpp
index f5f3109..e259170 100644
--- 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment>(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<sliderAttachment>& 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<SliderAttachment>(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](UI)/[OscComponent](OscComponent).h b/Source/[UI](UI)/[OscComponent](OscComponent).h
index c194ec3..410a7d0 100644
--- a/Source/UI/OscComponent.h
+++ b/Source/UI/OscComponent.h
@@ -11,48 +11,30 @@
#pragma once
#include <JuceHeader.h>
+#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<sliderAttachment>&, 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<juce::AudioProcessorValueTreeState::ComboBoxAttachment> oscSelAttachment;
- std::unique_ptr<sliderAttachment> gainAttachment;
- std::unique_ptr<sliderAttachment> pitchAttachment;
- std::unique_ptr<sliderAttachment> fmFreqAttachment;
- std::unique_ptr<sliderAttachment> 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](UI)/[ReverbComponent](ReverbComponent).cpp b/Source/[UI](UI)/[ReverbComponent](ReverbComponent).cpp
index c8783b6..85c407f 100644
--- 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<SliderAttachment>& 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<SliderAttachment>(apvts, paramId, slider);
-}
diff –git a/Source/[UI](UI)/[ReverbComponent](ReverbComponent).h b/Source/[UI](UI)/[ReverbComponent](ReverbComponent).h
index 7f8c068..1597e22 100644
--- a/Source/UI/ReverbComponent.h
+++ b/Source/UI/ReverbComponent.h
@@ -11,48 +11,29 @@
#pragma once
#include <JuceHeader.h>
+#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<SliderAttachment>& 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<SliderAttachment> sizeAttachment;
- std::unique_ptr<SliderAttachment> dampingAttachment;
- std::unique_ptr<SliderAttachment> widthAttachment;
- std::unique_ptr<SliderAttachment> wetAttachment;
- std::unique_ptr<SliderAttachment> dryAttachment;
- std::unique_ptr<SliderAttachment> 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
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -27,6 +27,10 @@
<FILE id="Gh5xhC" name="AdsrComponent.cpp" compile="1" resource="0"
file="Source/UI/AdsrComponent.cpp"/>
<FILE id="xSj7wT" name="AdsrComponent.h" compile="0" resource="0" file="Source/UI/AdsrComponent.h"/>
+ <FILE id="EycF9K" name="CustomComponent.cpp" compile="1" resource="0"
+ file="Source/UI/CustomComponent.cpp"/>
+ <FILE id="NqPkIn" name="CustomComponent.h" compile="0" resource="0"
+ file="Source/UI/CustomComponent.h"/>
<FILE id="E50Kbx" name="FilterComponent.cpp" compile="1" resource="0"
file="Source/UI/FilterComponent.cpp"/>
<FILE id="g6fDCL" name="FilterComponent.h" compile="0" resource="0"
Diff 11: 50b31071e6888577f0da4dddf1c4c2ede4cce0ea to b4816f92f13bccd46db0111403eaa01d74f3041b
diff –git a/Source/Data/[FilterData](FilterData).cpp b/Source/Data/[FilterData](FilterData).cpp
index 646b49c..815d85b 100644
--- 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](FilterData).h b/Source/Data/[FilterData](FilterData).h
index cecaede..67d573c 100644
--- a/Source/Data/FilterData.h
+++ b/Source/Data/FilterData.h
@@ -17,11 +17,13 @@
class FilterData : public juce::dsp::StateVariableTPTFilter<float>
{
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<float>& buffer);
float processNextSample (int channel, float inputValue);
+ void resetAll();
private:
void selectFilterType (const int type);
diff –git a/Source/Data/[OscData](OscData).cpp b/Source/Data/[OscData](OscData).cpp
index 89fba49..57b5f17 100644
--- 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](OscData).h b/Source/Data/[OscData](OscData).h
index 91c829b..32fae2b 100644
--- a/Source/Data/OscData.h
+++ b/Source/Data/OscData.h
@@ -24,6 +24,7 @@ public:
void renderNextBlock (juce::dsp::AudioBlock<float>& 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<float> fmOsc { [](float x) { return std::sin (x); }};
diff –git a/Source/[PluginProcessor](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index ab7f055..c7fe9c7 100644
--- 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<float>& 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<float> block { buffer };
reverb.process (juce::dsp::ProcessContextReplacing<float> (block));
}
@@ -268,10 +244,10 @@ juce::AudioProcessorValueTreeState::ParameterLayout TapSynthAudioProcessor::crea
params.push_back (std::make_unique<juce::AudioParameterFloat>("RELEASE", "Release", juce::NormalisableRange<float> { 0.1f, 3.0f, 0.1f }, 0.4f));
params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERADSRDEPTH", "Filter ADSR Depth", juce::NormalisableRange<float> { 0.0f, 10000.0f, 0.1f, 0.3f }, 10000.0f, ""));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERATTACK", "Filter Attack", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERDECAY", "Filter Decay", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 0.1f));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERSUSTAIN", "Filter Sustain", juce::NormalisableRange<float> { 0.1f, 1.0f, 0.1f }, 1.0f));
- params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERRELEASE", "Filter Release", juce::NormalisableRange<float> { 0.1f, 3.0f, 0.1f }, 0.4f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERATTACK", "Filter Attack", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.01f }, 0.01f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERDECAY", "Filter Decay", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 0.1f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERSUSTAIN", "Filter Sustain", juce::NormalisableRange<float> { 0.0f, 1.0f, 0.1f }, 1.0f));
+ params.push_back (std::make_unique<juce::AudioParameterFloat>("FILTERRELEASE", "Filter Release", juce::NormalisableRange<float> { 0.0f, 3.0f, 0.1f }, 0.1f));
// Reverb
params.push_back (std::make_unique<juce::AudioParameterFloat>("REVERBSIZE", "Reverb Size", juce::NormalisableRange<float> { 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<SynthVoice*>(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<float> (filterCutoff, 20.0f, 20000.0f);
-
- filter[ch].setParams (filterType, cutoff, filterResonance);
+ if (auto voice = dynamic_cast<SynthVoice*>(synth.getVoice(i)))
+ {
+ voice->updateModParams (filterType, filterCutoff, filterResonance, adsrDepth, lfoFreq, lfoDepth);
+ }
}
}
diff –git a/Source/[PluginProcessor](PluginProcessor).h b/Source/[PluginProcessor](PluginProcessor).h
index 4b5af6b..12372da 100644
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -11,8 +11,6 @@
#include <JuceHeader.h>
#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<FilterData, numChannelsToProcess> filter;
juce::AudioProcessorValueTreeState::ParameterLayout createParams();
void setParams();
@@ -71,8 +68,6 @@ private:
void setReverbParams();
static constexpr int numVoices { 5 };
- std::array<juce::dsp::Oscillator<float>, numChannelsToProcess> lfo;
- std::array<float, numChannelsToProcess> lfoOutput { 0.0f, 0.0f };
juce::dsp::Reverb reverb;
juce::Reverb::Parameters reverbParams;
diff –git a/Source/[SynthVoice](SynthVoice).cpp b/Source/[SynthVoice](SynthVoice).cpp
index 50951da..d263045 100644
--- a/Source/SynthVoice.cpp
+++ b/Source/SynthVoice.cpp
@@ -10,7 +10,6 @@
#include "SynthVoice.h"
-
bool SynthVoice::canPlaySound (juce::SynthesiserSound* sound)
{
return dynamic_cast<juce::SynthesiserSound*>(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<float> audioBlock { synthBuffer };
-
gain.process (juce::dsp::ProcessContextReplacing<float> (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<float> (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<float> (cutoff, 20.0f, 20000.0f);
+// filter[ch].setParams (filterType, cutoff, filterResonance);
+// }
+}
diff –git a/Source/[SynthVoice](SynthVoice).h b/Source/[SynthVoice](SynthVoice).h
index 26e8963..d947a99 100644
--- a/Source/SynthVoice.h
+++ b/Source/SynthVoice.h
@@ -13,6 +13,7 @@
#include <JuceHeader.h>
#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<OscData, 2>& getOscillator1() { return osc1; }
std::array<OscData, 2>& 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<OscData, 2> osc1;
- std::array<OscData, 2> osc2;
+ static constexpr int numChannelsToProcess { 2 };
+ std::array<OscData, numChannelsToProcess> osc1;
+ std::array<OscData, numChannelsToProcess> osc2;
+ std::array<FilterData, numChannelsToProcess> filter;
+ std::array<juce::dsp::Oscillator<float>, numChannelsToProcess> lfo;
AdsrData adsr;
AdsrData filterAdsr;
juce::AudioBuffer<float> synthBuffer;
float filterAdsrOutput { 0.0f };
+ std::array<float, numChannelsToProcess> lfoOutput { 0.0f, 0.0f };
+
juce::dsp::Gain<float> gain;
bool isPrepared { false };
Diff 12: b4816f92f13bccd46db0111403eaa01d74f3041b to e8bccebc9afa79aa9ee138b2469365e480025819
diff –git a/Assets/tapLogo.png b/Assets/tapLogo.png
new file mode 100644
index 0000000..6236100
Binary files /dev/null and b/Assets/tapLogo.png differ
diff –git a/Source/[PluginEditor](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index b162f54..263cad3 100644
--- 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](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index 08db2b9..07f853a 100644
--- 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](UI)/Assets.cpp b/Source/[UI](UI)/Assets.cpp
new file mode 100644
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](UI)/Assets.h b/Source/[UI](UI)/Assets.h
new file mode 100644
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](UI)/[MeterComponent](MeterComponent).cpp b/Source/[UI](UI)/[MeterComponent](MeterComponent).cpp
new file mode 100644
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 <JuceHeader.h>
+#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](UI)/[MeterComponent](MeterComponent).h b/Source/[UI](UI)/[MeterComponent](MeterComponent).h
new file mode 100644
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 <JuceHeader.h>
+#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
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -27,6 +27,8 @@
<FILE id="Gh5xhC" name="AdsrComponent.cpp" compile="1" resource="0"
file="Source/UI/AdsrComponent.cpp"/>
<FILE id="xSj7wT" name="AdsrComponent.h" compile="0" resource="0" file="Source/UI/AdsrComponent.h"/>
+ <FILE id="hSvdy6" name="Assets.cpp" compile="1" resource="0" file="Source/UI/Assets.cpp"/>
+ <FILE id="bgmm8T" name="Assets.h" compile="0" resource="0" file="Source/UI/Assets.h"/>
<FILE id="EycF9K" name="CustomComponent.cpp" compile="1" resource="0"
file="Source/UI/CustomComponent.cpp"/>
<FILE id="NqPkIn" name="CustomComponent.h" compile="0" resource="0"
@@ -45,8 +47,15 @@
file="Source/UI/ReverbComponent.cpp"/>
<FILE id="Q2fbNB" name="ReverbComponent.h" compile="0" resource="0"
file="Source/UI/ReverbComponent.h"/>
+ <FILE id="Sma7Kz" name="MeterComponent.cpp" compile="1" resource="0"
+ file="Source/UI/MeterComponent.cpp"/>
+ <FILE id="RVA90e" name="MeterComponent.h" compile="0" resource="0"
+ file="Source/UI/MeterComponent.h"/>
</GROUP>
</GROUP>
+ <GROUP id="{2079F4D1-B478-97B8-2F1E-3BC34F4CF5C7}" name="Assets">
+ <FILE id="YGqe4N" name="tapLogo.png" compile="0" resource="1" file="Assets/tapLogo.png"/>
+ </GROUP>
</MAINGROUP>
<JUCEOPTIONS JUCE_STRICT_REFCOUNTEDPOINTER="1" JUCE_VST3_CAN_REPLACE_VST2="0"/>
<EXPORTFORMATS>
Diff 13: e8bccebc9afa79aa9ee138b2469365e480025819 to cc201efd2b1c93bd623b95d356f82fabe151cdf5
diff –git a/Source/Data/[MeterData](MeterData).cpp b/Source/Data/[MeterData](MeterData).cpp
new file mode 100644
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<float>& 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<float>& 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](MeterData).h b/Source/Data/[MeterData](MeterData).h
new file mode 100644
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 <JuceHeader.h>
+
+class MeterData
+{
+public:
+ void processRMS (juce::AudioBuffer<float>& buffer);
+ void processPeak (juce::AudioBuffer<float>& buffer);
+
+ const std::atomic<float>& getRMS() { return rms; }
+ const std::atomic<float>& getPeak() { return peak; }
+
+private:
+ std::atomic<float> rms { 0.0f };
+ std::atomic<float> peak { 0.0f };
+};
diff –git a/Source/[PluginEditor](PluginEditor).cpp b/Source/[PluginEditor](PluginEditor).cpp
index 263cad3..157b498 100644
--- 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](PluginEditor).h b/Source/[PluginEditor](PluginEditor).h
index 07f853a..176e996 100644
--- 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](PluginProcessor).cpp b/Source/[PluginProcessor](PluginProcessor).cpp
index c7fe9c7..1f06cb8 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -170,6 +170,9 @@ void TapSynthAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juc
synth.renderNextBlock (buffer, midiMessages, 0, buffer.getNumSamples());
juce::dsp::AudioBlock<float> block { buffer };
reverb.process (juce::dsp::ProcessContextReplacing<float> (block));
+
+ meter.processRMS (buffer);
+ meter.processPeak (buffer);
}
//==============================================================================
diff –git a/Source/[PluginProcessor](PluginProcessor).h b/Source/[PluginProcessor](PluginProcessor).h
index 12372da..a19641b 100644
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -11,6 +11,7 @@
#include <JuceHeader.h>
#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<float>& getRMS() { return meter.getRMS(); }
+ const std::atomic<float>& 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](UI)/[MeterComponent](MeterComponent).cpp b/Source/[UI](UI)/[MeterComponent](MeterComponent).cpp
index cf40027..c31d0d2 100644
--- 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<float>& rms = audioProcessor.getRMS();
+ auto rmsLevel = juce::jmap<float> (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<float>& peak = audioProcessor.getPeak();
+ auto peakLevel = juce::jmap<float> (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](UI)/[MeterComponent](MeterComponent).h b/Source/[UI](UI)/[MeterComponent](MeterComponent).h
index e8fd172..95acd3b 100644
--- a/Source/UI/MeterComponent.h
+++ b/Source/UI/MeterComponent.h
@@ -11,6 +11,7 @@
#pragma once
#include <JuceHeader.h>
+#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
--- a/tapSynth.jucer
+++ b/tapSynth.jucer
@@ -20,6 +20,8 @@
<FILE id="jhGYSk" name="AdsrData.h" compile="0" resource="0" file="Source/Data/AdsrData.h"/>
<FILE id="aAQb1V" name="FilterData.cpp" compile="1" resource="0" file="Source/Data/FilterData.cpp"/>
<FILE id="Htzwpx" name="FilterData.h" compile="0" resource="0" file="Source/Data/FilterData.h"/>
+ <FILE id="wSXBfT" name="MeterData.cpp" compile="1" resource="0" file="Source/Data/MeterData.cpp"/>
+ <FILE id="AdziIs" name="MeterData.h" compile="0" resource="0" file="Source/Data/MeterData.h"/>
<FILE id="WYgre1" name="OscData.cpp" compile="1" resource="0" file="Source/Data/OscData.cpp"/>
<FILE id="Taa7Z9" name="OscData.h" compile="0" resource="0" file="Source/Data/OscData.h"/>
</GROUP>