Skip to content

Commit

Permalink
Merge pull request #5 from ghostintranslation/develop
Browse files Browse the repository at this point in the history
Polyphony improvements
  • Loading branch information
ghostintranslation authored May 6, 2021
2 parents ddddd6b + ce17a1a commit a0f5e3d
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 53 deletions.
141 changes: 106 additions & 35 deletions Synth/Synth.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Synth{
byte synthesis;
byte mode;
int parameter;
byte portamento = 255;
unsigned int arpTime;
elapsedMillis elapsedTime;
byte arpIndex;
Expand Down Expand Up @@ -160,27 +161,72 @@ inline void Synth::noteOn(byte channel, byte note, byte velocity){
bool foundOne = false;
int oldestVoice = 0;
unsigned long oldestVoiceTime = 0;
int closestVoice = 0;
unsigned long closestVoiceNote = sizeof(unsigned long);

switch (modes(getInstance()->mode)){
case SYNTH:
for (int i = 0; i < getInstance()->actualVoiceCount; i++) {
// Search for the oldest voice
if(getInstance()->voices[i]->last_played > oldestVoiceTime){
oldestVoiceTime = getInstance()->voices[i]->last_played;
oldestVoice = i;
if(getInstance()->portamento == 0){
for (int i = 0; i < getInstance()->actualVoiceCount; i++) {
// If no portamento we get an inactive voice to play the new note
// or else the oldest voice

// Search for the oldest voice
if(getInstance()->voices[i]->last_played > oldestVoiceTime){
oldestVoiceTime = getInstance()->voices[i]->last_played;
oldestVoice = i;
}

// Search for an inactive voice
if(!getInstance()->voices[i]->isActive()){
getInstance()->voices[i]->noteOn(note);
foundOne = true;
break;
}
}

// Search for an inactive voice
if(!getInstance()->voices[i]->isActive()){
getInstance()->voices[i]->noteOn(note);
foundOne = true;
break;
// No inactive voice then will take over the oldest note
if(!foundOne){
getInstance()->voices[oldestVoice]->noteOn(note);
}
}

// No inactive voice then will take over the oldest note
if(!foundOne){
getInstance()->voices[oldestVoice]->noteOn(note);
}else{
// If there is portamento then we find the voice with the closest note
// to play the new note, so that portamento is applied to it and it sounds better in polyphony

// Search first for a voice that is not being played and not active and not the same note as the new note
for (int i = 0; i < getInstance()->actualVoiceCount; i++) {
if(!getInstance()->voices[i]->isNotePlayed() &&
!getInstance()->voices[i]->isActive() &&
abs(getInstance()->voices[i]->getCurrentNote() - note) < closestVoiceNote &&
getInstance()->voices[i]->getCurrentNote() != note){
closestVoiceNote = getInstance()->voices[i]->getCurrentNote();
closestVoice = i;
foundOne = true;
break;
}
}

// If none is found then search for a voice that is just not being played and not the same note
if(!foundOne){
for (int i = 0; i < getInstance()->actualVoiceCount; i++) {
if(!getInstance()->voices[i]->isNotePlayed() &&
abs(getInstance()->voices[i]->getCurrentNote() - note) < closestVoiceNote &&
getInstance()->voices[i]->getCurrentNote() != note){
closestVoiceNote = getInstance()->voices[i]->getCurrentNote();
closestVoice = i;
foundOne = true;
}
}
}

// Make sure that the voice selected will not start with a note that is too far to avoid bad sounding portamento
if(getInstance()->voices[closestVoice]->getCurrentNote() - note > 12){
getInstance()->voices[closestVoice]->setCurrentNote(note + 12);
}else if(getInstance()->voices[closestVoice]->getCurrentNote() - note < -12){
getInstance()->voices[closestVoice]->setCurrentNote(note - 12);
}

getInstance()->voices[closestVoice]->noteOn(note);
}
break;
case ARP:
Expand Down Expand Up @@ -265,28 +311,43 @@ inline void Synth::update(){

if(this->clockUpdate > updateMillis){

for (int i = 0; i < voiceCount ; i++) {
for (byte i = 0; i < voiceCount ; i++) {
this->voices[i]->update();
}

this->clockUpdate = 0;
}

// Arp
if(modes(this->mode) == ARP){
if (this->elapsedTime >= this->arpTime) {

if(this->arpNotesPlaying > 0){
this->voices[0]->noteOn(this->arpNotes[this->arpIndex]);
// Arp
if(modes(this->mode) == ARP){
if (this->elapsedTime >= this->arpTime) {

if(this->arpNotesPlaying > 0){
if(this->arpIndex == 0){
if(this->voices[(this->arpNotesPlaying-1)]->isNotePlayed()){
this->voices[(this->arpNotesPlaying-1)]->noteOff();
}
}else{
if(this->voices[(this->arpIndex-1)]->isNotePlayed()){
this->voices[(this->arpIndex-1)]->noteOff();
}
}

this->arpIndex++;
if(this->arpIndex > this->arpNotesPlaying-1 ){
this->arpIndex = 0;
this->voices[this->arpIndex]->setGlide(255);
this->voices[this->arpIndex]->noteOn(this->arpNotes[this->arpIndex]);
}else{
for (byte i = 0; i < voiceCount ; i++) {
getInstance()->voices[i]->noteOff();
}
}

this->elapsedTime = 0;
this->arpIndex++;
if(this->arpIndex > this->arpNotesPlaying-1 ){
this->arpIndex = 0;
}

this->elapsedTime = 0;
}

this->clockUpdate = 0;
}
}

Expand All @@ -299,7 +360,7 @@ inline void Synth::onModeChange(byte inputIndex, float value, int diffToPrevious
map(
value,
getInstance()->device->getAnalogMinValue(),
getInstance()->device->getAnalogMaxValue(),
getInstance()->device->getAnalogMaxValue() - 100,
0,
2
),
Expand All @@ -324,9 +385,18 @@ inline void Synth::onModeChange(byte inputIndex, float value, int diffToPrevious
}

getInstance()->mode = mode;

for (int i = 0; i < voiceCount ; i++) {
getInstance()->voices[i]->setMode(mode);

switch(mode){
case ARP:
getInstance()->voices[i]->setGlide(255);
break;
default:
getInstance()->voices[i]->setGlide(getInstance()->portamento);
break;
}
}

if(modes(getInstance()->mode) == DRONE){
Expand Down Expand Up @@ -374,21 +444,22 @@ inline void Synth::onParamChange(byte inputIndex, float value, int diffToPreviou
switch(modes(getInstance()->mode)){
case SYNTH:
{
// Glide
int voiceGlide = constrain(
// Portamento
getInstance()->portamento = constrain(
map(
value,
getInstance()->device->getAnalogMinValue(),
getInstance()->device->getAnalogMaxValue(),
0,
255
255,
0
),
0,
255
);

// Set it to all voices
for (int i = 0; i < voiceCount ; i++) {
getInstance()->voices[i]->setGlide(voiceGlide);
getInstance()->voices[i]->setGlide(getInstance()->portamento);
}
}
break;
Expand Down
2 changes: 1 addition & 1 deletion Synth/Synth.ino
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
\_|| |\_/__) | _|_| | | | \| || |__)|__| | | _|_\_/| |
SYNTH
v1.2.0
v1.2.1
Support my work:
https://www.paypal.com/paypalme/ghostintranslation
Expand Down
42 changes: 25 additions & 17 deletions Synth/Voice.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ class Voice{
synthesis synth;
modes mode;
bool notePlayed;
unsigned int freq = 0;
unsigned int frequencyTarget = 0;
float freq = 0;
float frequencyTarget = 0;
byte currentNote = 0; // The midi note currently being played.
byte intervalGlide = 0;
byte updateMillis = 0;
Expand All @@ -67,7 +67,7 @@ class Voice{
void setAR(unsigned int attack, unsigned int release);
void setNotePlayed(bool notePlayed);
void setMode(byte mode);
void setFrequency(int freq);
void setFrequency(float freq);
void setFrequencyTarget(int freq);
void setModulatorFrequency(int freq);
void setModulatorAmplitude(float amp);
Expand All @@ -76,6 +76,7 @@ class Voice{
void setRelease(int rel);
void setGlide(byte glide);
void setUpdateMillis(byte updateMillis);
void setCurrentNote(byte midiNote);
// Getters
bool isActive();
bool isNotePlayed();
Expand Down Expand Up @@ -118,17 +119,19 @@ inline Voice::Voice(){
* Update
*/
inline void Voice::update(){
if(this->intervalGlide > 0){
if(this->frequencyTarget > this->freq){
this->freq += constrain((this->frequencyTarget - this->freq) / ((float)this->intervalGlide / (float)updateMillis), 0, (this->frequencyTarget - this->freq));
}else{
this->freq -= constrain((this->freq - this->frequencyTarget) / ((float)this->intervalGlide / (float)updateMillis), 0, (this->freq - this->frequencyTarget));
}

this->setFrequency(this->freq);
if(this->intervalGlide < 254){
if(this->frequencyTarget != this->freq){
this->freq += ((float)this->intervalGlide * (this->frequencyTarget - this->freq) / (float)255) / ((float)100 / (float)this->updateMillis);
}

if(roundf(this->freq * 100) / 100 == 0){
this->freq = 0;
}
}else{
this->setFrequency(this->frequencyTarget);
this->freq = this->frequencyTarget;
}

this->setFrequency(this->freq);
}

/**
Expand All @@ -154,7 +157,6 @@ inline void Voice::noteOn(byte midiNote) {
this->last_played = millis();
this->notePlayed=true;
this->frequencyTarget = 440.0 * powf(2.0, (float)(this->currentNote - 69) * 0.08333333);
this->update();
this->envelope->noteOn();
}

Expand All @@ -163,7 +165,8 @@ inline void Voice::noteOn(byte midiNote) {
*/
inline void Voice::noteOff() {
this->envelope->noteOff();
this->frequencyTarget = this->freq;
// this->frequencyTarget = this->freq;
this->notePlayed = false;
}

/**
Expand Down Expand Up @@ -216,7 +219,7 @@ inline void Voice::setMode(byte modeValue){
/**
* Set the frequency
*/
inline void Voice::setFrequency(int freq){
inline void Voice::setFrequency(float freq){
this->freq = freq;
this->sineFM->frequency(freq);
this->sawtoothFM->frequency(freq);
Expand All @@ -225,8 +228,8 @@ inline void Voice::setFrequency(int freq){
/**
* Set the trgeted frequency
*/
inline void Voice::setFrequencyTarget(int freq){
this->frequencyTarget = freq;
inline void Voice::setFrequencyTarget(int frequencyTarget){
this->frequencyTarget = frequencyTarget;
}

/**
Expand Down Expand Up @@ -276,6 +279,11 @@ inline byte Voice::getCurrentNote(){
return this->currentNote;
}

inline void Voice::setCurrentNote(byte midiNote){
this->currentNote = midiNote;
this->freq = 440.0 * powf(2.0, (float)(this->currentNote - 69) * 0.08333333);
}

inline void Voice::setUpdateMillis(byte updateMillis){
this->updateMillis = updateMillis;
}
Expand Down

0 comments on commit a0f5e3d

Please sign in to comment.