mirror of
https://github.com/yokemura/Magical8bitPlug2.git
synced 2025-06-03 00:58:05 -04:00
Legato
This commit is contained in:
parent
0a5110c8d3
commit
5bc7fa85d0
@ -10,32 +10,107 @@
|
|||||||
|
|
||||||
#include "CustomSynth.h"
|
#include "CustomSynth.h"
|
||||||
#include "BaseVoice.h"
|
#include "BaseVoice.h"
|
||||||
|
#include "TonalVoice.h"
|
||||||
#include "PluginProcessor.h"
|
#include "PluginProcessor.h"
|
||||||
|
|
||||||
CustomSynth::CustomSynth(Magical8bitPlug2AudioProcessor& p) : processor(p) {}
|
CustomSynth::CustomSynth(Magical8bitPlug2AudioProcessor& p) : processor(p) {}
|
||||||
|
|
||||||
void CustomSynth::noteOn(int midiChannel, int midiNoteNumber, float velocity) {
|
TonalVoice* CustomSynth::getVoiceIfShouldProcessInMonoMode() {
|
||||||
// Poly
|
|
||||||
if (!processor.settingRefs.isMonophonic()) {
|
if (!processor.settingRefs.isMonophonic()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (processor.settingRefs.monophonicBehavior() == kNonLegato) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try casting into TonalVoice* and return it if success(otherwise returns nullptr)
|
||||||
|
return dynamic_cast<TonalVoice *>(voices.getFirst());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomSynth::noteOn(int midiChannel, int midiNoteNumber, float velocity) {
|
||||||
|
TonalVoice *voice = getVoiceIfShouldProcessInMonoMode();
|
||||||
|
|
||||||
|
if (voice == nullptr) {
|
||||||
Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity);
|
Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mono
|
// Mono
|
||||||
auto voice = voices.getFirst();
|
|
||||||
|
|
||||||
if (voice == nullptr) {
|
// Start thread guard
|
||||||
return;
|
const ScopedLock sl (lock);
|
||||||
}
|
|
||||||
if (voice->isKeyDown()) {
|
if (voice->isKeyDown()) {
|
||||||
((BaseVoice*)voice)->changeNote(midiNoteNumber, velocity);
|
// Already key on
|
||||||
|
switch (processor.settingRefs.monophonicBehavior()) {
|
||||||
|
case kLegato:
|
||||||
|
voice->addLegatoNote(midiNoteNumber, velocity);
|
||||||
|
break;
|
||||||
|
case kArpeggioUp:
|
||||||
|
voice->addArpeggioNoteAscending(midiNoteNumber);
|
||||||
|
break;
|
||||||
|
case kArpeggioDown:
|
||||||
|
voice->addArpeggioNoteDescending(midiNoteNumber);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// no-op
|
||||||
|
break;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Synthesiser::noteOn(midiChannel, midiNoteNumber, velocity);
|
switch (processor.settingRefs.monophonicBehavior()) {
|
||||||
|
case kLegato:
|
||||||
|
// just start
|
||||||
|
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
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// no-op
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CustomSynth::noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff) {
|
void CustomSynth::noteOff(int midiChannel, int midiNoteNumber, float velocity, bool allowTailOff) {
|
||||||
Synthesiser::noteOff(midiChannel, midiNoteNumber, velocity, allowTailOff);
|
TonalVoice *voice = getVoiceIfShouldProcessInMonoMode();
|
||||||
|
|
||||||
|
if (voice == nullptr) {
|
||||||
|
Synthesiser::noteOff(midiChannel, midiNoteNumber, velocity, allowTailOff);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mono
|
||||||
|
|
||||||
|
// Start thread guard
|
||||||
|
const ScopedLock sl (lock);
|
||||||
|
|
||||||
|
if (!voice->isKeyDown()) {
|
||||||
|
// key is already off
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (processor.settingRefs.monophonicBehavior()) {
|
||||||
|
case kLegato:
|
||||||
|
{
|
||||||
|
int numBuffer = voice->removeLegatoNote(midiNoteNumber);
|
||||||
|
if (numBuffer < 1) {
|
||||||
|
Synthesiser::noteOff(midiChannel, voice->getCurrentlyPlayingNote(), velocity, allowTailOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kArpeggioUp:
|
||||||
|
case kArpeggioDown:
|
||||||
|
// remove arpeggio note and get # of remaining arpeggio notes
|
||||||
|
// if zero
|
||||||
|
// all notes off
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CustomSynth::allNotesOff (const int midiChannel, const bool allowTailOff) {
|
void CustomSynth::allNotesOff (const int midiChannel, const bool allowTailOff) {
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "../JuceLibraryCode/JuceHeader.h"
|
#include "../JuceLibraryCode/JuceHeader.h"
|
||||||
|
|
||||||
class Magical8bitPlug2AudioProcessor;
|
class Magical8bitPlug2AudioProcessor;
|
||||||
|
class TonalVoice;
|
||||||
|
|
||||||
class CustomSynth : public Synthesiser {
|
class CustomSynth : public Synthesiser {
|
||||||
public:
|
public:
|
||||||
@ -22,5 +23,6 @@ public:
|
|||||||
void allNotesOff (const int midiChannel, const bool allowTailOff) override;
|
void allNotesOff (const int midiChannel, const bool allowTailOff) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
TonalVoice* getVoiceIfShouldProcessInMonoMode();
|
||||||
Magical8bitPlug2AudioProcessor& processor;
|
Magical8bitPlug2AudioProcessor& processor;
|
||||||
};
|
};
|
||||||
|
@ -16,7 +16,9 @@
|
|||||||
// The base for pulse/triangle (Abstract)
|
// The base for pulse/triangle (Abstract)
|
||||||
//
|
//
|
||||||
//---------------------------------------------
|
//---------------------------------------------
|
||||||
TonalVoice::TonalVoice (SettingRefs* sRefs) : BaseVoice (sRefs) {}
|
TonalVoice::TonalVoice (SettingRefs* sRefs) : BaseVoice (sRefs) {
|
||||||
|
// testArpeggioNotes();
|
||||||
|
}
|
||||||
|
|
||||||
void TonalVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int currentPitchBendPosition)
|
void TonalVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound*, int currentPitchBendPosition)
|
||||||
{
|
{
|
||||||
@ -30,6 +32,12 @@ void TonalVoice::startNote (int midiNoteNumber, float velocity, SynthesiserSound
|
|||||||
float time = * (settingRefs->sweepTime);
|
float time = * (settingRefs->sweepTime);
|
||||||
currentAutoBendAmount = iniPitch;
|
currentAutoBendAmount = iniPitch;
|
||||||
autoBendDelta = -1.0 * iniPitch / (time * getSampleRate());
|
autoBendDelta = -1.0 * iniPitch / (time * getSampleRate());
|
||||||
|
|
||||||
|
portamentoTime = 0;
|
||||||
|
currentArpeggioFrame = 0;
|
||||||
|
arpeggioFrameTimer = 0;
|
||||||
|
arpeggioFrameLength = 0;
|
||||||
|
currentNumNoteBuffer = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TonalVoice::advanceControlFrame()
|
void TonalVoice::advanceControlFrame()
|
||||||
@ -90,6 +98,127 @@ void TonalVoice::controllerMoved (int type, int amount)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TonalVoice::setLegatoMode(double time) {
|
||||||
|
portamentoTime = time;
|
||||||
|
// currentNumNoteBuffer = 1;
|
||||||
|
// noteBuffer[0] = noteNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::addLegatoNote (int midiNoteNumber, float velocity) {
|
||||||
|
if (currentNumNoteBuffer >= 10) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push to last
|
||||||
|
// noteBuffer[currentNumNoteBuffer] = midiNoteNumber;
|
||||||
|
// currentNumNoteBuffer++;
|
||||||
|
|
||||||
|
int previousNoteNo = noteNumber;
|
||||||
|
BaseVoice::changeNote(midiNoteNumber, velocity);
|
||||||
|
|
||||||
|
// Portamento implementation is just applying auto bend
|
||||||
|
if (portamentoTime > 0) {
|
||||||
|
currentAutoBendAmount = (double)(previousNoteNo - midiNoteNumber);
|
||||||
|
autoBendDelta = -1.0 * currentAutoBendAmount / (portamentoTime * getSampleRate());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int TonalVoice::removeLegatoNote(int midiNoteNumber) {
|
||||||
|
// if (currentNumNoteBuffer==0) {
|
||||||
|
// return 0;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// int i;
|
||||||
|
// for (i=0; i<currentNumNoteBuffer; i++) {
|
||||||
|
// if (noteBuffer[i] == midiNoteNumber) break;
|
||||||
|
// }
|
||||||
|
// if (i == currentNumNoteBuffer) { // not found
|
||||||
|
// return currentNumNoteBuffer;
|
||||||
|
// }
|
||||||
|
// shiftNoteBuffer(i);
|
||||||
|
// currentNumNoteBuffer--;
|
||||||
|
|
||||||
|
if (midiNoteNumber == noteNumber) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TonalVoice::setArpeggioMode(double interval)
|
||||||
|
{
|
||||||
|
arpeggioFrameLength = interval;
|
||||||
|
arpeggioFrameTimer = 0;
|
||||||
|
currentArpeggioFrame = 0;
|
||||||
|
currentNumNoteBuffer = 1;
|
||||||
|
noteBuffer[0] = noteNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::addArpeggioNoteAscending(int midiNoteNumber)
|
||||||
|
{
|
||||||
|
if (currentNumNoteBuffer >= 10) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; i<currentNumNoteBuffer; i++) {
|
||||||
|
if (noteBuffer[i] > midiNoteNumber) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushNoteBuffer(i, midiNoteNumber);
|
||||||
|
|
||||||
|
currentNumNoteBuffer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::addArpeggioNoteDescending(int midiNoteNumber)
|
||||||
|
{
|
||||||
|
if (currentNumNoteBuffer >= 10) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int i;
|
||||||
|
for (i = 0; i<currentNumNoteBuffer; i++) {
|
||||||
|
if (noteBuffer[i] < midiNoteNumber) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
pushNoteBuffer(i, midiNoteNumber);
|
||||||
|
|
||||||
|
currentNumNoteBuffer++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of remaining arpeggio notes
|
||||||
|
int TonalVoice::removeArpeggioNote(int midiNoteNumber)
|
||||||
|
{
|
||||||
|
if (currentNumNoteBuffer==0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i=0; i<currentNumNoteBuffer; i++) {
|
||||||
|
if (noteBuffer[i] == midiNoteNumber) break;
|
||||||
|
}
|
||||||
|
if (i == currentNumNoteBuffer) { // not found
|
||||||
|
return currentNumNoteBuffer;
|
||||||
|
}
|
||||||
|
shiftNoteBuffer(i);
|
||||||
|
currentNumNoteBuffer--;
|
||||||
|
|
||||||
|
return currentNumNoteBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::pushNoteBuffer(int index, int value) {
|
||||||
|
for (int i=9; i>index; i--) {
|
||||||
|
noteBuffer[i] = noteBuffer[i-1];
|
||||||
|
}
|
||||||
|
noteBuffer[index] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TonalVoice::shiftNoteBuffer(int index) {
|
||||||
|
for (int i=index; i<9; i++) {
|
||||||
|
noteBuffer[i] = noteBuffer[i+1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double TonalVoice::noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA)
|
double TonalVoice::noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA)
|
||||||
{
|
{
|
||||||
return frequencyOfA * std::pow (2.0, (noteNoInDouble - 69) / 12.0);
|
return frequencyOfA * std::pow (2.0, (noteNoInDouble - 69) / 12.0);
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
||||||
{
|
{
|
||||||
|
|
||||||
TonalVoice (SettingRefs* sRefs);
|
TonalVoice (SettingRefs* sRefs);
|
||||||
float voltageForAngle (double angle) override = 0;
|
float voltageForAngle (double angle) override = 0;
|
||||||
|
|
||||||
@ -35,6 +34,18 @@ struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
|||||||
|
|
||||||
// Custom Pitch/Note states
|
// Custom Pitch/Note states
|
||||||
int currentPitchSequenceFrame = 0;
|
int currentPitchSequenceFrame = 0;
|
||||||
|
|
||||||
|
// Legato/Arpeggio
|
||||||
|
int noteBuffer[10];
|
||||||
|
int currentNumNoteBuffer = 0;
|
||||||
|
|
||||||
|
// Legato
|
||||||
|
double portamentoTime = 0;
|
||||||
|
|
||||||
|
// Arpeggio
|
||||||
|
int currentArpeggioFrame = 0;
|
||||||
|
double arpeggioFrameTimer = 0;
|
||||||
|
double arpeggioFrameLength = 0; // Unit: seconds. Set non-zero value to enable arpeggio
|
||||||
|
|
||||||
void startNote (int midiNoteNumber, float velocity,
|
void startNote (int midiNoteNumber, float velocity,
|
||||||
SynthesiserSound*, int currentPitchWheelPosition) override;
|
SynthesiserSound*, int currentPitchWheelPosition) override;
|
||||||
@ -43,7 +54,92 @@ struct TonalVoice : public BaseVoice // The base for Pulse and Triangle
|
|||||||
void pitchWheelMoved (int) override;
|
void pitchWheelMoved (int) override;
|
||||||
void controllerMoved (int, int) override;
|
void controllerMoved (int, int) override;
|
||||||
|
|
||||||
|
void setLegatoMode(double time);
|
||||||
|
void addLegatoNote (int midiNoteNumber, float velocity);
|
||||||
|
int removeLegatoNote(int midiNoteNumber);
|
||||||
|
|
||||||
|
void setArpeggioMode(double interval);
|
||||||
|
void addArpeggioNoteAscending(int midiNoteNumber);
|
||||||
|
void addArpeggioNoteDescending(int midiNoteNumber);
|
||||||
|
int removeArpeggioNote(int midiNoteNumber);
|
||||||
|
|
||||||
|
void pushNoteBuffer(int index, int value);
|
||||||
|
void shiftNoteBuffer(int index);
|
||||||
|
|
||||||
double noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA = 440);
|
double noteNoToHeltzDouble (double noteNoInDouble, const double frequencyOfA = 440);
|
||||||
|
|
||||||
void onFrameAdvanced() override;
|
void onFrameAdvanced() override;
|
||||||
|
|
||||||
|
bool isArpeggioEnabled() {
|
||||||
|
return arpeggioFrameLength > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void testArpeggioNotes() {
|
||||||
|
startNote(100, 1, NULL, 0);
|
||||||
|
|
||||||
|
setArpeggioMode(1.0);
|
||||||
|
jassert(isArpeggioEnabled());
|
||||||
|
jassert(arpeggioNotes[0] == 100);
|
||||||
|
jassert(currentNumArpeggioNotes == 1);
|
||||||
|
|
||||||
|
addArpeggioNoteAscending(90);
|
||||||
|
jassert(arpeggioNotes[0] == 90);
|
||||||
|
jassert(arpeggioNotes[1] == 100);
|
||||||
|
jassert(currentNumArpeggioNotes == 2);
|
||||||
|
|
||||||
|
addArpeggioNoteAscending(95);
|
||||||
|
jassert(arpeggioNotes[0] == 90);
|
||||||
|
jassert(arpeggioNotes[1] == 95);
|
||||||
|
jassert(arpeggioNotes[2] == 100);
|
||||||
|
jassert(currentNumArpeggioNotes == 3);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(92) == 3);
|
||||||
|
jassert(arpeggioNotes[0] == 90);
|
||||||
|
jassert(arpeggioNotes[1] == 95);
|
||||||
|
jassert(arpeggioNotes[2] == 100);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(100) == 2);
|
||||||
|
jassert(arpeggioNotes[0] == 90);
|
||||||
|
jassert(arpeggioNotes[1] == 95);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(90) == 1);
|
||||||
|
jassert(arpeggioNotes[0] == 95);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(95) == 0);
|
||||||
|
|
||||||
|
|
||||||
|
startNote(100, 1, NULL, 0);
|
||||||
|
|
||||||
|
setArpeggioMode(1.0);
|
||||||
|
jassert(isArpeggioEnabled());
|
||||||
|
jassert(arpeggioNotes[0] == 100);
|
||||||
|
jassert(currentNumArpeggioNotes == 1);
|
||||||
|
|
||||||
|
addArpeggioNoteDescending(90);
|
||||||
|
jassert(arpeggioNotes[0] == 100);
|
||||||
|
jassert(arpeggioNotes[1] == 90);
|
||||||
|
jassert(currentNumArpeggioNotes == 2);
|
||||||
|
|
||||||
|
addArpeggioNoteDescending(95);
|
||||||
|
jassert(arpeggioNotes[0] == 100);
|
||||||
|
jassert(arpeggioNotes[1] == 95);
|
||||||
|
jassert(arpeggioNotes[2] == 90);
|
||||||
|
jassert(currentNumArpeggioNotes == 3);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(92) == 3);
|
||||||
|
jassert(arpeggioNotes[0] == 100);
|
||||||
|
jassert(arpeggioNotes[1] == 95);
|
||||||
|
jassert(arpeggioNotes[2] == 90);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(100) == 2);
|
||||||
|
jassert(arpeggioNotes[0] == 95);
|
||||||
|
jassert(arpeggioNotes[1] == 90);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(90) == 1);
|
||||||
|
jassert(arpeggioNotes[0] == 95);
|
||||||
|
|
||||||
|
jassert(removeArpeggioNote(95) == 0);
|
||||||
|
}
|
||||||
|
*/
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user