This commit is contained in:
Takeshi Yokemura 2021-05-21 00:01:53 +09:00
parent 0a5110c8d3
commit 5bc7fa85d0
4 changed files with 313 additions and 11 deletions

View File

@ -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) {

View File

@ -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;
}; };

View File

@ -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);

View File

@ -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);
}
*/
}; };