diff --git a/Source/CustomSynth.cpp b/Source/CustomSynth.cpp index ab295b8..d864b57 100644 --- a/Source/CustomSynth.cpp +++ b/Source/CustomSynth.cpp @@ -59,14 +59,15 @@ void CustomSynth::noteOn(int midiChannel, int midiNoteNumber, float velocity) { } else { switch (processor.settingRefs.monophonicBehavior()) { case kLegato: - // just start + // start note and set legato mode Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity); voice->setLegatoMode(*(processor.settingRefs.portamentoTime)); break; case kArpeggioUp: case kArpeggioDown: - // calc arpeggio interval - // set arpeggio mode with this note number and arp interval + // start note and calc arpeggio interval + Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity); + voice->setArpeggioMode(calcArpeggioInterval()); break; default: // no-op @@ -75,6 +76,30 @@ void CustomSynth::noteOn(int midiChannel, int midiNoteNumber, float velocity) { } } +double CustomSynth::calcArpeggioInterval() { + switch (processor.settingRefs.apreggioIntervalType()) { + case k1frame: + return 1.0 / 60.0; + case k2frames: + return 1.0 / 30.0; + case k3frames: + return 1.0 / 20.0; + case k64th: + return 240.0 / (processor.getCurrentBPM() * 64); + case k48th: + return 240.0 / (processor.getCurrentBPM() * 48); + case k32nd: + return 240.0 / (processor.getCurrentBPM() * 32); + case k24th: + return 240.0 / (processor.getCurrentBPM() * 24); + case kSlider: + return *(processor.settingRefs.arpeggioIntervalSliderValue); + default: + return 1.0 / 60.0; + break; + } +} + void CustomSynth::noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff) { TonalVoice *voice = getVoiceIfShouldProcessInMonoMode(); @@ -104,9 +129,12 @@ void CustomSynth::noteOff(int midiChannel, int midiNoteNumber, float velocity, b break; case kArpeggioUp: case kArpeggioDown: - // remove arpeggio note and get # of remaining arpeggio notes - // if zero - // all notes off + { + int numBuffer = voice->removeArpeggioNote(midiNoteNumber); + if (numBuffer < 1) { + Synthesiser::noteOff(midiChannel, voice->getCurrentlyPlayingNote(), velocity, allowTailOff); + } + } break; default: break; diff --git a/Source/CustomSynth.h b/Source/CustomSynth.h index 51dfa18..d339295 100644 --- a/Source/CustomSynth.h +++ b/Source/CustomSynth.h @@ -24,5 +24,6 @@ public: private: TonalVoice* getVoiceIfShouldProcessInMonoMode(); + double calcArpeggioInterval(); Magical8bitPlug2AudioProcessor& processor; }; diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp index fae053e..39563ad 100644 --- a/Source/PluginProcessor.cpp +++ b/Source/PluginProcessor.cpp @@ -176,6 +176,17 @@ void Magical8bitPlug2AudioProcessor::setupVoice() } } +double Magical8bitPlug2AudioProcessor::getCurrentBPM() +{ + auto ph = getPlayHead(); + if (ph == NULL) { + return 120.0; + } + juce::AudioPlayHead::CurrentPositionInfo result; + ph->getCurrentPosition(result); + + return result.bpm > 0 ? result.bpm : 120.0; +} //============================================================================== const String Magical8bitPlug2AudioProcessor::getName() const diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h index bd82f6d..efc9ca1 100644 --- a/Source/PluginProcessor.h +++ b/Source/PluginProcessor.h @@ -72,6 +72,7 @@ public: //============================================================================== void setupVoice(); + double getCurrentBPM(); AudioProcessorValueTreeState parameters; SettingRefs settingRefs; diff --git a/Source/TonalVoice.cpp b/Source/TonalVoice.cpp index 62cd1fa..937cfd4 100644 --- a/Source/TonalVoice.cpp +++ b/Source/TonalVoice.cpp @@ -38,6 +38,7 @@ void TonalVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound arpeggioFrameTimer = 0; arpeggioFrameLength = 0; currentNumNoteBuffer = 0; + for (int i=0; i<10; i++) { noteBuffer[i] = 0; } } void TonalVoice::advanceControlFrame() @@ -69,7 +70,7 @@ void TonalVoice::calculateAngleDelta() break; } } - + double byWheel = settingRefs->vibratoIgnoresWheel() ? 1.0 : currentModWheelValue; double vibratoAmount = * (settingRefs->vibratoDepth) * sin (getVibratoPhase()) * byWheel; double noteNoInDouble = noteNumber @@ -262,4 +263,23 @@ void TonalVoice::onFrameAdvanced() autoBendDelta = 0; } } + + if (arpeggioFrameLength > 0) { + arpeggioFrameTimer += 1.0 / getSampleRate(); + + if (arpeggioFrameTimer >= arpeggioFrameLength) + { + currentArpeggioFrame++; + + if (currentArpeggioFrame >= currentNumNoteBuffer) { + currentArpeggioFrame = 0; + } + noteNumber = noteBuffer[currentArpeggioFrame]; + + while (arpeggioFrameTimer >= arpeggioFrameLength) + { + arpeggioFrameTimer -= arpeggioFrameLength; + } + } + } }; diff --git a/Source/TonalVoice.h b/Source/TonalVoice.h index 2acb0c0..93180a4 100644 --- a/Source/TonalVoice.h +++ b/Source/TonalVoice.h @@ -69,7 +69,7 @@ struct TonalVoice : public BaseVoice // The base for Pulse and Triangle double noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA = 440); void onFrameAdvanced() override; - + bool isArpeggioEnabled() { return arpeggioFrameLength > 0; }