diff --git a/Magical8bitPlug2.jucer b/Magical8bitPlug2.jucer index a8b4e52..7703122 100644 --- a/Magical8bitPlug2.jucer +++ b/Magical8bitPlug2.jucer @@ -33,6 +33,10 @@ file="Source/SliderComponent.cpp"/> + + @@ -68,6 +72,13 @@ file="Source/VibratoParamsComponent.h"/> + + + + noiseAlgorithm()) { case kNoiseInfinite2: cycleLength = MathConstants::pi / 25.0; @@ -29,10 +31,19 @@ void NoiseVoice::startNote(int midiNoteNumber, float velocity, SynthesiserSound cycleLength = MathConstants::pi / 8.0; break; + case kNoiseLongNes: + case kNoiseShortNes: + cycleLength = MathConstants::pi / 20.175; + //rgstr = 0x8000; + //rgstr = rand(); + midiNote = midiNoteRange[(midiNote + 8) % 16]; + break; + default: cycleLength = MathConstants::pi / 8.0; break; } + TonalVoice::startNote(midiNote, velocity, 0, currentPitchBendPosition); } float NoiseVoice::voltageForAngle (double angle) @@ -58,9 +69,10 @@ float NoiseVoice::voltageForAngle (double angle) { if (settingRefs->noiseAlgorithm() == kNoiseInfinite2) { - currentVoltage = float (rand() % 16 - 8) / 16.; + currentVoltage = float (rand() % 16 - 8) / 16.0; + //currentVoltage = float(rand() % 16 - 7.5) / 15.0; } - else + else if (settingRefs->noiseAlgorithm() == kNoiseLong || settingRefs->noiseAlgorithm() == kNoiseShort) { int compareBitPos = settingRefs->noiseAlgorithm() == kNoiseLong ? 1 : 6; @@ -76,6 +88,14 @@ float NoiseVoice::voltageForAngle (double angle) currentVoltage = (float)bit0 - 0.5; } + else + { + int shortFreq = settingRefs->noiseAlgorithm() == kNoiseLongNes ? 1 : 6; + + rgstr >>= 1; + rgstr |= ((rgstr ^ (rgstr >> shortFreq)) & 1) << 15; + currentVoltage = (float)(rgstr & 1) - 0.5; + } nextAngle = (double) ((int) (angle / cycleLength) + 1) * cycleLength; diff --git a/Source/PluginEditor.cpp b/Source/PluginEditor.cpp index d165238..bec901f 100644 --- a/Source/PluginEditor.cpp +++ b/Source/PluginEditor.cpp @@ -18,6 +18,7 @@ #include "BendParamsComponent.h" #include "SweepParamsComponent.h" #include "VibratoParamsComponent.h" +#include "WaveformParamsComponent.h" //============================================================================== Magical8bitPlug2AudioProcessorEditor::Magical8bitPlug2AudioProcessorEditor (Magical8bitPlug2AudioProcessor& p) @@ -29,7 +30,7 @@ Magical8bitPlug2AudioProcessorEditor::Magical8bitPlug2AudioProcessorEditor (Magi basicCompo.reset (new BasicParamsComponent (p, *this)); addAndMakeVisible (basicCompo.get()); - + envCompo.reset (new EnvelopeParamsComponent (p)); addAndMakeVisible (envCompo.get()); @@ -51,6 +52,10 @@ Magical8bitPlug2AudioProcessorEditor::Magical8bitPlug2AudioProcessorEditor (Magi vibCompo.reset (new VibratoParamsComponent (p)); addAndMakeVisible (vibCompo.get()); + // waveform + waveformCompo.reset (new WaveformParamsComponent (p)); + addAndMakeVisible (waveformCompo.get()); + (p.parameters.getParameter ("isVolumeSequenceEnabled_raw"))->addListener (this); (p.parameters.getParameter ("isDutySequenceEnabled_raw"))->addListener (this); @@ -141,7 +146,7 @@ struct + genericControlHeight; const int sweepCompoHeight = componentMargin * 2 + indexHeight - + genericControlHeight * 2; + + genericControlHeight * 3; const int vibCompoHeight = componentMargin * 2 + indexHeight + genericControlHeight * 4; @@ -240,6 +245,11 @@ void Magical8bitPlug2AudioProcessorEditor::resized() y3 += sizes.sectionSeparatorHeight; advCompo->setBounds (x, y3, sizes.fullComponentWidth, sizes.advCompoHeight); + // Waveform + int wrX = sizes.leftMargin + sizes.totalWidth; + int wrY = sizes.topMargin; + waveformCompo->setBounds(wrX, wrY, waveformCompo->getWidth(), waveformCompo->getHeight()); + // // Visibility // @@ -272,7 +282,12 @@ void Magical8bitPlug2AudioProcessorEditor::resized() void Magical8bitPlug2AudioProcessorEditor::resizeWholePanel() { - setSize (sizes.totalWidth, sizes.totalHeight (processor.settingRefs.isAdvancedPanelOpen())); + int totalWidth = sizes.totalWidth; + if (processor.settingRefs.oscillatorType() == kVoiceTypeWaveform) + { + totalWidth += sizes.leftMargin * 2 + waveformCompo->getWidth(); + } + setSize (totalWidth, sizes.totalHeight (processor.settingRefs.isAdvancedPanelOpen())); } void Magical8bitPlug2AudioProcessorEditor::parameterValueChanged (int parameterIndex, float newValue) @@ -291,3 +306,13 @@ void Magical8bitPlug2AudioProcessorEditor::parameterValueChanged (int parameterI } } +// waveform +//void Magical8bitPlug2AudioProcessorEditor::waveformInit() +//{ +// waveformCompo->sliderInit(); +//} + +void Magical8bitPlug2AudioProcessorEditor::waveformUpdate() +{ + waveformCompo->sliderRepaint(); +} diff --git a/Source/PluginEditor.h b/Source/PluginEditor.h index 385b278..1ce2fde 100644 --- a/Source/PluginEditor.h +++ b/Source/PluginEditor.h @@ -20,6 +20,7 @@ class NoiseParamsComponent; class BendParamsComponent; class SweepParamsComponent; class VibratoParamsComponent; +class WaveformParamsComponent; //============================================================================== /** @@ -40,6 +41,10 @@ class Magical8bitPlug2AudioProcessorEditor : public AudioProcessorEditor void parameterValueChanged (int parameterIndex, float newValue) override; void parameterGestureChanged (int parameterIndex, bool gestureIsStarting) override {}; + // waveform + //void waveformInit(); + void waveformUpdate(); + private: Magical8bitPlug2AudioProcessor& processor; @@ -51,6 +56,7 @@ class Magical8bitPlug2AudioProcessorEditor : public AudioProcessorEditor std::unique_ptr bendCompo; std::unique_ptr sweepCompo; std::unique_ptr vibCompo; + std::unique_ptr waveformCompo; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Magical8bitPlug2AudioProcessorEditor) }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index 27b41f7..11c7499 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -13,6 +13,7 @@ #include "PulseVoice.h" #include "TriangleVoice.h" #include "NoiseVoice.h" +#include "WaveformVoice.h" #include "FrameSequenceParseErrors.h" #include "EnvelopeParserTest.h" @@ -21,21 +22,21 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() : parameters ( *this, nullptr, Identifier ("Params"), { - // - // Meta - // - std::make_unique ("isAdvancedPanelOpen_raw", "Advanced", false), + // + // Meta + // + std::make_unique ("isAdvancedPanelOpen_raw", "Advanced", false), std::make_unique ("colorScheme", "Color Scheme", StringArray ({"YMCK", "YMCK Dark", "Japan", "Worldwide", "Monotone", "Mono Dark"}), 0), // // Basic // - std::make_unique ("osc", "OSC Type", StringArray ({"Pulse/Square", "Triangle", "Noise"}), 0), + std::make_unique ("osc", "OSC Type", StringArray ({"Pulse/Square", "Triangle", "Noise", "Waveform"}), 0), std::make_unique ("gain", "Gain", 0.0f, 1.0f, 0.5f), std::make_unique ("maxPoly", "Max Poly", NormalisableRange (1.0f, //min - 64.0f, //max - 1.0f, //step - 1.0f), //skew - 8), + 64.0f, //max + 1.0f, //step + 1.0f), //skew + 8), // // ADSR // @@ -45,14 +46,14 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() 5.0f, //max 0.001f, //step 0.5f), //skew - 0.0f), //default + 0.0f), //default std::make_unique ("decay", //ID "Decay", //name NormalisableRange (0.0f, //min 5.0f, //max 0.001f, //step 0.5f), //skew - 0.0f), //default + 0.0f), //default std::make_unique ("suslevel", //ID "Sustain", //name 0.0f, //min @@ -64,7 +65,7 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() 5.0f, //max 0.001f, //step 0.5f), //skew - 0.0f), //default + 0.0f), //default // // Arpeggio // @@ -84,7 +85,7 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() 1.0f, //max 0.001f, //step 0.5f), //skew - 0.15f), //default + 0.15f), //default std::make_unique ("vibratoDepth", "Depth", 0.0f, 2.0f, 0.0f), std::make_unique ("vibratoDelay", "Delay", 0.0f, 1.0f, 0.3f), std::make_unique ("vibratoIgnoresWheel_raw", "Ignores Wheel", true), @@ -92,13 +93,14 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() // Sweep // std::make_unique ("sweepInitialPitch", "Ini.Pitch", -24, 24, 0), + std::make_unique ("sweepEndPitch", "End.Pitch", -24, 24, 0), std::make_unique ("sweepTime", //ID "Time", //name NormalisableRange (0.01f, //min 5.0f, //max 0.001f, //step 0.5f), //skew - 0.1f), //default + 0.1f), //default // // For Pulse // @@ -106,7 +108,7 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() // // For Noise // - std::make_unique ("noiseAlgorithm_raw", "Algorithm", StringArray ({"4bit Pure Random", "1bit Long Cycle", "1bit Short Cycle"}), 0), + std::make_unique ("noiseAlgorithm_raw", "Algorithm", StringArray ({"4bit Pure Random", "1bit Long Cycle", "1bit Short Cycle", "Nes Long Cycle", "Nes Short Cycle"}), 0), std::make_unique ("restrictsToNESFrequency_raw", "Restricts to NES frequency", false), // // Sequence @@ -114,8 +116,77 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() std::make_unique ("isVolumeSequenceEnabled_raw", "Enabled", false), std::make_unique ("isPitchSequenceEnabled_raw", "Enabled", false), std::make_unique ("isDutySequenceEnabled_raw", "Enabled", false), - std::make_unique ("pitchSequenceMode_raw", "Mode", StringArray ({"Coarse", "Fine"}), 0) - } + std::make_unique ("pitchSequenceMode_raw", "Mode", StringArray ({"Coarse", "Fine8", "Fine16"}), 0), + // waveform + std::make_unique("waveformX", "X", StringArray({ "16", "32", "64" }), 2), + std::make_unique("waveformY", "Y", StringArray({ "16", "32", "64" }), 2), + std::make_unique("waveformTemplate", "Template", StringArray({ "Custom", "Sine", "Triangle", "Sawtooth", "Square 6.25%", "Square 18.75%", "Square 31.25%", "Square 37.5%", "Square 43.75%" }), 0), + std::make_unique("waveformWave0", "wave", 0, 63, 0), + std::make_unique("waveformWave1", "wave", 0, 63, 0), + std::make_unique("waveformWave2", "wave", 0, 63, 0), + std::make_unique("waveformWave3", "wave", 0, 63, 0), + std::make_unique("waveformWave4", "wave", 0, 63, 0), + std::make_unique("waveformWave5", "wave", 0, 63, 0), + std::make_unique("waveformWave6", "wave", 0, 63, 0), + std::make_unique("waveformWave7", "wave", 0, 63, 0), + std::make_unique("waveformWave8", "wave", 0, 63, 0), + std::make_unique("waveformWave9", "wave", 0, 63, 0), + std::make_unique("waveformWave10", "wave", 0, 63, 0), + std::make_unique("waveformWave11", "wave", 0, 63, 0), + std::make_unique("waveformWave12", "wave", 0, 63, 0), + std::make_unique("waveformWave13", "wave", 0, 63, 0), + std::make_unique("waveformWave14", "wave", 0, 63, 0), + std::make_unique("waveformWave15", "wave", 0, 63, 0), + std::make_unique("waveformWave16", "wave", 0, 63, 0), + std::make_unique("waveformWave17", "wave", 0, 63, 0), + std::make_unique("waveformWave18", "wave", 0, 63, 0), + std::make_unique("waveformWave19", "wave", 0, 63, 0), + std::make_unique("waveformWave20", "wave", 0, 63, 0), + std::make_unique("waveformWave21", "wave", 0, 63, 0), + std::make_unique("waveformWave22", "wave", 0, 63, 0), + std::make_unique("waveformWave23", "wave", 0, 63, 0), + std::make_unique("waveformWave24", "wave", 0, 63, 0), + std::make_unique("waveformWave25", "wave", 0, 63, 0), + std::make_unique("waveformWave26", "wave", 0, 63, 0), + std::make_unique("waveformWave27", "wave", 0, 63, 0), + std::make_unique("waveformWave28", "wave", 0, 63, 0), + std::make_unique("waveformWave29", "wave", 0, 63, 0), + std::make_unique("waveformWave30", "wave", 0, 63, 0), + std::make_unique("waveformWave31", "wave", 0, 63, 0), + std::make_unique("waveformWave32", "wave", 0, 63, 0), + std::make_unique("waveformWave33", "wave", 0, 63, 0), + std::make_unique("waveformWave34", "wave", 0, 63, 0), + std::make_unique("waveformWave35", "wave", 0, 63, 0), + std::make_unique("waveformWave36", "wave", 0, 63, 0), + std::make_unique("waveformWave37", "wave", 0, 63, 0), + std::make_unique("waveformWave38", "wave", 0, 63, 0), + std::make_unique("waveformWave39", "wave", 0, 63, 0), + std::make_unique("waveformWave40", "wave", 0, 63, 0), + std::make_unique("waveformWave41", "wave", 0, 63, 0), + std::make_unique("waveformWave42", "wave", 0, 63, 0), + std::make_unique("waveformWave43", "wave", 0, 63, 0), + std::make_unique("waveformWave44", "wave", 0, 63, 0), + std::make_unique("waveformWave45", "wave", 0, 63, 0), + std::make_unique("waveformWave46", "wave", 0, 63, 0), + std::make_unique("waveformWave47", "wave", 0, 63, 0), + std::make_unique("waveformWave48", "wave", 0, 63, 0), + std::make_unique("waveformWave49", "wave", 0, 63, 0), + std::make_unique("waveformWave50", "wave", 0, 63, 0), + std::make_unique("waveformWave51", "wave", 0, 63, 0), + std::make_unique("waveformWave52", "wave", 0, 63, 0), + std::make_unique("waveformWave53", "wave", 0, 63, 0), + std::make_unique("waveformWave54", "wave", 0, 63, 0), + std::make_unique("waveformWave55", "wave", 0, 63, 0), + std::make_unique("waveformWave56", "wave", 0, 63, 0), + std::make_unique("waveformWave57", "wave", 0, 63, 0), + std::make_unique("waveformWave58", "wave", 0, 63, 0), + std::make_unique("waveformWave59", "wave", 0, 63, 0), + std::make_unique("waveformWave60", "wave", 0, 63, 0), + std::make_unique("waveformWave61", "wave", 0, 63, 0), + std::make_unique("waveformWave62", "wave", 0, 63, 0), + std::make_unique("waveformWave63", "wave", 0, 63, 0) + +} ) , settingRefs (¶meters) #ifndef JucePlugin_PreferredChannelConfigurations @@ -129,6 +200,16 @@ Magical8bitPlug2AudioProcessor::Magical8bitPlug2AudioProcessor() ) #endif { + // waveform + //parameters.createAndAddParameter(std::make_unique("waveformX", "X", StringArray({ "16", "32", "64" }), 2)); + //parameters.createAndAddParameter(std::make_unique("waveformY", "Y", StringArray({ "16", "32", "64" }), 2)); + //parameters.createAndAddParameter(std::make_unique("waveformTemplate", "Template", StringArray({ "Custom", "Sine", "Triangle", "Sawtooth", "Square 6.25%", "Square 18.75%", "Square 31.25%", "Square 37.5%", "Square 43.75%" }), 0)); + //for (int i = 0; i < 64; i++) + //{ + // parameters.createAndAddParameter(std::make_unique("waveformWave" + String(i), "wave" + String(i), 0, 63, 0)); + //} + //settingRefs.setWaveform(¶meters); + synth.setCurrentPlaybackSampleRate (44100); // Temporary setup, just in case. The actual sample rate is set in prepareToPlay func. setupVoice(); @@ -171,6 +252,10 @@ void Magical8bitPlug2AudioProcessor::setupVoice() case kVoiceTypeNoise: synth.addVoice (new NoiseVoice (&settingRefs)); break; + + case kVoiceTypeWaveform: + synth.addVoice(new WaveformVoice(&settingRefs)); + break; } } } @@ -375,8 +460,13 @@ void Magical8bitPlug2AudioProcessor::getStateInformation (MemoryBlock& destData) copyXmlToBinary (*xml, destData); } -void Magical8bitPlug2AudioProcessor::setStateInformation (const void* data, int sizeInBytes) +void Magical8bitPlug2AudioProcessor::setStateInformation(const void* data, int sizeInBytes) { + //if (Magical8bitPlug2AudioProcessorEditor* activeEditor = (Magical8bitPlug2AudioProcessorEditor*)getActiveEditor()) + //{ + // activeEditor->waveformInit(); + //} + std::unique_ptr xmlState (getXmlFromBinary (data, sizeInBytes)); if (xmlState.get() != nullptr) @@ -473,6 +563,13 @@ void Magical8bitPlug2AudioProcessor::setStateInformation (const void* data, int } setupVoice(); + + if (Magical8bitPlug2AudioProcessorEditor* activeEditor = (Magical8bitPlug2AudioProcessorEditor*)getActiveEditor()) + { + activeEditor->resized(); + activeEditor->resizeWholePanel(); + activeEditor->waveformUpdate(); + } } //============================================================================== diff --git a/Source/PulseVoice.cpp b/Source/PulseVoice.cpp index 90efb9b..10d86bf 100644 --- a/Source/PulseVoice.cpp +++ b/Source/PulseVoice.cpp @@ -56,7 +56,37 @@ void PulseVoice::advanceControlFrame() if (settingRefs->isDutySequenceEnabled()) { - currentDutySequenceFrame = settingRefs->dutySequence.nextIndexOf (currentDutySequenceFrame); - currentDuty = (PulseDuty)settingRefs->dutySequence.valueAt (currentDutySequenceFrame); + //currentDutySequenceFrame = settingRefs->dutySequence.nextIndexOf(currentDutySequenceFrame); + //currentDuty = (PulseDuty)settingRefs->dutySequence.valueAt(currentDutySequenceFrame); + + int currentDutySequenceFrameTmp = settingRefs->dutySequence.nextIndexOf(currentDutySequenceFrame); + if (currentDutySequenceFrameTmp != FrameSequence::SHOULD_RETIRE) + { + currentDutySequenceFrame = currentDutySequenceFrameTmp; + currentDuty = (PulseDuty)settingRefs->dutySequence.valueAt(currentDutySequenceFrame); + } + } +} + +void PulseVoice::stopNote(float velocity, bool allowTailOff) +{ + TonalVoice::stopNote(velocity, allowTailOff); + + if (!allowTailOff) + { + return; + } + + if (settingRefs->isDutySequenceEnabled()) + { + if (settingRefs->dutySequence.hasRelease) + { + if (settingRefs->dutySequence.isInRelease(currentDutySequenceFrame)) { + // Already in release(Custom Env.) + return; + } + currentDutySequenceFrame = settingRefs->dutySequence.releaseSequenceStartIndex; + currentDuty = (PulseDuty)settingRefs->dutySequence.valueAt(currentDutySequenceFrame); + } } } diff --git a/Source/PulseVoice.h b/Source/PulseVoice.h index 97c5340..58ce417 100644 --- a/Source/PulseVoice.h +++ b/Source/PulseVoice.h @@ -25,4 +25,5 @@ struct PulseVoice : public TonalVoice SynthesiserSound*, int currentPitchWheelPosition) override; float voltageForAngle (double angle) override; void advanceControlFrame() override; + void stopNote(float velocity, bool allowTailOff) override; }; diff --git a/Source/Settings.cpp b/Source/Settings.cpp index dd2ebd2..3ef46d3 100644 --- a/Source/Settings.cpp +++ b/Source/Settings.cpp @@ -77,3 +77,29 @@ String& SettingRefs::getSequenceString (const String& type) printf ("*** parameter type invalid!\n"); return volumeSequenceString; } + +//void SettingRefs::setWaveform(AudioProcessorValueTreeState* parameters) +//{ +// // waveform +// for (int i = 0; i < 64; i++) +// { +// waveformWave[i] = (float*)parameters->getRawParameterValue("waveformWave" + String(i)); +// } +// waveformX = (float*)parameters->getRawParameterValue("waveformX"); +// waveformY = (float*)parameters->getRawParameterValue("waveformY"); +// waveformTemplate = (float*)parameters->getRawParameterValue("waveformTemplate"); +//} + +int SettingRefs::getWaveformX() +{ + int range[3] = {16, 32, 64}; + + return range[(int)(*waveformX)]; +} + +int SettingRefs::getWaveformY() +{ + int range[3] = { 16, 32, 64 }; + + return range[(int)(*waveformY)] - 1; +} diff --git a/Source/Settings.h b/Source/Settings.h index 738aa72..f7a9d59 100644 --- a/Source/Settings.h +++ b/Source/Settings.h @@ -23,7 +23,8 @@ enum VoiceType { kVoiceTypePulse = 0, kVoiceTypeTriangle, - kVoiceTypeNoise + kVoiceTypeNoise, + kVoiceTypeWaveform }; struct PluginSettings @@ -52,12 +53,15 @@ enum NoiseAlgorithm kNoiseInfinite2 = 0, kNoiseLong, kNoiseShort, + kNoiseLongNes, + kNoiseShortNes, }; enum PitchSequenceMode { kPitchSequenceModeCoarse = 0, - kPitchSequenceModeFine + kPitchSequenceModeFine, + kPitchSequenceModeFine16 }; class FrameSequenceChangeListener @@ -108,6 +112,7 @@ struct SettingRefs float* vibratoIgnoresWheel_raw = nullptr; // Sweep float* sweepInitialPitch = nullptr; + float* sweepEndPitch = nullptr; float* sweepTime = nullptr; // For Pulse float* duty = nullptr; @@ -123,9 +128,9 @@ struct SettingRefs FrameSequence volumeSequence; FrameSequence pitchSequence; FrameSequence dutySequence; - String volumeSequenceString = ""; - String pitchSequenceString = ""; - String dutySequenceString = ""; + String volumeSequenceString; //= "15 x 5, 15 to 0 in 15 [5, 4, 3] | 2, 1, 0"; + String pitchSequenceString; //= "15 x 5, 15 to 0 in 15 [5, 4, 3] | 2, 1, 0"; + String dutySequenceString; //= "2 x 5, 2 to 0 in 15 [2, 1, 0] | 2, 1, 0"; bool setSequenceWithString (const String& type, const String& input, ParseError* error); String& getSequenceString (const String& type); @@ -152,6 +157,14 @@ struct SettingRefs bool isDutySequenceEnabled() { return *isDutySequenceEnabled_raw > 0.5; } PitchSequenceMode pitchSequenceMode() { return (PitchSequenceMode) ((int) (*pitchSequenceMode_raw)); } + // waveform + //void setWaveform(AudioProcessorValueTreeState* parameters); + int getWaveformX(); + int getWaveformY(); + float* waveformWave[64]; + float* waveformX = nullptr; + float* waveformY = nullptr; + float* waveformTemplate = nullptr; // // constructor @@ -183,6 +196,7 @@ struct SettingRefs vibratoIgnoresWheel_raw = (float*) parameters->getRawParameterValue ("vibratoIgnoresWheel_raw"); // Sweep sweepInitialPitch = (float*) parameters->getRawParameterValue ("sweepInitialPitch"); + sweepEndPitch = (float*)parameters->getRawParameterValue("sweepEndPitch"); sweepTime = (float*) parameters->getRawParameterValue ("sweepTime"); // For Pulse duty = (float*) parameters->getRawParameterValue ("duty"); @@ -194,6 +208,13 @@ struct SettingRefs isPitchSequenceEnabled_raw = (float*) parameters->getRawParameterValue ("isPitchSequenceEnabled_raw"); isDutySequenceEnabled_raw = (float*) parameters->getRawParameterValue ("isDutySequenceEnabled_raw"); pitchSequenceMode_raw = (float*) parameters->getRawParameterValue ("pitchSequenceMode_raw"); - + // waveform + for (int i = 0; i < 64; i++) + { + waveformWave[i] = (float*)parameters->getRawParameterValue("waveformWave" + String(i)); + } + waveformX = (float*)parameters->getRawParameterValue("waveformX"); + waveformY = (float*)parameters->getRawParameterValue("waveformY"); + waveformTemplate = (float*)parameters->getRawParameterValue("waveformTemplate"); } }; diff --git a/Source/SliderVerticalComponent.cpp b/Source/SliderVerticalComponent.cpp new file mode 100644 index 0000000..a7e7232 --- /dev/null +++ b/Source/SliderVerticalComponent.cpp @@ -0,0 +1,172 @@ +/* + ============================================================================== + + This is an automatically generated GUI class created by the Projucer! + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Created with Projucer version: 6.0.8 + + ------------------------------------------------------------------------------ + + The Projucer is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited. + + ============================================================================== +*/ + +//[Headers] You can add your own extra header files here... +//[/Headers] + +#include "SliderVerticalComponent.h" + + +//[MiscUserDefs] You can add your own user definitions and misc code here... +//[/MiscUserDefs] + +//============================================================================== +SliderVerticalComponent::SliderVerticalComponent (Magical8bitPlug2AudioProcessor& p, String paramId) + : processor(p) +{ + //[Constructor_pre] You can add your own custom stuff here.. + setInterceptsMouseClicks(true, false); + for (int i = 0; i < 64; i++) + { + verticalSliders[i].reset(new juce::Slider("vertical slider")); + addAndMakeVisible(verticalSliders[i].get()); + verticalSliders[i]->setRange(0, 63, 0); + verticalSliders[i]->setSliderStyle(juce::Slider::LinearBarVertical); + verticalSliders[i]->setTextBoxStyle(juce::Slider::NoTextBox, true, 30, 20); + verticalSliders[i]->setBounds(0 + i * 8, 0, 9, 250); + verticalSliders[i]->setInterceptsMouseClicks(false, false); + attc[i].reset(new SliderAttachment(p.parameters, paramId + String(i), *verticalSliders[i])); + } + //[/Constructor_pre] + + + //[UserPreSize] + //[/UserPreSize] + + setSize (513, 250); + + + //[Constructor] You can add your own custom stuff here.. + baseMaxValue = 63; + //[/Constructor] +} + +SliderVerticalComponent::~SliderVerticalComponent() +{ + //[Destructor_pre]. You can add your own custom destruction code here.. + for (int i = 0; i < 64; i++) + { + attc[i].reset(); + verticalSliders[i] = nullptr; + } + //[/Destructor_pre] + + + + //[Destructor]. You can add your own custom destruction code here.. + //[/Destructor] +} + +//============================================================================== +void SliderVerticalComponent::paint (juce::Graphics& g) +{ + //[UserPrePaint] Add your own custom painting code here.. + //[/UserPrePaint] + + //[UserPaint] Add your own custom painting code here.. + //[/UserPaint] +} + +void SliderVerticalComponent::resized() +{ + //[UserPreResize] Add your own custom resize code here.. + //[/UserPreResize] + + //[UserResized] Add your own custom resize handling here.. + //[/UserResized] +} + +void SliderVerticalComponent::mouseDown (const juce::MouseEvent& e) +{ + //[UserCode_mouseDown] -- Add your code here... + mouseDrag(e); + //[/UserCode_mouseDown] +} + +void SliderVerticalComponent::mouseDrag (const juce::MouseEvent& e) +{ + //[UserCode_mouseDrag] -- Add your code here... + int rate = (baseMaxValue + 1) / (maxValue + 1); + int sliderIndex = jlimit(0, numWidth - 1, (int)floor(e.position.x / (getWidth() / ((double)numWidth)))); + waveValue = jlimit(0, baseMaxValue, (int)(baseMaxValue * ((double)getHeight() - (double)e.position.y) / ((double)getHeight()) + 0.5)); + waveValue = (int)(waveValue / rate); + verticalSliders[sliderIndex]->setValue(((double)waveValue / maxValue * baseMaxValue)); + getParentComponent()->mouseDrag(e.getEventRelativeTo(getParentComponent())); + //[/UserCode_mouseDrag] +} + + + +//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... +void SliderVerticalComponent::setForm(int numWidth, int maxValue, int width, int height) +{ + this->numWidth = jlimit(0, 64, numWidth); + this->maxValue = maxValue; + + int sliderWidth = width / this->numWidth; + + setSize(sliderWidth * this->numWidth + 1, height); + + int rate = (baseMaxValue + 1) / (maxValue + 1); + + for (int i = 0; i < 64; i++) + { + verticalSliders[i]->setBounds(0 + i * sliderWidth, 0, sliderWidth + 1, height); + int value = verticalSliders[i]->getValue(); + verticalSliders[i]->setValue(((double)(value / rate) / maxValue * baseMaxValue)); + verticalSliders[i]->repaint(); + } +} + +void SliderVerticalComponent::setValue(int index, int value) +{ + verticalSliders[index]->setValue(((double)value / maxValue * baseMaxValue)); +} +//[/MiscUserCode] + + +//============================================================================== +#if 0 +/* -- Projucer information section -- + + This is where the Projucer stores the metadata that describe this GUI layout, so + make changes in here at your peril! + +BEGIN_JUCER_METADATA + + + + + + + + + +END_JUCER_METADATA +*/ +#endif + + +//[EndFile] You can add extra defines here... +//[/EndFile] + diff --git a/Source/SliderVerticalComponent.h b/Source/SliderVerticalComponent.h new file mode 100644 index 0000000..8356699 --- /dev/null +++ b/Source/SliderVerticalComponent.h @@ -0,0 +1,78 @@ +/* + ============================================================================== + + This is an automatically generated GUI class created by the Projucer! + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Created with Projucer version: 6.0.8 + + ------------------------------------------------------------------------------ + + The Projucer is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited. + + ============================================================================== +*/ + +#pragma once + +//[Headers] -- You can add your own extra header files here -- +#include +#include "PluginProcessor.h" +#include "Defs.h" +//[/Headers] + + + +//============================================================================== +/** + //[Comments] + An auto-generated component, created by the Projucer. + + Describe your class and how it works here! + //[/Comments] +*/ +class SliderVerticalComponent : public juce::Component +{ +public: + //============================================================================== + SliderVerticalComponent (Magical8bitPlug2AudioProcessor& p, String paramId); + ~SliderVerticalComponent() override; + + //============================================================================== + //[UserMethods] -- You can add your own custom methods in this section. + void setForm(int numWidth, int maxValue, int width, int height); + void setValue(int index, int value); + int waveValue = -1; + //[/UserMethods] + + void paint (juce::Graphics& g) override; + void resized() override; + void mouseDown (const juce::MouseEvent& e) override; + void mouseDrag (const juce::MouseEvent& e) override; + + + +private: + //[UserVariables] -- You can add your own custom variables in this section. + std::unique_ptr verticalSliders[64]; + int numWidth; + int maxValue; + int baseMaxValue; + Magical8bitPlug2AudioProcessor& processor; + std::unique_ptr attc[64]; + //[/UserVariables] + + //============================================================================== + + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SliderVerticalComponent) +}; + +//[EndFile] You can add extra defines here... +//[/EndFile] + diff --git a/Source/SweepParamsComponent.cpp b/Source/SweepParamsComponent.cpp index 8003e04..a96cf8d 100644 --- a/Source/SweepParamsComponent.cpp +++ b/Source/SweepParamsComponent.cpp @@ -51,11 +51,15 @@ SweepParamsComponent::SweepParamsComponent (Magical8bitPlug2AudioProcessor& p) addAndMakeVisible (timeSlider.get()); timeSlider->setName ("time slider"); + endPitchSlider.reset (new SliderComponent (p, "sweepEndPitch", "End.Pitch")); + addAndMakeVisible (endPitchSlider.get()); + endPitchSlider->setName ("end pitch slider"); + //[UserPreSize] //[/UserPreSize] - setSize (340, 86); + setSize (340, 114); //[Constructor] You can add your own custom stuff here.. @@ -70,6 +74,7 @@ SweepParamsComponent::~SweepParamsComponent() label = nullptr; iniPitchSlider = nullptr; timeSlider = nullptr; + endPitchSlider = nullptr; //[Destructor]. You can add your own custom destruction code here.. @@ -92,7 +97,8 @@ void SweepParamsComponent::resized() //[/UserPreResize] iniPitchSlider->setBounds (0, 26, proportionOfWidth (1.0000f), 28); - timeSlider->setBounds (0, 54, proportionOfWidth (1.0000f), 28); + timeSlider->setBounds (0, 82, proportionOfWidth (1.0000f), 28); + endPitchSlider->setBounds (0, 54, proportionOfWidth (1.0000f), 28); //[UserResized] Add your own custom resize handling here.. //[/UserResized] } @@ -115,7 +121,7 @@ BEGIN_JUCER_METADATA + overlayOpacity="0.330" fixedSize="1" initialWidth="340" initialHeight="114"> END_JUCER_METADATA diff --git a/Source/SweepParamsComponent.h b/Source/SweepParamsComponent.h index bd80ed8..e4e5add 100644 --- a/Source/SweepParamsComponent.h +++ b/Source/SweepParamsComponent.h @@ -58,6 +58,7 @@ class SweepParamsComponent : public Component std::unique_ptr label; std::unique_ptr iniPitchSlider; std::unique_ptr timeSlider; + std::unique_ptr endPitchSlider; //============================================================================== diff --git a/Source/TonalVoice.cpp b/Source/TonalVoice.cpp index f0471bc..ef7b365 100644 --- a/Source/TonalVoice.cpp +++ b/Source/TonalVoice.cpp @@ -27,16 +27,28 @@ void TonalVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound vibratoCount = 0; float iniPitch = * (settingRefs->sweepInitialPitch); + float endPitch = *(settingRefs->sweepEndPitch); float time = * (settingRefs->sweepTime); currentAutoBendAmount = iniPitch; - autoBendDelta = -1.0 * iniPitch / (time * getSampleRate()); + autoBendDelta = -1.0 * (iniPitch - endPitch) / (time * getSampleRate()); + + //if (*(settingRefs->osc) == kVoiceTypeTriangle) + //{ + // nesPitchCorrection = -12; + //} } void TonalVoice::advanceControlFrame() { BaseVoice::advanceControlFrame(); - currentPitchSequenceFrame = settingRefs->pitchSequence.nextIndexOf (currentPitchSequenceFrame); + //currentPitchSequenceFrame = settingRefs->pitchSequence.nextIndexOf(currentPitchSequenceFrame); + + int currentPitchSequenceFrameTmp = settingRefs->pitchSequence.nextIndexOf(currentPitchSequenceFrame); + if (currentPitchSequenceFrameTmp != FrameSequence::SHOULD_RETIRE) + { + currentPitchSequenceFrame = currentPitchSequenceFrameTmp; + } } void TonalVoice::calculateAngleDelta() @@ -49,6 +61,10 @@ void TonalVoice::calculateAngleDelta() { switch (settingRefs->pitchSequenceMode()) { + case kPitchSequenceModeFine16: + finePitchInSeq = (double)settingRefs->pitchSequence.valueAt (currentPitchSequenceFrame) / 16.0; + break; + case kPitchSequenceModeFine: finePitchInSeq = (double)settingRefs->pitchSequence.valueAt (currentPitchSequenceFrame) / 8.0; break; @@ -69,7 +85,8 @@ void TonalVoice::calculateAngleDelta() + currentBendAmount + currentAutoBendAmount + vibratoAmount - + finePitchInSeq; + + finePitchInSeq + + nesPitchCorrection; auto cyclesPerSecond = noteNoToHeltzDouble (noteNoInDouble); auto cyclesPerSample = cyclesPerSecond / getSampleRate(); @@ -118,19 +135,41 @@ void TonalVoice::onFrameAdvanced() if (autoBendDelta > 0) { // positive slope - if (currentAutoBendAmount > 0) + if (currentAutoBendAmount > *(settingRefs->sweepEndPitch)) { - currentAutoBendAmount = 0; + currentAutoBendAmount = *(settingRefs->sweepEndPitch); autoBendDelta = 0; } } else { // negative slope - if (currentAutoBendAmount < 0) + if (currentAutoBendAmount < *(settingRefs->sweepEndPitch)) { - currentAutoBendAmount = 0; + currentAutoBendAmount = *(settingRefs->sweepEndPitch); autoBendDelta = 0; } } -}; +} + +void TonalVoice::stopNote(float velocity, bool allowTailOff) +{ + BaseVoice::stopNote(velocity, allowTailOff); + + if (!allowTailOff) + { + return; + } + + if (settingRefs->isPitchSequenceEnabled()) + { + if (settingRefs->pitchSequence.hasRelease) + { + if (settingRefs->pitchSequence.isInRelease(currentPitchSequenceFrame)) { + // Already in release(Custom Env.) + return; + } + currentPitchSequenceFrame = settingRefs->pitchSequence.releaseSequenceStartIndex; + } + } +} diff --git a/Source/TonalVoice.h b/Source/TonalVoice.h index 5146fb7..a969762 100644 --- a/Source/TonalVoice.h +++ b/Source/TonalVoice.h @@ -36,6 +36,8 @@ struct TonalVoice : public BaseVoice // The base for Pulse and Triangle // Custom Pitch/Note states int currentPitchSequenceFrame = 0; + double nesPitchCorrection = 0; + void startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int currentPitchWheelPosition) override; void advanceControlFrame() override; @@ -46,4 +48,5 @@ struct TonalVoice : public BaseVoice // The base for Pulse and Triangle double noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA = 440); void onFrameAdvanced() override; + void stopNote(float velocity, bool allowTailOff) override; }; diff --git a/Source/WaveformParamsComponent.cpp b/Source/WaveformParamsComponent.cpp new file mode 100644 index 0000000..56b6ca3 --- /dev/null +++ b/Source/WaveformParamsComponent.cpp @@ -0,0 +1,374 @@ +/* + ============================================================================== + + This is an automatically generated GUI class created by the Projucer! + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Created with Projucer version: 6.0.8 + + ------------------------------------------------------------------------------ + + The Projucer is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited. + + ============================================================================== +*/ + +//[Headers] You can add your own extra header files here... +//[/Headers] + +#include "WaveformParamsComponent.h" + + +//[MiscUserDefs] You can add your own user definitions and misc code here... +//[/MiscUserDefs] + +//============================================================================== +WaveformParamsComponent::WaveformParamsComponent (Magical8bitPlug2AudioProcessor& p) + : processor(p) +{ + //[Constructor_pre] You can add your own custom stuff here.. + //[/Constructor_pre] + + sliderVerticalComponent.reset (new SliderVerticalComponent (p, "waveformWave")); + addAndMakeVisible (sliderVerticalComponent.get()); + sliderVerticalComponent->setName ("slider vertical component"); + + sliderVerticalComponent->setBounds (10, 34, 513, 250); + + label.reset (new juce::Label ("label", + TRANS("Waveform"))); + addAndMakeVisible (label.get()); + label->setFont (juce::Font (17.00f, juce::Font::plain).withTypefaceStyle ("Regular")); + label->setJustificationType (juce::Justification::centredLeft); + label->setEditable (false, false, false); + label->setColour (juce::TextEditor::textColourId, juce::Colours::black); + label->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000)); + + label->setBounds (0, 4, 150, 22); + + waveformWaveText.reset (new juce::TextEditor ("waveform wave text")); + addAndMakeVisible (waveformWaveText.get()); + waveformWaveText->setMultiLine (false); + waveformWaveText->setReturnKeyStartsNewLine (false); + waveformWaveText->setReadOnly (true); + waveformWaveText->setScrollbarsShown (false); + waveformWaveText->setCaretVisible (false); + waveformWaveText->setPopupMenuEnabled (false); + waveformWaveText->setText (juce::String()); + + waveformWaveText->setBounds (472, 8, 50, 20); + + waveformComboX.reset (new juce::ComboBox ("waveform combo x")); + addAndMakeVisible (waveformComboX.get()); + waveformComboX->setEditableText (false); + waveformComboX->setJustificationType (juce::Justification::centredLeft); + waveformComboX->setTextWhenNothingSelected (juce::String()); + waveformComboX->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); + waveformComboX->addListener (this); + + waveformComboX->setBounds (40, 296, 80, 24); + + waveformXLabel.reset (new juce::Label ("waveform x label", + TRANS("X"))); + addAndMakeVisible (waveformXLabel.get()); + waveformXLabel->setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); + waveformXLabel->setJustificationType (juce::Justification::centredLeft); + waveformXLabel->setEditable (false, false, false); + waveformXLabel->setColour (juce::TextEditor::textColourId, juce::Colours::black); + waveformXLabel->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000)); + + waveformXLabel->setBounds (16, 296, 24, 24); + + waveformYLabel.reset (new juce::Label ("waveform y label", + TRANS("Y"))); + addAndMakeVisible (waveformYLabel.get()); + waveformYLabel->setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); + waveformYLabel->setJustificationType (juce::Justification::centredLeft); + waveformYLabel->setEditable (false, false, false); + waveformYLabel->setColour (juce::TextEditor::textColourId, juce::Colours::black); + waveformYLabel->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000)); + + waveformYLabel->setBounds (136, 296, 24, 24); + + waveformComboY.reset (new juce::ComboBox ("waveform combo y")); + addAndMakeVisible (waveformComboY.get()); + waveformComboY->setEditableText (false); + waveformComboY->setJustificationType (juce::Justification::centredLeft); + waveformComboY->setTextWhenNothingSelected (juce::String()); + waveformComboY->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); + waveformComboY->addListener (this); + + waveformComboY->setBounds (160, 296, 80, 24); + + waveformTemplateLabel.reset (new juce::Label ("waveform template label", + TRANS("Template"))); + addAndMakeVisible (waveformTemplateLabel.get()); + waveformTemplateLabel->setFont (juce::Font (15.00f, juce::Font::plain).withTypefaceStyle ("Regular")); + waveformTemplateLabel->setJustificationType (juce::Justification::centredLeft); + waveformTemplateLabel->setEditable (false, false, false); + waveformTemplateLabel->setColour (juce::TextEditor::textColourId, juce::Colours::black); + waveformTemplateLabel->setColour (juce::TextEditor::backgroundColourId, juce::Colour (0x00000000)); + + waveformTemplateLabel->setBounds (280, 296, 72, 24); + + waveformComboTemplate.reset (new juce::ComboBox ("waveform combo template")); + addAndMakeVisible (waveformComboTemplate.get()); + waveformComboTemplate->setEditableText (false); + waveformComboTemplate->setJustificationType (juce::Justification::centredLeft); + waveformComboTemplate->setTextWhenNothingSelected (juce::String()); + waveformComboTemplate->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); + waveformComboTemplate->addListener (this); + + waveformComboTemplate->setBounds (352, 296, 168, 24); + + + //[UserPreSize] + //[/UserPreSize] + + setSize (536, 340); + + + //[Constructor] You can add your own custom stuff here.. + AudioParameterChoice* cX = (AudioParameterChoice*)p.parameters.getParameter("waveformX"); + for (int i = 0; i < cX->choices.size(); i++) + { + String choice = cX->choices[i]; + waveformComboX->addItem(choice, i + 1); + } + waveformComboX->setSelectedItemIndex(cX->getIndex()); + attcX.reset(new ComboBoxAttachment(p.parameters, "waveformX", *waveformComboX)); + + AudioParameterChoice* cY = (AudioParameterChoice*)p.parameters.getParameter("waveformY"); + for (int i = 0; i < cY->choices.size(); i++) + { + String choice = cY->choices[i]; + waveformComboY->addItem(choice, i + 1); + } + waveformComboY->setSelectedItemIndex(cY->getIndex()); + attcY.reset(new ComboBoxAttachment(p.parameters, "waveformY", *waveformComboY)); + + AudioParameterChoice* cTemplate = (AudioParameterChoice*)p.parameters.getParameter("waveformTemplate"); + for (int i = 0; i < cTemplate->choices.size(); i++) + { + String choice = cTemplate->choices[i]; + waveformComboTemplate->addItem(choice, i + 1); + } + waveformComboTemplate->setSelectedItemIndex(cTemplate->getIndex()); + attcTemplate.reset(new ComboBoxAttachment(p.parameters, "waveformTemplate", *waveformComboTemplate)); + + int fontHeight = waveformWaveText->getFont().getHeight(); + int topIndent = (waveformWaveText->getHeight() - fontHeight) / 2; + int fontWidth = fontHeight; + int leftIndent = (waveformWaveText->getWidth() - fontWidth) / 2; + waveformWaveText->setBorder(BorderSize(0, 0, 0, 0)); + waveformWaveText->setIndents(leftIndent, topIndent); + //[/Constructor] +} + +WaveformParamsComponent::~WaveformParamsComponent() +{ + //[Destructor_pre]. You can add your own custom destruction code here.. + attcX.reset(); + attcY.reset(); + attcTemplate.reset(); + //[/Destructor_pre] + + sliderVerticalComponent = nullptr; + label = nullptr; + waveformWaveText = nullptr; + waveformComboX = nullptr; + waveformXLabel = nullptr; + waveformYLabel = nullptr; + waveformComboY = nullptr; + waveformTemplateLabel = nullptr; + waveformComboTemplate = nullptr; + + + //[Destructor]. You can add your own custom destruction code here.. + //[/Destructor] +} + +//============================================================================== +void WaveformParamsComponent::paint (juce::Graphics& g) +{ + //[UserPrePaint] Add your own custom painting code here.. + //[/UserPrePaint] + + //[UserPaint] Add your own custom painting code here.. + //[/UserPaint] +} + +void WaveformParamsComponent::resized() +{ + //[UserPreResize] Add your own custom resize code here.. + //[/UserPreResize] + + //[UserResized] Add your own custom resize handling here.. + //[/UserResized] +} + +void WaveformParamsComponent::comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged) +{ + //[UsercomboBoxChanged_Pre] + //[/UsercomboBoxChanged_Pre] + + if (comboBoxThatHasChanged == waveformComboX.get()) + { + //[UserComboBoxCode_waveformComboX] -- add your combo box handling code here.. + sliderVerticalComponent->setForm(processor.settingRefs.getWaveformX(), processor.settingRefs.getWaveformY(), 513, 250); + //[/UserComboBoxCode_waveformComboX] + } + else if (comboBoxThatHasChanged == waveformComboY.get()) + { + //[UserComboBoxCode_waveformComboY] -- add your combo box handling code here.. + sliderVerticalComponent->setForm(processor.settingRefs.getWaveformX(), processor.settingRefs.getWaveformY(), 513, 250); + //[/UserComboBoxCode_waveformComboY] + } + else if (comboBoxThatHasChanged == waveformComboTemplate.get()) + { + //[UserComboBoxCode_waveformComboTemplate] -- add your combo box handling code here.. + //0:"Custom", 1:"Sine", 2:"Triangle", 3:"Sawtooth", 4:"Square 6.25%", 5:"Square 18.75%", 6:"Square 31.25%", 7:"Square 37.5%", 8:"Square 43.75%" + int x = processor.settingRefs.getWaveformX(); + int y = processor.settingRefs.getWaveformY(); + if (waveformComboTemplate->getSelectedItemIndex() == 1) + { + double twopi = MathConstants::pi * 2.0; + for (int i = 0; i < x; i++) + { + sliderVerticalComponent->setValue(i, (int)((std::sin(twopi * i / x) + 1.0) * y / 2.0 + 0.5)); + } + } + else if (waveformComboTemplate->getSelectedItemIndex() == 2) + { + for (int i = 0; i < (x / 2); i++) + { + sliderVerticalComponent->setValue(i, (int)(i * (double)y / (x / 2 - 1) + 0.5)); + sliderVerticalComponent->setValue(i + (x / 2), (int)(y - i * (double)y / (x / 2 - 1) + 0.5)); + } + } + else if (waveformComboTemplate->getSelectedItemIndex() == 3) + { + for (int i = 0; i < x; i++) + { + sliderVerticalComponent->setValue(i, (int)(i * (double)y / (x - 1) + 0.5)); + } + } + else if (waveformComboTemplate->getSelectedItemIndex() >= 4 && waveformComboTemplate->getSelectedItemIndex() <= 8) + { + double duty[5] = { 0.0625, 0.1875, 0.3125, 0.375, 0.4375 }; + for (int i = 0; i < (int)(x * duty[waveformComboTemplate->getSelectedItemIndex() - 4]); i++) + { + sliderVerticalComponent->setValue(i, 0); + } + for (int i = (int)(x * duty[waveformComboTemplate->getSelectedItemIndex() - 4]); i < x; i++) + { + sliderVerticalComponent->setValue(i, y); + } + } + waveformComboTemplate->setSelectedItemIndex(0); + //[/UserComboBoxCode_waveformComboTemplate] + } + + //[UsercomboBoxChanged_Post] + //[/UsercomboBoxChanged_Post] +} + +void WaveformParamsComponent::mouseDrag (const juce::MouseEvent& e) +{ + //[UserCode_mouseDrag] -- Add your code here... + int waveValue = sliderVerticalComponent->waveValue; + if (waveValue >= 0) + { + waveformWaveText->setText(String(waveValue)); + } + //[/UserCode_mouseDrag] +} + + + +//[MiscUserCode] You can add your own definitions of your custom methods or any other code here... +void WaveformParamsComponent::sliderRepaint() +{ + sliderVerticalComponent->setForm(processor.settingRefs.getWaveformX(), processor.settingRefs.getWaveformY(), 513, 250); +} + +//void WaveformParamsComponent::sliderInit() +//{ +// for (int i = 0; i < 64; i++) +// { +// //sliderVerticalComponent->verticalSliders[i]->setValue(0); +// sliderVerticalComponent->verticalSliders[i]->setRange(0, 63, 1); +// //sliderVerticalComponent->verticalSliders[i]->repaint(); +// } +//} +//[/MiscUserCode] + + +//============================================================================== +#if 0 +/* -- Projucer information section -- + + This is where the Projucer stores the metadata that describe this GUI layout, so + make changes in here at your peril! + +BEGIN_JUCER_METADATA + + + + + + + + + +END_JUCER_METADATA +*/ +#endif + + +//[EndFile] You can add extra defines here... +//[/EndFile] + diff --git a/Source/WaveformParamsComponent.h b/Source/WaveformParamsComponent.h new file mode 100644 index 0000000..99c267c --- /dev/null +++ b/Source/WaveformParamsComponent.h @@ -0,0 +1,84 @@ +/* + ============================================================================== + + This is an automatically generated GUI class created by the Projucer! + + Be careful when adding custom code to these files, as only the code within + the "//[xyz]" and "//[/xyz]" sections will be retained when the file is loaded + and re-saved. + + Created with Projucer version: 6.0.8 + + ------------------------------------------------------------------------------ + + The Projucer is part of the JUCE library. + Copyright (c) 2020 - Raw Material Software Limited. + + ============================================================================== +*/ + +#pragma once + +//[Headers] -- You can add your own extra header files here -- +#include +#include "SliderVerticalComponent.h" +//[/Headers] + + + +//============================================================================== +/** + //[Comments] + An auto-generated component, created by the Projucer. + + Describe your class and how it works here! + //[/Comments] +*/ +class WaveformParamsComponent : public Component, + public juce::ComboBox::Listener +{ +public: + //============================================================================== + WaveformParamsComponent (Magical8bitPlug2AudioProcessor& p); + ~WaveformParamsComponent() override; + + //============================================================================== + //[UserMethods] -- You can add your own custom methods in this section. + void sliderRepaint(); + //void sliderInit(); + //[/UserMethods] + + void paint (juce::Graphics& g) override; + void resized() override; + void comboBoxChanged (juce::ComboBox* comboBoxThatHasChanged) override; + void mouseDrag (const juce::MouseEvent& e) override; + + + +private: + //[UserVariables] -- You can add your own custom variables in this section. + Magical8bitPlug2AudioProcessor& processor; + std::unique_ptr attcX; + std::unique_ptr attcY; + std::unique_ptr attcTemplate; + //[/UserVariables] + + //============================================================================== + std::unique_ptr sliderVerticalComponent; + std::unique_ptr label; + std::unique_ptr waveformWaveText; + std::unique_ptr waveformComboX; + std::unique_ptr waveformXLabel; + std::unique_ptr waveformYLabel; + std::unique_ptr waveformComboY; + std::unique_ptr waveformTemplateLabel; + std::unique_ptr waveformComboTemplate; + + + //============================================================================== + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WaveformParamsComponent) +}; + +//[EndFile] You can add extra defines here... +//[/EndFile] + diff --git a/Source/WaveformVoice.cpp b/Source/WaveformVoice.cpp new file mode 100644 index 0000000..a2a19b0 --- /dev/null +++ b/Source/WaveformVoice.cpp @@ -0,0 +1,39 @@ +/* + ============================================================================== + + WaveformVoice.cpp + Created: 7 Jul 2021 5:57:30am + Author: SHACHO + + ============================================================================== +*/ + +#include "WaveformVoice.h" + +//--------------------------------------------- +// +// Waveform Voice +// +//--------------------------------------------- +WaveformVoice::WaveformVoice(SettingRefs* sRefs) : TonalVoice(sRefs) {} + +float WaveformVoice::voltageForAngle(double angle) +{ + int x = settingRefs->getWaveformX(); + int y = 63; // settingRefs->getWaveformY(); + + float sequence[64]; + + for (int i = 0; i < x; i++) + { + sequence[i] = *(settingRefs->waveformWave[i]); + } + + double twopi = MathConstants::pi * 2.0; + int step = (int)(x * angle / twopi); + + float level = sequence[step]; + float output = (float)level / (y / 2.0f) - 1.0f; + + return output; +} diff --git a/Source/WaveformVoice.h b/Source/WaveformVoice.h new file mode 100644 index 0000000..27f959c --- /dev/null +++ b/Source/WaveformVoice.h @@ -0,0 +1,18 @@ +/* + ============================================================================== + + WaveformVoice.h + Created: 7 Jul 2021 5:57:30am + Author: SHACHO + + ============================================================================== +*/ + +#pragma once +#include "TonalVoice.h" + +struct WaveformVoice : public TonalVoice +{ + WaveformVoice(SettingRefs* sRefs); + float voltageForAngle(double angle) override; +};