commit a65b407cb85e94c4017d98642b93db951baca808
parent a6b23e8a7b9ac3f6a95b522833400d4365c25fa2
Author: Johannes Lorenz <j.git@lorenz-ho.me>
Date: Sat, 4 Apr 2020 23:38:35 +0200
Merge branch 'master' of https://git.code.sf.net/p/zynaddsubfx/code
Diffstat:
35 files changed, 650 insertions(+), 462 deletions(-)
diff --git a/src/Containers/NotePool.cpp b/src/Containers/NotePool.cpp
@@ -205,7 +205,7 @@ void NotePool::insertLegatoNote(note_t note, uint8_t sendto, SynthDescriptor des
};
//There should only be one pair of notes which are still playing
-void NotePool::applyLegato(note_t note, LegatoParams &par)
+void NotePool::applyLegato(note_t note, const LegatoParams &par)
{
for(auto &desc:activeDesc()) {
desc.note = note;
diff --git a/src/Containers/NotePool.h b/src/Containers/NotePool.h
@@ -122,7 +122,7 @@ class NotePool
void insertLegatoNote(note_t note, uint8_t sendto, SynthDescriptor desc);
void upgradeToLegato(void);
- void applyLegato(note_t note, LegatoParams &par);
+ void applyLegato(note_t note, const LegatoParams &par);
void makeUnsustainable(note_t note);
diff --git a/src/Misc/Master.cpp b/src/Misc/Master.cpp
@@ -973,15 +973,10 @@ void Master::noteOff(char chan, note_t note)
*/
void Master::polyphonicAftertouch(char chan, note_t note, char velocity)
{
- if(velocity) {
- for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
- if(chan == part[npart]->Prcvchn)
- if(part[npart]->Penabled)
- part[npart]->PolyphonicAftertouch(note, velocity, keyshift);
-
- }
- else
- this->noteOff(chan, note);
+ for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
+ if(chan == part[npart]->Prcvchn)
+ if(part[npart]->Penabled)
+ part[npart]->PolyphonicAftertouch(note, velocity);
}
/*
@@ -1023,6 +1018,20 @@ void Master::setController(char chan, int type, int par)
}
}
+/*
+ * Per note controllers
+ */
+void Master::setController(char chan, int type, note_t note, float value)
+{
+ if(frozenState)
+ return;
+
+ /* Send the controller to all part assigned to the channel */
+ for(int npart = 0; npart < NUM_MIDI_PARTS; ++npart)
+ if((chan == part[npart]->Prcvchn) && (part[npart]->Penabled != 0))
+ part[npart]->SetController(type, note, value, keyshift);
+}
+
void Master::vuUpdate(const float *outr, const float *outl)
{
//Peak computation (for vumeters)
diff --git a/src/Misc/Master.h b/src/Misc/Master.h
@@ -115,6 +115,7 @@ class Master
void noteOff(char chan, note_t note);
void polyphonicAftertouch(char chan, note_t note, char velocity);
void setController(char chan, int type, int par);
+ void setController(char chan, int type, note_t note, float value);
//void NRPN...
diff --git a/src/Misc/Microtonal.cpp b/src/Misc/Microtonal.cpp
@@ -221,7 +221,7 @@ void Microtonal::defaults()
Pmapping[i] = i;
for(int i = 0; i < MAX_OCTAVE_SIZE; ++i) {
- octave[i].tuning = powf(2, (i % octavesize + 1) / 12.0f);
+ octave[i].tuning_log2 = (i % octavesize + 1) / 12.0f;
octave[i].type = 1;
octave[i].x1 = (i % octavesize + 1) * 100;
octave[i].x2 = 0;
@@ -255,110 +255,132 @@ unsigned char Microtonal::getoctavesize() const
}
/*
- * Get the frequency according the note number
+ * Update the logarithmic power of two frequency according the note number
*/
-float Microtonal::getnotefreq(float note_log2_freq, int keyshift) const
+bool Microtonal::updatenotefreq_log2(float ¬e_log2_freq, int keyshift) const
{
note_t note = roundf(12.0f * note_log2_freq);
+ float freq_log2 = note_log2_freq;
// in this function will appears many times things like this:
// var=(a+b*100)%b
// I had written this way because if I use var=a%b gives unwanted results when a<0
// This is the same with divisions.
- if((Pinvertupdown != 0) && ((Pmappingenabled == 0) || (Penabled == 0)))
+ if((Pinvertupdown != 0) && ((Pmappingenabled == 0) || (Penabled == 0))) {
note = (int) Pinvertupdowncenter * 2 - note;
+ freq_log2 = Pinvertupdowncenter * (2.0f / 12.0f) - freq_log2;
+ }
- //compute global fine detune
- float globalfinedetunerap =
- powf(2.0f, (Pglobalfinedetune - 64.0f) / 1200.0f); //-64.0f .. 63.0f cents
+ /* compute global fine detune, -64.0f .. 63.0f cents */
+ const float globalfinedetunerap_log2 = (Pglobalfinedetune - 64.0f) / 1200.0f;
- if(Penabled == 0) //12tET
- return powf(2.0f, note_log2_freq +
- ((keyshift - PAnote) / 12.0f)) * PAfreq * globalfinedetunerap;
+ if(Penabled == 0) { /* 12tET */
+ freq_log2 += (keyshift - PAnote) / 12.0f;
+ }
+ else { /* Microtonal */
+ const int scaleshift =
+ ((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize;
+
+ /* compute the keyshift */
+ float rap_keyshift_log2;
+ if(keyshift != 0) {
+ const int kskey = (keyshift + (int)octavesize * 100) % octavesize;
+ const int ksoct = (keyshift + (int)octavesize * 100) / octavesize - 100;
+
+ rap_keyshift_log2 =
+ ((kskey == 0) ? 0.0f : octave[kskey - 1].tuning_log2) +
+ (octave[octavesize - 1].tuning_log2 * ksoct);
+ }
+ else {
+ rap_keyshift_log2 = 0.0f;
+ }
- int scaleshift =
- ((int)Pscaleshift - 64 + (int) octavesize * 100) % octavesize;
+ /* if the mapping is enabled */
+ if(Pmappingenabled) {
+ if((note < Pfirstkey) || (note > Plastkey))
+ goto failure;
+
+ /*
+ * Compute how many mapped keys are from middle note to reference note
+ * and find out the proportion between the freq. of middle note and "A" note
+ */
+ int tmp = PAnote - Pmiddlenote;
+ const bool minus = (tmp < 0);
+ if(minus)
+ tmp = -tmp;
+
+ int deltanote = 0;
+ for(int i = 0; i < tmp; ++i)
+ if(Pmapping[i % Pmapsize] >= 0)
+ deltanote++;
+
+ float rap_anote_middlenote_log2;
+ if(deltanote == 0) {
+ rap_anote_middlenote_log2 = 0.0f;
+ }
+ else {
+ rap_anote_middlenote_log2 =
+ octave[(deltanote - 1) % octavesize].tuning_log2 +
+ octave[octavesize - 1].tuning_log2 * ((deltanote - 1) / octavesize);
+ }
+ if(minus)
+ rap_anote_middlenote_log2 = -rap_anote_middlenote_log2;
+
+ /* Convert from note (midi) to degree (note from the tuning) */
+ int degoct =
+ (note - (int)Pmiddlenote + (int) Pmapsize
+ * 200) / (int)Pmapsize - 200;
+ int degkey = (note - Pmiddlenote + (int)Pmapsize * 100) % Pmapsize;
+ degkey = Pmapping[degkey];
+
+ /* check if key is not mapped */
+ if(degkey < 0)
+ goto failure;
+
+ /*
+ * Invert the keyboard upside-down if it is asked for
+ * TODO: do the right way by using Pinvertupdowncenter
+ */
+ if(Pinvertupdown != 0) {
+ degkey = octavesize - degkey - 1;
+ degoct = -degoct;
+ }
- //compute the keyshift
- float rap_keyshift = 1.0f;
- if(keyshift != 0) {
- int kskey = (keyshift + (int)octavesize * 100) % octavesize;
- int ksoct = (keyshift + (int)octavesize * 100) / octavesize - 100;
- rap_keyshift = (kskey == 0) ? (1.0f) : (octave[kskey - 1].tuning);
- rap_keyshift *= powf(octave[octavesize - 1].tuning, ksoct);
- }
+ degkey = degkey + scaleshift;
+ degoct += degkey / octavesize;
+ degkey %= octavesize;
- //if the mapping is enabled
- if(Pmappingenabled) {
- if((note < Pfirstkey) || (note > Plastkey))
- return -1.0f;
- //Compute how many mapped keys are from middle note to reference note
- //and find out the proportion between the freq. of middle note and "A" note
- int tmp = PAnote - Pmiddlenote, minus = 0;
- if(tmp < 0) {
- tmp = -tmp;
- minus = 1;
+ /* compute the logrithmic frequency of the note */
+ freq_log2 =
+ ((degkey == 0) ? 0.0f : octave[degkey - 1].tuning_log2) +
+ (octave[octavesize - 1].tuning_log2 * degoct) -
+ rap_anote_middlenote_log2;
}
- int deltanote = 0;
- for(int i = 0; i < tmp; ++i)
- if(Pmapping[i % Pmapsize] >= 0)
- deltanote++;
- float rap_anote_middlenote =
- (deltanote ==
- 0) ? (1.0f) : (octave[(deltanote - 1) % octavesize].tuning);
- if(deltanote)
- rap_anote_middlenote *=
- powf(octave[octavesize - 1].tuning,
- (deltanote - 1) / octavesize);
- if(minus)
- rap_anote_middlenote = 1.0f / rap_anote_middlenote;
-
- //Convert from note (midi) to degree (note from the tunning)
- int degoct =
- (note - (int)Pmiddlenote + (int) Pmapsize
- * 200) / (int)Pmapsize - 200;
- int degkey = (note - Pmiddlenote + (int)Pmapsize * 100) % Pmapsize;
- degkey = Pmapping[degkey];
- if(degkey < 0)
- return -1.0f; //this key is not mapped
-
- //invert the keyboard upside-down if it is asked for
- //TODO: do the right way by using Pinvertupdowncenter
- if(Pinvertupdown != 0) {
- degkey = octavesize - degkey - 1;
- degoct = -degoct;
+ else { /* if the mapping is disabled */
+ const int nt = note - PAnote + scaleshift;
+ const int ntkey = (nt + (int)octavesize * 100) % octavesize;
+ const int ntoct = (nt - ntkey) / octavesize;
+
+ freq_log2 =
+ octave[(ntkey + octavesize - 1) % octavesize].tuning_log2 +
+ octave[octavesize - 1].tuning_log2 * (ntkey ? ntoct : (ntoct - 1));
}
- //compute the frequency of the note
- degkey = degkey + scaleshift;
- degoct += degkey / octavesize;
- degkey %= octavesize;
-
- float freq = (degkey == 0) ? (1.0f) : octave[degkey - 1].tuning;
- freq *= powf(octave[octavesize - 1].tuning, degoct);
- freq *= PAfreq / rap_anote_middlenote;
- freq *= globalfinedetunerap;
if(scaleshift)
- freq /= octave[scaleshift - 1].tuning;
- return freq * rap_keyshift;
- }
- else { //if the mapping is disabled
- int nt = note - PAnote + scaleshift;
- int ntkey = (nt + (int)octavesize * 100) % octavesize;
- int ntoct = (nt - ntkey) / octavesize;
-
- float oct = octave[octavesize - 1].tuning;
- float freq =
- octave[(ntkey + octavesize - 1) % octavesize].tuning * powf(oct,
- ntoct)
- * PAfreq;
- if(!ntkey)
- freq /= oct;
- if(scaleshift)
- freq /= octave[scaleshift - 1].tuning;
- freq *= globalfinedetunerap;
- return freq * rap_keyshift;
+ freq_log2 -= octave[scaleshift - 1].tuning_log2;
+ freq_log2 += rap_keyshift_log2;
}
+
+ /* common part */
+ freq_log2 += log2f(PAfreq);
+ freq_log2 += globalfinedetunerap_log2;
+
+ /* update value */
+ note_log2_freq = freq_log2;
+ return true;
+
+failure:
+ return false;
}
bool Microtonal::operator==(const Microtonal µ) const
@@ -395,7 +417,7 @@ bool Microtonal::operator!=(const Microtonal µ) const
MCREQ(Pmapping[i]);
for(int i = 0; i < octavesize; ++i) {
- FMCREQ(octave[i].tuning);
+ FMCREQ(octave[i].tuning_log2);
MCREQ(octave[i].type);
MCREQ(octave[i].x1);
MCREQ(octave[i].x2);
@@ -418,8 +440,11 @@ bool Microtonal::operator!=(const Microtonal µ) const
*/
int Microtonal::linetotunings(OctaveTuning &octave, const char *line)
{
- int x1 = -1, x2 = -1, type = -1;
- float x = -1.0f, tmp, tuning = 1.0f;
+ int x1 = -1, x2 = -1;
+ int type;
+ float tmp;
+ float x = -1.0f;
+ float tuning_log2 = 0.0f;
if(strstr(line, "/") == NULL) {
if(strstr(line, ".") == NULL) { // M case (M=M/1)
sscanf(line, "%d", &x1);
@@ -443,7 +468,7 @@ int Microtonal::linetotunings(OctaveTuning &octave, const char *line)
}
if(x1 <= 0)
- x1 = 1; //not allow zero frequency sounds (consider 0 as 1)
+ x1 = 1; //do not allow zero frequency sounds (consider 0 as 1)
//convert to float if the number are too big
if((type == 2)
@@ -455,16 +480,18 @@ int Microtonal::linetotunings(OctaveTuning &octave, const char *line)
case 1:
x1 = (int) floor(x);
tmp = fmod(x, 1.0f);
- x2 = (int) (floor(tmp * 1e6));
- tuning = powf(2.0f, x / 1200.0f);
+ x2 = (int) floor(tmp * 1e6);
+ tuning_log2 = x / 1200.0f;
break;
case 2:
x = ((float)x1) / x2;
- tuning = x;
+ tuning_log2 = log2f(x);
break;
+ default:
+ return 1;
}
- octave.tuning = tuning;
+ octave.tuning_log2 = tuning_log2;
octave.type = type;
octave.x1 = x1;
octave.x2 = x2;
@@ -504,7 +531,7 @@ int Microtonal::texttotunings(const char *text)
return -2; //the input is empty
octavesize = nl;
for(int i = 0; i < octavesize; ++i) {
- octave[i].tuning = tmpoctave[i].tuning;
+ octave[i].tuning_log2 = tmpoctave[i].tuning_log2;
octave[i].type = tmpoctave[i].type;
octave[i].x1 = tmpoctave[i].x1;
octave[i].x2 = tmpoctave[i].x2;
@@ -620,7 +647,7 @@ int Microtonal::loadscl(SclInfo &scl, const char *filename)
scl.octavesize = nnotes;
for(int i = 0; i < scl.octavesize; ++i) {
- scl.octave[i].tuning = tmpoctave[i].tuning;
+ scl.octave[i].tuning_log2 = tmpoctave[i].tuning_log2;
scl.octave[i].type = tmpoctave[i].type;
scl.octave[i].x1 = tmpoctave[i].x1;
scl.octave[i].x2 = tmpoctave[i].x2;
@@ -730,8 +757,8 @@ void Microtonal::add2XML(XMLwrapper& xml) const
for(int i = 0; i < octavesize; ++i) {
xml.beginbranch("DEGREE", i);
if(octave[i].type == 1)
- xml.addparreal("cents", octave[i].tuning);
- ;
+ xml.addparreal("cents", powf(2.0f, octave[i].tuning_log2));
+
if(octave[i].type == 2) {
xml.addpar("numerator", octave[i].x1);
xml.addpar("denominator", octave[i].x2);
@@ -780,7 +807,8 @@ void Microtonal::getfromXML(XMLwrapper& xml)
if(xml.enterbranch("DEGREE", i) == 0)
continue;
octave[i].x2 = 0;
- octave[i].tuning = xml.getparreal("cents", octave[i].tuning);
+ octave[i].tuning_log2 = log2f(xml.getparreal("cents",
+ powf(2.0f, octave[i].tuning_log2))) / log2(2.0f);
octave[i].x1 = xml.getpar127("numerator", octave[i].x1);
octave[i].x2 = xml.getpar127("denominator", octave[i].x2);
@@ -788,10 +816,10 @@ void Microtonal::getfromXML(XMLwrapper& xml)
octave[i].type = 2;
else {
octave[i].type = 1;
- //populate fields for display
- float x = logf(octave[i].tuning) / LOG_2 * 1200.0f;
- octave[i].x1 = (int) floor(x);
- octave[i].x2 = (int) (floor((x-octave[i].x1) * 1.0e6));
+ /* populate fields for display */
+ const float x = octave[i].tuning_log2 * 1200.0f;
+ octave[i].x1 = (int) floorf(x);
+ octave[i].x2 = (int) (floorf((x-octave[i].x1) * 1.0e6));
}
diff --git a/src/Misc/Microtonal.h b/src/Misc/Microtonal.h
@@ -41,9 +41,12 @@ struct KbmInfo
struct OctaveTuning {
unsigned char type; //1 for cents or 2 for division
- // the real tuning (eg. +1.05946f for one halftone)
- // or 2.0f for one octave
- float tuning;
+ /*
+ * The real tuning in logarithmic power of two.
+ * For example 1/12 for one halftone and
+ * 1 for one octave.
+ */
+ float tuning_log2;
//the real tunning is x1/x2
unsigned int x1, x2;
@@ -66,10 +69,17 @@ class Microtonal
/**Destructor*/
~Microtonal();
void defaults();
+ /**Updates the logarithmic power of two frequency for a given note
+ */
+ bool updatenotefreq_log2(float ¬e_log2_freq, int keyshift) const;
/**Calculates the frequency for a given note
*/
- float getnotefreq(float note_log2_freq, int keyshift) const;
-
+ float getnotefreq(float note_log2_freq, int keyshift) const {
+ if (updatenotefreq_log2(note_log2_freq, keyshift))
+ return powf(2.0f, note_log2_freq);
+ else
+ return -1.0f;
+ };
//Parameters
/**if the keys are inversed (the pitch is lower to keys from the right direction)*/
diff --git a/src/Misc/Part.cpp b/src/Misc/Part.cpp
@@ -287,7 +287,7 @@ Part::Part(Allocator &alloc, const SYNTH_T &synth_, const AbsTime &time_,
}
killallnotes = false;
- oldfreq = -1.0f;
+ oldfreq_log2 = -1.0f;
cleanup();
@@ -469,9 +469,8 @@ static int kit_usage(const Part::Kit *kits, int note, int mode)
/*
* Note On Messages
*/
-bool Part::NoteOn(note_t note,
+bool Part::NoteOnInternal(note_t note,
unsigned char velocity,
- int masterkeyshift,
float note_log2_freq)
{
//Verify Basic Mode and sanity
@@ -490,7 +489,6 @@ bool Part::NoteOn(note_t note,
if(isMonoMode() || isLegatoMode()) {
monomemPush(note);
monomem[note].velocity = velocity;
- monomem[note].mkeyshift = masterkeyshift;
monomem[note].note_log2_freq = note_log2_freq;
} else if(!monomemEmpty())
@@ -504,31 +502,26 @@ bool Part::NoteOn(note_t note,
//Compute Note Parameters
const float vel = getVelocity(velocity, Pvelsns, Pveloffs);
- const int partkeyshift = (int)Pkeyshift - 64;
- const int keyshift = masterkeyshift + partkeyshift;
- const float notebasefreq = getBaseFreq(note_log2_freq, keyshift);
-
- if(notebasefreq < 0.0f)
- return false;
//Portamento
lastnote = note;
- if(oldfreq < 1.0f)
- oldfreq = notebasefreq;//this is only the first note is played
+
+ /* check if first note is played */
+ if(oldfreq_log2 < 0.0f)
+ oldfreq_log2 = note_log2_freq;
// For Mono/Legato: Force Portamento Off on first
// notes. That means it is required that the previous note is
// still held down or sustained for the Portamento to activate
// (that's like Legato).
- bool portamento = false;
- if(Ppolymode || isRunningNote)
- portamento = ctl.initportamento(oldfreq, notebasefreq, doingLegato);
+ const bool portamento = (Ppolymode || isRunningNote) &&
+ ctl.initportamento(oldfreq_log2, note_log2_freq, doingLegato);
- oldfreq = notebasefreq;
+ oldfreq_log2 = note_log2_freq;
//Adjust Existing Notes
if(doingLegato) {
- LegatoParams pars = {notebasefreq, vel, portamento, note_log2_freq, true, prng()};
+ LegatoParams pars = {vel, portamento, note_log2_freq, true, prng()};
notePool.applyLegato(note, pars);
return true;
}
@@ -543,7 +536,7 @@ bool Part::NoteOn(note_t note,
if(Pkitmode != 0 && !item.validNote(note))
continue;
- SynthParams pars{memory, ctl, synth, time, notebasefreq, vel,
+ SynthParams pars{memory, ctl, synth, time, vel,
portamento, note_log2_freq, false, prng()};
const int sendto = Pkitmode ? item.sendto() : 0;
@@ -604,14 +597,18 @@ void Part::NoteOff(note_t note) //release the key
}
void Part::PolyphonicAftertouch(note_t note,
- unsigned char velocity,
- int masterkeyshift)
+ unsigned char velocity)
{
- (void) masterkeyshift;
-
if(!Pnoteon || !inRange(note, Pminkey, Pmaxkey) || Pdrummode)
return;
+ /*
+ * Don't allow the velocity to reach zero.
+ * Keep it alive until note off.
+ */
+ if(velocity == 0)
+ velocity = 1;
+
// MonoMem stuff:
if(!Ppolymode) // if Poly is off
monomem[note].velocity = velocity; // Store this note's velocity.
@@ -715,6 +712,47 @@ void Part::SetController(unsigned int type, int par)
break;
}
}
+
+/*
+ * Per note controllers.
+ */
+void Part::SetController(unsigned int type, note_t note, float value,
+ int masterkeyshift)
+{
+ if(!Pnoteon || !inRange(note, Pminkey, Pmaxkey) || Pdrummode)
+ return;
+
+ switch (type) {
+ case C_aftertouch:
+ PolyphonicAftertouch(note, floorf(value));
+ break;
+ case C_pitch: {
+ if (getNoteLog2Freq(masterkeyshift, value) == false)
+ break;
+
+ /* Make sure MonoMem's frequency information is kept up to date */
+ if(!Ppolymode)
+ monomem[note].note_log2_freq = value;
+
+ for(auto &d:notePool.activeDesc()) {
+ if(d.note == note && d.playing())
+ for(auto &s:notePool.activeNotes(d))
+ s.note->setPitch(value);
+ }
+ break;
+ }
+ case C_filtercutoff:
+ for(auto &d:notePool.activeDesc()) {
+ if(d.note == note && d.playing())
+ for(auto &s:notePool.activeNotes(d))
+ s.note->setFilterCutoff(value);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
/*
* Release the sustained keys
*/
@@ -750,18 +788,19 @@ void Part::MonoMemRenote()
{
note_t mmrtempnote = monomemBack(); // Last list element.
monomemPop(mmrtempnote); // We remove it, will be added again in NoteOn(...).
- NoteOn(mmrtempnote,
+ NoteOnInternal(mmrtempnote,
monomem[mmrtempnote].velocity,
- monomem[mmrtempnote].mkeyshift,
monomem[mmrtempnote].note_log2_freq);
}
-float Part::getBaseFreq(float note_log2_freq, int keyshift) const
+bool Part::getNoteLog2Freq(int masterkeyshift, float ¬e_log2_freq)
{
- if(Pdrummode)
- return 440.0f * powf(2.0f, note_log2_freq - (69.0f / 12.0f));
- else
- return microtonal->getnotefreq(note_log2_freq, keyshift);
+ if(Pdrummode) {
+ note_log2_freq += log2f(440.0f) - 69.0f / 12.0f;
+ return true;
+ }
+ return microtonal->updatenotefreq_log2(note_log2_freq,
+ (int)Pkeyshift - 64 + masterkeyshift);
}
float Part::getVelocity(uint8_t velocity, uint8_t velocity_sense,
diff --git a/src/Misc/Part.h b/src/Misc/Part.h
@@ -42,20 +42,34 @@ class Part
// Midi commands implemented
+ //returns true when successful
+ bool getNoteLog2Freq(int masterkeyshift, float ¬e_log2_freq);
+
//returns true when note is successfully applied
bool NoteOn(note_t note, uint8_t vel, int shift) REALTIME {
- return (NoteOn(note, vel, shift, note / 12.0f));
+ float log2_freq = note / 12.0f;
+ return (getNoteLog2Freq(shift, log2_freq) &&
+ NoteOnInternal(note, vel, log2_freq));
+ };
+
+ //returns true when note is successfully applied
+ bool NoteOn(note_t note, uint8_t vel, int shift,
+ float log2_freq) REALTIME {
+ return (getNoteLog2Freq(shift, log2_freq) &&
+ NoteOnInternal(note, vel, log2_freq));
};
- bool NoteOn(note_t note,
+
+ //returns true when note is successfully applied
+ bool NoteOnInternal(note_t note,
unsigned char velocity,
- int masterkeyshift,
float note_log2_freq) REALTIME;
void NoteOff(note_t note) REALTIME;
void PolyphonicAftertouch(note_t note,
- unsigned char velocity,
- int masterkeyshift) REALTIME;
+ unsigned char velocity) REALTIME;
void AllNotesOff() REALTIME; //panic
void SetController(unsigned int type, int par) REALTIME;
+ void SetController(unsigned int type, note_t, float value,
+ int masterkeyshift) REALTIME;
void ReleaseSustainedKeys() REALTIME; //this is called when the sustain pedal is released
void ReleaseAllKeys() REALTIME; //this is called on AllNotesOff controller
@@ -164,7 +178,6 @@ class Part
private:
void MonoMemRenote(); // MonoMem stuff.
- float getBaseFreq(float note_log2_freq, int keyshift) const;
float getVelocity(uint8_t velocity, uint8_t velocity_sense,
uint8_t velocity_offset) const;
void verifyKeyMode(void);
@@ -191,15 +204,14 @@ class Part
short monomemnotes[256]; // A list to remember held notes.
struct {
unsigned char velocity;
- int mkeyshift; // I'm not sure masterkeyshift should be remembered.
float note_log2_freq;
} monomem[256];
/* 256 is to cover all possible note values.
monomem[] is used in conjunction with the list to
- store the velocity and masterkeyshift values of a given note (the list only store note values).
+ store the velocity and logarithmic frequency values of a given note.
For example 'monomem[note].velocity' would be the velocity value of the note 'note'.*/
- float oldfreq; //this is used for portamento
+ float oldfreq_log2; //this is used for portamento
Microtonal *microtonal;
FFTwrapper *fft;
WatchManager *wm;
diff --git a/src/Nio/InMgr.cpp b/src/Nio/InMgr.cpp
@@ -45,6 +45,13 @@ ostream &operator<<(ostream &out, const MidiEvent &ev)
<< " value(" << ev.value << ")";
break;
+ case M_FLOAT_CTRL:
+ out << "MidiNote: controller(" << ev.num << ")\n"
+ << " channel(" << ev.channel << ")\n"
+ << " note(" << ev.value << ")\n"
+ << " log2_value(" << ev.log2_freq << ")";
+ break;
+
case M_PGMCHANGE:
out << "PgmChange: program(" << ev.num << ")\n"
<< " channel(" << ev.channel << ")";
@@ -120,6 +127,10 @@ void InMgr::flush(unsigned frameStart, unsigned frameStop)
master->setController(ev.channel, ev.num, ev.value);
break;
+ case M_FLOAT_CTRL:
+ master->setController(ev.channel, ev.num, ev.value, ev.log2_freq);
+ break;
+
case M_PGMCHANGE:
for(int i=0; i < NUM_MIDI_PARTS; ++i) {
//set the program of the parts assigned to the midi channel
diff --git a/src/Nio/InMgr.h b/src/Nio/InMgr.h
@@ -23,7 +23,8 @@ enum midi_type {
M_CONTROLLER = 2, // for controller
M_PGMCHANGE = 3, // for program change
M_PRESSURE = 4, // for polyphonic aftertouch
- M_FLOAT_NOTE = 5 // for floating point note
+ M_FLOAT_NOTE = 5, // for floating point note
+ M_FLOAT_CTRL = 6 // for floating point controller
};
struct MidiEvent {
@@ -33,7 +34,7 @@ struct MidiEvent {
int num; //note, controller or program number
int value; //velocity or controller value
int time; //time offset of event (used only in jack->jack case at the moment)
- float log2_freq; //type=5 for logarithmic representation of note
+ float log2_freq; //type=5,6 for logarithmic representation of note/parameter
};
//super simple class to manage the inputs
diff --git a/src/Nio/JackEngine.cpp b/src/Nio/JackEngine.cpp
@@ -382,61 +382,78 @@ void JackEngine::handleMidi(unsigned long frames)
void *midi_buf = jack_port_get_buffer(midi.inport, frames);
jack_midi_event_t jack_midi_event;
jack_nframes_t event_index = 0;
- unsigned char *midi_data;
+ unsigned char buf[3];
unsigned char type;
while(jack_midi_event_get(&jack_midi_event, midi_buf,
event_index++) == 0) {
- MidiEvent ev;
- midi_data = jack_midi_event.buffer;
- type = midi_data[0] & 0xF0;
- ev.channel = midi_data[0] & 0x0F;
+ MidiEvent ev = {};
+
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, jack_midi_event.buffer,
+ std::min(sizeof(buf), jack_midi_event.size));
+
+ /* make sure the values are within range */
+ buf[1] &= 0x7F;
+ buf[2] &= 0x7F;
+ type = buf[0] & 0xF0;
+ ev.channel = buf[0] & 0x0F;
ev.time = midi.jack_sync ? jack_midi_event.time : 0;
switch(type) {
case 0x80: /* note-off */
ev.type = M_NOTE;
- ev.num = midi_data[1];
+ ev.num = buf[1];
ev.value = 0;
InMgr::getInstance().putEvent(ev);
break;
case 0x90: /* note-on */
ev.type = M_NOTE;
- ev.num = midi_data[1];
- ev.value = midi_data[2];
+ ev.num = buf[1];
+ ev.value = buf[2];
InMgr::getInstance().putEvent(ev);
break;
case 0xA0: /* pressure, aftertouch */
ev.type = M_PRESSURE;
- ev.num = midi_data[1];
- ev.value = midi_data[2];
+ ev.num = buf[1];
+ ev.value = buf[2];
InMgr::getInstance().putEvent(ev);
break;
case 0xB0: /* controller */
ev.type = M_CONTROLLER;
- ev.num = midi_data[1];
- ev.value = midi_data[2];
+ ev.num = buf[1];
+ ev.value = buf[2];
InMgr::getInstance().putEvent(ev);
break;
case 0xC0: /* program change */
ev.type = M_PGMCHANGE;
- ev.num = midi_data[1];
- ev.value = 0;
+ ev.num = buf[1];
InMgr::getInstance().putEvent(ev);
break;
case 0xE0: /* pitch bend */
ev.type = M_CONTROLLER;
ev.num = C_pitchwheel;
- ev.value = ((midi_data[2] << 7) | midi_data[1]) - 8192;
+ ev.value = ((buf[2] << 7) | buf[1]) - 8192;
InMgr::getInstance().putEvent(ev);
break;
- /* XXX TODO: handle MSB/LSB controllers and RPNs and NRPNs */
+ default:
+ for (size_t x = 0; x < jack_midi_event.size; x += 3) {
+ size_t y = jack_midi_event.size - x;
+ if (y >= 3) {
+ memcpy(buf, (uint8_t *)jack_midi_event.buffer + x, 3);
+ } else {
+ memset(buf, 0, sizeof(buf));
+ memcpy(buf, (uint8_t *)jack_midi_event.buffer + x, y);
+ }
+ midiProcess(buf[0], buf[1], buf[2]);
+ }
+ break;
}
}
}
diff --git a/src/Nio/MidiIn.cpp b/src/Nio/MidiIn.cpp
@@ -65,20 +65,43 @@ void MidiIn::midiProcess(unsigned char head,
if (sysex_offset >= 10 &&
sysex_data[1] == 0x0A &&
sysex_data[2] == 0x55) {
- ev.type = M_FLOAT_NOTE;
ev.channel = sysex_data[3] & 0x0F;
- ev.num = sysex_data[4];
- ev.value = sysex_data[5];
- ev.log2_freq = (sysex_data[6] +
+ ev.log2_freq = sysex_data[6] +
(sysex_data[7] / (128.0f)) +
(sysex_data[8] / (128.0f * 128.0f)) +
- (sysex_data[9] / (128.0f * 128.0f * 128.0f))
- ) / 12.0f;
+ (sysex_data[9] / (128.0f * 128.0f * 128.0f));
+
+ switch (sysex_data[3] >> 4) {
+ case 0: /* Note ON */
+ ev.type = M_FLOAT_NOTE;
+ ev.num = sysex_data[4];
+ ev.value = sysex_data[5];
+ ev.log2_freq /= 12.0f;
+ break;
+ case 1: /* Pressure, Aftertouch */
+ ev.type = M_FLOAT_CTRL;
+ ev.num = C_aftertouch;
+ ev.value = sysex_data[4];
+ break;
+ case 2: /* Controller */
+ ev.type = M_FLOAT_CTRL;
+ ev.num = sysex_data[5];
+ ev.value = sysex_data[4];
+ break;
+ case 3: /* Absolute pitch */
+ ev.type = M_FLOAT_CTRL;
+ ev.num = C_pitch;
+ ev.value = sysex_data[4];
+ ev.log2_freq /= 12.0f;
+ break;
+ default:
+ return;
+ }
InMgr::getInstance().putEvent(ev);
}
return; /* message complete */
} else {
- return; /* wait for more data */
+ return; /* wait for more data */
}
}
switch(head & 0xf0) {
diff --git a/src/Params/Controller.cpp b/src/Params/Controller.cpp
@@ -131,11 +131,10 @@ void Controller::defaults()
portamento.updowntimestretch = 64;
portamento.pitchthresh = 3;
portamento.pitchthreshtype = 1;
- portamento.noteusing = -1;
resonancecenter.depth = 64;
resonancebandwidth.depth = 64;
- initportamento(440.0f, 440.0f, false); // Now has a third argument
+ initportamento(log2f(440.0f), log2f(440.0f), false);
setportamento(0);
}
@@ -270,8 +269,8 @@ void Controller::setportamento(int value)
portamento.portamento = ((value < 64) ? 0 : 1);
}
-int Controller::initportamento(float oldfreq,
- float newfreq,
+int Controller::initportamento(float oldfreq_log2,
+ float newfreq_log2,
bool legatoflag)
{
portamento.x = 0.0f;
@@ -280,59 +279,47 @@ int Controller::initportamento(float oldfreq,
if(portamento.portamento == 0)
return 0;
}
- else // No legato, do the original if...return
- if((portamento.used != 0) || (portamento.portamento == 0))
- return 0;
- ;
+ else { // No legato, do the original if...return
+ if((portamento.used != 0) || (portamento.portamento == 0))
+ return 0;
+ }
float portamentotime = powf(100.0f, portamento.time / 127.0f) / 50.0f; //portamento time in seconds
+ const float deltafreq_log2 = oldfreq_log2 - newfreq_log2;
+ const float absdeltaf_log2 = fabsf(deltafreq_log2);
if(portamento.proportional) {
- //If there is a min(float,float) and a max(float,float) then they
- //could be used here
- //Linear functors could also make this nicer
- if(oldfreq > newfreq) //2 is the center of propRate
- portamentotime *=
- powf(oldfreq / newfreq
- / (portamento.propRate / 127.0f * 3 + .05),
- (portamento.propDepth / 127.0f * 1.6f + .2));
- else //1 is the center of propDepth
- portamentotime *=
- powf(newfreq / oldfreq
- / (portamento.propRate / 127.0f * 3 + .05),
- (portamento.propDepth / 127.0f * 1.6f + .2));
+ const float absdeltaf = powf(2.0f, absdeltaf_log2);
+
+ portamentotime *= powf(absdeltaf
+ / (portamento.propRate / 127.0f * 3 + .05),
+ (portamento.propDepth / 127.0f * 1.6f + .2));
}
- if((portamento.updowntimestretch >= 64) && (newfreq < oldfreq)) {
+ if((portamento.updowntimestretch >= 64) && (newfreq_log2 < oldfreq_log2)) {
if(portamento.updowntimestretch == 127)
return 0;
portamentotime *= powf(0.1f,
(portamento.updowntimestretch - 64) / 63.0f);
}
- if((portamento.updowntimestretch < 64) && (newfreq > oldfreq)) {
+ if((portamento.updowntimestretch < 64) && (newfreq_log2 > oldfreq_log2)) {
if(portamento.updowntimestretch == 0)
return 0;
portamentotime *= powf(0.1f,
(64.0f - portamento.updowntimestretch) / 64.0f);
}
- //printf("%f->%f : Time %f\n",oldfreq,newfreq,portamentotime);
-
portamento.dx = synth.buffersize_f / (portamentotime * synth.samplerate_f);
- portamento.origfreqrap = oldfreq / newfreq;
-
- float tmprap = ((portamento.origfreqrap > 1.0f) ?
- (portamento.origfreqrap) :
- (1.0f / portamento.origfreqrap));
+ portamento.origfreqdelta_log2 = deltafreq_log2;
- float thresholdrap = powf(2.0f, portamento.pitchthresh / 12.0f);
- if((portamento.pitchthreshtype == 0) && (tmprap - 0.00001f > thresholdrap))
+ const float threshold_log2 = portamento.pitchthresh / 12.0f;
+ if((portamento.pitchthreshtype == 0) && (absdeltaf_log2 - 0.00001f > threshold_log2))
return 0;
- if((portamento.pitchthreshtype == 1) && (tmprap + 0.00001f < thresholdrap))
+ if((portamento.pitchthreshtype == 1) && (absdeltaf_log2 + 0.00001f < threshold_log2))
return 0;
- portamento.used = 1;
- portamento.freqrap = portamento.origfreqrap;
+ portamento.used = 1;
+ portamento.freqdelta_log2 = deltafreq_log2;
return 1;
}
@@ -346,8 +333,8 @@ void Controller::updateportamento()
portamento.x = 1.0f;
portamento.used = 0;
}
- portamento.freqrap =
- (1.0f - portamento.x) * portamento.origfreqrap + portamento.x;
+ portamento.freqdelta_log2 =
+ (1.0f - portamento.x) * portamento.origfreqdelta_log2;
}
diff --git a/src/Params/Controller.h b/src/Params/Controller.h
@@ -169,12 +169,11 @@ class Controller
unsigned char updowntimestretch;
/**this value is used to compute the actual portamento
*
- * This is a multiplyer to change the frequency of the newer
- * frequency to fit the profile of the portamento.
+ * This is the logarithmic power of two frequency
+ * adjustment of the newer frequency to fit the profile of
+ * the portamento.
* This will be linear with respect to x.*/
- float freqrap;
- /**this is used by the Part for knowing which note uses the portamento*/
- int noteusing;
+ float freqdelta_log2;
/**if a the portamento is used by a note
* \todo see if this can be a bool*/
int used;
@@ -185,8 +184,8 @@ class Controller
float x;
/**dx is the increment to x when updateportamento is called*/
float dx;
- /** this is used for computing oldfreq value from x*/
- float origfreqrap;
+ /** this is used for computing freqdelta_log2 value from x*/
+ float origfreqdelta_log2;
} portamento;
struct { //Resonance Center Frequency
diff --git a/src/Params/EnvelopeParams.cpp b/src/Params/EnvelopeParams.cpp
@@ -329,7 +329,7 @@ void EnvelopeParams::init(zyn::consumer_location_t _loc)
case ad_voice_filter: ADSRinit_filter(90, 0.025, 40, 0.025, 0.009, 40); break;
case ad_voice_fm_freq: ASRinit(20, 3.62, 40, 1.876); break;
case ad_voice_fm_amp: ADSRinit(80, 90, 127, 100); break;
- case sub_freq: ASRinit(0.01, 0.025, 64, 0.5); break;
+ case sub_freq: ASRinit(30, 0.025, 64, 0.5); break;
case sub_bandwidth: ASRinit_bw(100, 0.97, 64, 0.5); break;
default: throw std::logic_error("Invalid envelope consumer location");
};
diff --git a/src/Params/FilterParams.cpp b/src/Params/FilterParams.cpp
@@ -469,7 +469,7 @@ float FilterParams::getq() const
}
float FilterParams::getfreqtracking(float notefreq) const
{
- return log2f(notefreq / 440.0f) * (freqtracking / 100.0);
+ return log2f(notefreq / 440.0f) * (freqtracking / 100.0f);
}
float FilterParams::getgain() const
diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp
@@ -31,7 +31,7 @@
#define LENGTHOF(x) ((int)(sizeof(x)/sizeof(x[0])))
namespace zyn {
-ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars,
+ADnote::ADnote(ADnoteParameters *pars_, const SynthParams &spars,
WatchManager *wm, const char *prefix)
:SynthNote(spars), watch_be4_add(wm, prefix, "noteout/be4_mix"), watch_after_add(wm,prefix,"noteout/after_mix"),
watch_punch(wm, prefix, "noteout/punch"), watch_legato(wm, prefix, "noteout/legato"), pars(*pars_)
@@ -46,7 +46,6 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars,
portamento = spars.portamento;
note_log2_freq = spars.note_log2_freq;
NoteEnabled = ON;
- basefreq = spars.frequency;
velocity = spars.velocity;
initial_seed = spars.seed;
current_prng_state = spars.seed;
@@ -75,7 +74,7 @@ ADnote::ADnote(ADnoteParameters *pars_, SynthParams &spars,
pars.GlobalPar.PPunchVelocitySensing));
float time =
powf(10, 3.0f * pars.GlobalPar.PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms
- float stretch = powf(440.0f / spars.frequency,
+ float stretch = powf(440.0f / powf(2.0f, spars.note_log2_freq),
pars.GlobalPar.PPunchStretch / 64.0f);
NoteGlobalPar.Punch.dt = 1.0f / (time * synth.samplerate_f * stretch);
}
@@ -425,6 +424,8 @@ void ADnote::setupVoiceMod(int nvoice, bool first_run)
{
auto ¶m = pars.VoicePar[nvoice];
auto &voice = NoteVoicePar[nvoice];
+ float FMVolume;
+
if (param.Type != 0)
voice.FMEnabled = NONE;
else
@@ -496,27 +497,28 @@ void ADnote::setupVoiceMod(int nvoice, bool first_run)
case PW_MOD:
fmvoldamp = powf(440.0f / getvoicebasefreq(nvoice),
param.PFMVolumeDamp / 64.0f);
- voice.FMVolume = (expf(fmvolume_ * FM_AMP_MULTIPLIER) - 1.0f)
+ FMVolume = (expf(fmvolume_ * FM_AMP_MULTIPLIER) - 1.0f)
* fmvoldamp * 4.0f;
break;
case FREQ_MOD:
- voice.FMVolume = (expf(fmvolume_ * FM_AMP_MULTIPLIER) - 1.0f)
+ FMVolume = (expf(fmvolume_ * FM_AMP_MULTIPLIER) - 1.0f)
* fmvoldamp * 4.0f;
break;
default:
if(fmvoldamp > 1.0f)
fmvoldamp = 1.0f;
- voice.FMVolume = fmvolume_ * fmvoldamp;
+ FMVolume = fmvolume_ * fmvoldamp;
+ break;
}
//Voice's modulator velocity sensing
- NoteVoicePar[nvoice].FMVolume *=
+ voice.FMVolume = FMVolume *
VelF(velocity, pars.VoicePar[nvoice].PFMVelocityScaleFunction);
}
SynthNote *ADnote::cloneLegato(void)
{
- SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity,
+ SynthParams sp{memory, ctl, synth, time, velocity,
(bool)portamento, legato.param.note_log2_freq, true,
initial_seed };
return memory.alloc<ADnote>(&pars, sp);
@@ -526,7 +528,7 @@ SynthNote *ADnote::cloneLegato(void)
// initparameters() stuck together with some lines removed so that it
// only alter the already playing note (to perform legato). It is
// possible I left stuff that is not required for this.
-void ADnote::legatonote(LegatoParams lpars)
+void ADnote::legatonote(const LegatoParams &lpars)
{
//ADnoteParameters &pars = *partparams;
// Manage legato stuff
@@ -535,22 +537,22 @@ void ADnote::legatonote(LegatoParams lpars)
portamento = lpars.portamento;
note_log2_freq = lpars.note_log2_freq;
- basefreq = lpars.frequency;
initial_seed = lpars.seed;
current_prng_state = lpars.seed;
- if(velocity > 1.0f)
+ if(lpars.velocity > 1.0f)
velocity = 1.0f;
- velocity = lpars.velocity;
+ else
+ velocity = lpars.velocity;
+
+ const float basefreq = powf(2.0f, note_log2_freq);
NoteGlobalPar.Detune = getdetune(pars.GlobalPar.PDetuneType,
pars.GlobalPar.PCoarseDetune,
pars.GlobalPar.PDetune);
bandwidthDetuneMultiplier = pars.getBandwidthDetuneMultiplier();
- if(pars.GlobalPar.PPanning == 0) {
- NoteGlobalPar.Panning = getRandomFloat();
- } else
+ if(pars.GlobalPar.PPanning)
NoteGlobalPar.Panning = pars.GlobalPar.PPanning / 128.0f;
NoteGlobalPar.Filter->updateSense(velocity,
@@ -559,112 +561,94 @@ void ADnote::legatonote(LegatoParams lpars)
for(int nvoice = 0; nvoice < NUM_VOICES; ++nvoice) {
- if(NoteVoicePar[nvoice].Enabled == OFF)
+ auto &voice = NoteVoicePar[nvoice];
+ float FMVolume;
+
+ if(voice.Enabled == OFF)
continue; //(gf) Stay the same as first note in legato.
- NoteVoicePar[nvoice].fixedfreq = pars.VoicePar[nvoice].Pfixedfreq;
- NoteVoicePar[nvoice].fixedfreqET = pars.VoicePar[nvoice].PfixedfreqET;
+ voice.fixedfreq = pars.VoicePar[nvoice].Pfixedfreq;
+ voice.fixedfreqET = pars.VoicePar[nvoice].PfixedfreqET;
//use the Globalpars.detunetype if the detunetype is 0
if(pars.VoicePar[nvoice].PDetuneType != 0) {
- NoteVoicePar[nvoice].Detune = getdetune(
+ voice.Detune = getdetune(
pars.VoicePar[nvoice].PDetuneType,
pars.VoicePar[nvoice].PCoarseDetune,
8192); //coarse detune
- NoteVoicePar[nvoice].FineDetune = getdetune(
+ voice.FineDetune = getdetune(
pars.VoicePar[nvoice].PDetuneType,
0,
pars.VoicePar[nvoice].PDetune); //fine detune
}
else {
- NoteVoicePar[nvoice].Detune = getdetune(
+ voice.Detune = getdetune(
pars.GlobalPar.PDetuneType,
pars.VoicePar[nvoice].PCoarseDetune,
8192); //coarse detune
- NoteVoicePar[nvoice].FineDetune = getdetune(
+ voice.FineDetune = getdetune(
pars.GlobalPar.PDetuneType,
0,
pars.VoicePar[nvoice].PDetune); //fine detune
}
if(pars.VoicePar[nvoice].PFMDetuneType != 0)
- NoteVoicePar[nvoice].FMDetune = getdetune(
+ voice.FMDetune = getdetune(
pars.VoicePar[nvoice].PFMDetuneType,
pars.VoicePar[nvoice].PFMCoarseDetune,
pars.VoicePar[nvoice].PFMDetune);
else
- NoteVoicePar[nvoice].FMDetune = getdetune(
+ voice.FMDetune = getdetune(
pars.GlobalPar.PDetuneType,
pars.VoicePar[nvoice].PFMCoarseDetune,
pars.VoicePar[nvoice].PFMDetune);
- //Get the voice's oscil or external's voice oscil
- int vc = nvoice;
- if(pars.VoicePar[nvoice].Pextoscil != -1)
- vc = pars.VoicePar[nvoice].Pextoscil;
- if(!pars.GlobalPar.Hrandgrouping)
- pars.VoicePar[vc].OscilSmp->newrandseed(getRandomUint());
-
- pars.VoicePar[vc].OscilSmp->get(NoteVoicePar[nvoice].OscilSmp,
- getvoicebasefreq(nvoice),
- pars.VoicePar[nvoice].Presonance); //(gf)Modif of the above line.
-
- //I store the first elements to the last position for speedups
- for(int i = 0; i < OSCIL_SMP_EXTRA_SAMPLES; ++i)
- NoteVoicePar[nvoice].OscilSmp[synth.oscilsize
- + i] =
- NoteVoicePar[nvoice].OscilSmp[i];
-
- auto &voiceFilter = NoteVoicePar[nvoice].Filter;
+ auto &voiceFilter = voice.Filter;
if(voiceFilter) {
const auto &vce = pars.VoicePar[nvoice];
voiceFilter->updateSense(velocity, vce.PFilterVelocityScale,
vce.PFilterVelocityScaleFunction);
}
- NoteVoicePar[nvoice].filterbypass =
+ voice.filterbypass =
pars.VoicePar[nvoice].Pfilterbypass;
- NoteVoicePar[nvoice].FMVoice = pars.VoicePar[nvoice].PFMVoice;
+ voice.FMVoice = pars.VoicePar[nvoice].PFMVoice;
//Compute the Voice's modulator volume (incl. damping)
float fmvoldamp = powf(440.0f / getvoicebasefreq(nvoice),
pars.VoicePar[nvoice].PFMVolumeDamp / 64.0f
- 1.0f);
- switch(NoteVoicePar[nvoice].FMEnabled) {
+ switch(voice.FMEnabled) {
case PHASE_MOD:
case PW_MOD:
fmvoldamp =
powf(440.0f / getvoicebasefreq(
nvoice), pars.VoicePar[nvoice].PFMVolumeDamp
/ 64.0f);
- NoteVoicePar[nvoice].FMVolume =
+ FMVolume =
(expf(pars.VoicePar[nvoice].FMvolume / 100.0f
* FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f;
break;
case FREQ_MOD:
- NoteVoicePar[nvoice].FMVolume =
+ FMVolume =
(expf(pars.VoicePar[nvoice].FMvolume / 100.0f
* FM_AMP_MULTIPLIER) - 1.0f) * fmvoldamp * 4.0f;
break;
default:
if(fmvoldamp > 1.0f)
fmvoldamp = 1.0f;
- NoteVoicePar[nvoice].FMVolume =
+ FMVolume =
pars.VoicePar[nvoice].FMvolume
/ 100.0f * fmvoldamp;
+ break;
}
//Voice's modulator velocity sensing
- NoteVoicePar[nvoice].FMVolume *=
+ voice.FMVolume = FMVolume *
VelF(velocity,
pars.VoicePar[nvoice].PFMVelocityScaleFunction);
-
- NoteVoicePar[nvoice].DelayTicks =
- (int)((expf(pars.VoicePar[nvoice].PDelay / 127.0f
- * logf(50.0f))
- - 1.0f) / synth.buffersize_f / 10.0f * synth.samplerate_f);
}
/// initparameters();
@@ -833,7 +817,7 @@ void ADnote::initparameters(WatchManager *wm, const char *prefix)
{
int tmp[NUM_VOICES];
ScratchString pre = prefix;
- //ADnoteParameters &pars = *partparams;
+ const float basefreq = powf(2.0f, note_log2_freq);
// Global Parameters
NoteGlobalPar.initparameters(pars.GlobalPar, synth,
@@ -1063,27 +1047,29 @@ void ADnote::setfreqFM(int nvoice, float in_freq)
/*
* Get Voice base frequency
*/
-float ADnote::getvoicebasefreq(int nvoice) const
+float ADnote::getvoicebasefreq(int nvoice, float adjust_log2) const
{
- float detune = NoteVoicePar[nvoice].Detune / 100.0f
+ const float detune = NoteVoicePar[nvoice].Detune / 100.0f
+ NoteVoicePar[nvoice].FineDetune / 100.0f
* ctl.bandwidth.relbw * bandwidthDetuneMultiplier
+ NoteGlobalPar.Detune / 100.0f;
- if(NoteVoicePar[nvoice].fixedfreq == 0)
- return this->basefreq * powf(2, detune / 12.0f);
+ if(NoteVoicePar[nvoice].fixedfreq == 0) {
+ return powf(2.0f, note_log2_freq + detune / 12.0f + adjust_log2);
+ }
else { //the fixed freq is enabled
- float fixedfreq = 440.0f;
- int fixedfreqET = NoteVoicePar[nvoice].fixedfreqET;
+ const int fixedfreqET = NoteVoicePar[nvoice].fixedfreqET;
+ float fixedfreq_log2 = log2f(440.0f);
+
if(fixedfreqET != 0) { //if the frequency varies according the keyboard note
- float tmp = (note_log2_freq - (69.0f / 12.0f))
- * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
+ float tmp_log2 = (note_log2_freq - fixedfreq_log2) *
+ (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
if(fixedfreqET <= 64)
- fixedfreq *= powf(2.0f, tmp);
+ fixedfreq_log2 += tmp_log2;
else
- fixedfreq *= powf(3.0f, tmp);
+ fixedfreq_log2 += tmp_log2 * log2f(3.0f);
}
- return fixedfreq * powf(2.0f, detune / 12.0f);
+ return powf(2.0f, fixedfreq_log2 + detune / 12.0f + adjust_log2);
}
}
@@ -1092,8 +1078,7 @@ float ADnote::getvoicebasefreq(int nvoice) const
*/
float ADnote::getFMvoicebasefreq(int nvoice) const
{
- float detune = NoteVoicePar[nvoice].FMDetune / 100.0f;
- return getvoicebasefreq(nvoice) * powf(2, detune / 12.0f);
+ return getvoicebasefreq(nvoice, NoteVoicePar[nvoice].FMDetune / 1200.0f);
}
/*
@@ -1101,9 +1086,11 @@ float ADnote::getFMvoicebasefreq(int nvoice) const
*/
void ADnote::computecurrentparameters()
{
+ const float relfreq = getFilterCutoffRelFreq();
int nvoice;
float voicefreq, voicepitch, FMfreq,
FMrelativepitch, globalpitch;
+
globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout()
+ NoteGlobalPar.FreqLfo->lfoout()
* ctl.modwheel.relmod);
@@ -1112,14 +1099,12 @@ void ADnote::computecurrentparameters()
* NoteGlobalPar.AmpEnvelope->envout_dB()
* NoteGlobalPar.AmpLfo->amplfoout();
- NoteGlobalPar.Filter->update(ctl.filtercutoff.relfreq,
- ctl.filterq.relq);
-
+ NoteGlobalPar.Filter->update(relfreq, ctl.filterq.relq);
//compute the portamento, if it is used by this note
- float portamentofreqrap = 1.0f;
+ float portamentofreqdelta_log2 = 0.0f;
if(portamento != 0) { //this voice use portamento
- portamentofreqrap = ctl.portamento.freqrap;
+ portamentofreqdelta_log2 = ctl.portamento.freqdelta_log2;
if(ctl.portamento.used == 0) //the portamento has finished
portamento = 0; //this note is no longer "portamented"
}
@@ -1151,8 +1136,7 @@ void ADnote::computecurrentparameters()
/****************/
auto *voiceFilter = NoteVoicePar[nvoice].Filter;
if(voiceFilter) {
- voiceFilter->update(ctl.filtercutoff.relfreq,
- ctl.filterq.relq);
+ voiceFilter->update(relfreq, ctl.filterq.relq);
}
if(NoteVoicePar[nvoice].noisetype == 0) { //compute only if the voice isn't noise
@@ -1167,11 +1151,11 @@ void ADnote::computecurrentparameters()
if(NoteVoicePar[nvoice].FreqEnvelope)
voicepitch += NoteVoicePar[nvoice].FreqEnvelope->envout()
/ 100.0f;
- voicefreq = getvoicebasefreq(nvoice)
- * powf(2, (voicepitch + globalpitch) / 12.0f); //Hz frequency
+ voicefreq = getvoicebasefreq(nvoice, portamentofreqdelta_log2 +
+ (voicepitch + globalpitch) / 12.0f); //Hz frequency
voicefreq *=
powf(ctl.pitchwheel.relfreq, NoteVoicePar[nvoice].BendAdjust); //change the frequency by the controller
- setfreq(nvoice, voicefreq * portamentofreqrap + NoteVoicePar[nvoice].OffsetHz);
+ setfreq(nvoice, voicefreq + NoteVoicePar[nvoice].OffsetHz);
/***************/
/* Modulator */
@@ -1182,15 +1166,11 @@ void ADnote::computecurrentparameters()
FMrelativepitch = NoteVoicePar[nvoice].FMDetune / 100.0f;
if(NoteVoicePar[nvoice].FMFreqEnvelope)
FMrelativepitch +=
- NoteVoicePar[nvoice].FMFreqEnvelope->envout() / 100;
+ NoteVoicePar[nvoice].FMFreqEnvelope->envout() / 100.0f;
if (NoteVoicePar[nvoice].FMFreqFixed)
- FMfreq =
- powf(2.0f, FMrelativepitch
- / 12.0f) * 440.0f;
+ FMfreq = powf(2.0f, FMrelativepitch / 12.0f) * 440.0f;
else
- FMfreq =
- powf(2.0f, FMrelativepitch
- / 12.0f) * voicefreq * portamentofreqrap;
+ FMfreq = powf(2.0f, FMrelativepitch / 12.0f) * voicefreq;
setfreqFM(nvoice, FMfreq);
FMoldamplitude[nvoice] = FMnewamplitude[nvoice];
diff --git a/src/Synth/ADnote.h b/src/Synth/ADnote.h
@@ -37,13 +37,13 @@ class ADnote:public SynthNote
/**Constructor.
* @param pars Note Parameters
* @param spars Synth Engine Agnostic Parameters*/
- ADnote(ADnoteParameters *pars, SynthParams &spars,
+ ADnote(ADnoteParameters *pars, const SynthParams &spars,
WatchManager *wm=0, const char *prefix=0);
/**Destructor*/
~ADnote();
/**Alters the playing note for legato effect*/
- void legatonote(LegatoParams pars);
+ void legatonote(const LegatoParams &pars);
int noteout(float *outl, float *outr);
void releasekey();
@@ -77,7 +77,7 @@ class ADnote:public SynthNote
/**Deallocate Note resources and voice resources*/
void KillNote();
/**Get the Voice's base frequency*/
- inline float getvoicebasefreq(int nvoice) const;
+ inline float getvoicebasefreq(int nvoice, float adjust_log2 = 0.0f) const;
/**Get modulator's base frequency*/
inline float getFMvoicebasefreq(int nvoice) const;
/**Compute the Oscillator's samples.
@@ -116,7 +116,7 @@ class ADnote:public SynthNote
ADnoteParameters &pars;
unsigned char stereo; //if the note is stereo (allows note Panning)
float note_log2_freq;
- float velocity, basefreq;
+ float velocity;
ONOFFTYPE NoteEnabled;
@@ -251,7 +251,7 @@ class ADnote:public SynthNote
/* Wave of the Voice */
float *FMSmp;
- float FMVolume;
+ smooth_float FMVolume;
float FMDetune; //in cents
Envelope *FMFreqEnvelope;
diff --git a/src/Synth/ModFilter.cpp b/src/Synth/ModFilter.cpp
@@ -30,7 +30,6 @@ ModFilter::ModFilter(const FilterParams &pars_,
bool stereo,
float notefreq)
:pars(pars_), synth(synth_), time(time_), alloc(alloc_),
- baseQ(pars.getq()), baseFreq(pars.getfreq()),
noteFreq(notefreq),
left(nullptr),
right(nullptr),
@@ -99,7 +98,7 @@ void ModFilter::update(float relfreq, float relq)
void ModFilter::updateNoteFreq(float noteFreq_)
{
noteFreq = noteFreq_;
- tracking = pars.getfreqtracking(noteFreq);
+ tracking = pars.getfreqtracking(noteFreq_);
}
void ModFilter::updateSense(float velocity, uint8_t scale,
diff --git a/src/Synth/ModFilter.h b/src/Synth/ModFilter.h
@@ -53,12 +53,11 @@ class ModFilter
Allocator &alloc; //RT Memory Pool
-
- float baseQ; //filter sharpness
- float baseFreq; //base filter frequency
+ smooth_float baseQ; //filter sharpness
+ smooth_float baseFreq; //base filter frequency
float noteFreq; //frequency note was initialized to
- float tracking; //shift due to note frequency
- float sense; //shift due to note velocity
+ smooth_float tracking; //shift due to note frequency
+ smooth_float sense; //shift due to note velocity
Filter *left; //left channel filter
diff --git a/src/Synth/PADnote.cpp b/src/Synth/PADnote.cpp
@@ -26,7 +26,7 @@
namespace zyn {
PADnote::PADnote(const PADnoteParameters *parameters,
- SynthParams pars, const int& interpolation, WatchManager *wm,
+ const SynthParams &pars, const int& interpolation, WatchManager *wm,
const char *prefix)
:SynthNote(pars),
watch_int(wm, prefix, "noteout/after_interpolation"), watch_punch(wm, prefix, "noteout/after_punch"),
@@ -38,13 +38,12 @@ PADnote::PADnote(const PADnoteParameters *parameters,
NoteGlobalPar.FilterLfo = nullptr;
firsttime = true;
- setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, false, wm, prefix);
+ setup(pars.velocity, pars.portamento, pars.note_log2_freq, false, wm, prefix);
}
-void PADnote::setup(float freq,
- float velocity_,
+void PADnote::setup(float velocity_,
int portamento_,
- float note_log2_freq,
+ float note_log2_freq_,
bool legato,
WatchManager *wm,
const char *prefix)
@@ -53,22 +52,25 @@ void PADnote::setup(float freq,
velocity = velocity_;
finished_ = false;
+ if(pars.Pfixedfreq == 0) {
+ note_log2_freq = note_log2_freq_;
+ }
+ else { //the fixed freq is enabled
+ const int fixedfreqET = pars.PfixedfreqET;
+ float fixedfreq_log2 = log2f(440.0f);
- if(!pars.Pfixedfreq)
- basefreq = freq;
- else {
- basefreq = 440.0f;
- int fixedfreqET = pars.PfixedfreqET;
if(fixedfreqET != 0) { //if the frequency varies according the keyboard note
- float tmp =
- (note_log2_freq - (69.0f / 12.0f))
- * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
+ float tmp_log2 = (note_log2_freq_ - fixedfreq_log2) *
+ (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
if(fixedfreqET <= 64)
- basefreq *= powf(2.0f, tmp);
+ fixedfreq_log2 += tmp_log2;
else
- basefreq *= powf(3.0f, tmp);
+ fixedfreq_log2 += tmp_log2 * log2f(3.0f);
}
+ note_log2_freq = fixedfreq_log2;
}
+ const float basefreq = powf(2.0f, note_log2_freq);
+
int BendAdj = pars.PBendAdjust - 64;
if (BendAdj % 24 == 0)
BendAdjust = BendAdj / 24;
@@ -84,13 +86,13 @@ void PADnote::setup(float freq,
//find out the closest note
- float logfreq = logf(basefreq * powf(2.0f, NoteGlobalPar.Detune / 1200.0f));
- float mindist = fabsf(logfreq - logf(pars.sample[0].basefreq + 0.0001f));
+ const float log2freq = note_log2_freq + NoteGlobalPar.Detune / 1200.0f;
+ float mindist = fabsf(log2freq - log2f(pars.sample[0].basefreq + 0.0001f));
nsample = 0;
for(int i = 1; i < PAD_MAX_SAMPLES; ++i) {
if(pars.sample[i].smp == NULL)
break;
- float dist = fabsf(logfreq - logf(pars.sample[i].basefreq + 0.0001f));
+ const float dist = fabsf(log2freq - log2f(pars.sample[i].basefreq + 0.0001f));
if(dist < mindist) {
nsample = i;
@@ -113,10 +115,10 @@ void PADnote::setup(float freq,
}
- if(pars.PPanning == 0)
- NoteGlobalPar.Panning = RND;
- else
+ if(pars.PPanning)
NoteGlobalPar.Panning = pars.PPanning / 128.0f;
+ else if(!legato)
+ NoteGlobalPar.Panning = RND;
if(!legato) {
NoteGlobalPar.Fadein_adjustment =
@@ -131,6 +133,7 @@ void PADnote::setup(float freq,
pars.PPunchVelocitySensing));
const float time =
powf(10, 3.0f * pars.PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms
+ const float freq = powf(2.0f, note_log2_freq_);
const float stretch = powf(440.0f / freq, pars.PPunchStretch / 64.0f);
NoteGlobalPar.Punch.dt = 1.0f
/ (time * synth.samplerate_f * stretch);
@@ -199,18 +202,18 @@ void PADnote::setup(float freq,
SynthNote *PADnote::cloneLegato(void)
{
- SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity,
+ SynthParams sp{memory, ctl, synth, time, velocity,
(bool)portamento, legato.param.note_log2_freq, true, legato.param.seed};
return memory.alloc<PADnote>(&pars, sp, interpolation);
}
-void PADnote::legatonote(LegatoParams pars)
+void PADnote::legatonote(const LegatoParams &pars)
{
// Manage legato stuff
if(legato.update(pars))
return;
- setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq, true);
+ setup(pars.velocity, pars.portamento, pars.note_log2_freq, true);
}
@@ -251,6 +254,7 @@ inline void PADnote::fadein(float *smps)
void PADnote::computecurrentparameters()
{
+ const float relfreq = getFilterCutoffRelFreq();
const float globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout()
+ NoteGlobalPar.FreqLfo->lfoout()
* ctl.modwheel.relmod + NoteGlobalPar.Detune);
@@ -259,20 +263,19 @@ void PADnote::computecurrentparameters()
* NoteGlobalPar.AmpEnvelope->envout_dB()
* NoteGlobalPar.AmpLfo->amplfoout();
- NoteGlobalPar.GlobalFilter->update(ctl.filtercutoff.relfreq,
- ctl.filterq.relq);
+ NoteGlobalPar.GlobalFilter->update(relfreq, ctl.filterq.relq);
//compute the portamento, if it is used by this note
- float portamentofreqrap = 1.0f;
+ float portamentofreqdelta_log2 = 0.0f;
if(portamento) { //this voice use portamento
- portamentofreqrap = ctl.portamento.freqrap;
+ portamentofreqdelta_log2 = ctl.portamento.freqdelta_log2;
if(ctl.portamento.used == 0) //the portamento has finished
portamento = false; //this note is no longer "portamented"
}
- realfreq = basefreq * portamentofreqrap
- * powf(2.0f, globalpitch / 12.0f)
- * powf(ctl.pitchwheel.relfreq, BendAdjust) + OffsetHz;
+ realfreq =
+ powf(2.0f, note_log2_freq + globalpitch / 12.0f + portamentofreqdelta_log2) *
+ powf(ctl.pitchwheel.relfreq, BendAdjust) + OffsetHz;
}
diff --git a/src/Synth/PADnote.h b/src/Synth/PADnote.h
@@ -24,12 +24,12 @@ namespace zyn {
class PADnote:public SynthNote
{
public:
- PADnote(const PADnoteParameters *parameters, SynthParams pars,
+ PADnote(const PADnoteParameters *parameters, const SynthParams &pars,
const int &interpolation, WatchManager *wm=0, const char *prefix=0);
~PADnote();
SynthNote *cloneLegato(void);
- void legatonote(LegatoParams pars);
+ void legatonote(const LegatoParams &pars);
int noteout(float *outl, float *outr);
bool finished() const;
@@ -39,7 +39,7 @@ class PADnote:public SynthNote
void releasekey();
private:
- void setup(float freq, float velocity, int portamento_,
+ void setup(float velocity, int portamento_,
float note_log2_freq, bool legato = false, WatchManager *wm=0, const char *prefix=0);
void fadein(float *smps);
void computecurrentparameters();
@@ -49,7 +49,7 @@ class PADnote:public SynthNote
int poshi_l, poshi_r;
float poslo;
- float basefreq;
+ float note_log2_freq;
float BendAdjust;
float OffsetHz;
bool firsttime;
diff --git a/src/Synth/SUBnote.cpp b/src/Synth/SUBnote.cpp
@@ -35,7 +35,7 @@
namespace zyn {
-SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars,
+SUBnote::SUBnote(const SUBnoteParameters *parameters, const SynthParams &spars,
WatchManager *wm, const char *prefix) :
SynthNote(spars),
watch_filter(wm, prefix, "noteout/filter"), watch_amp_int(wm,prefix,"noteout/amp_int"),
@@ -47,12 +47,13 @@ SUBnote::SUBnote(const SUBnoteParameters *parameters, SynthParams &spars,
GlobalFilter(nullptr),
GlobalFilterEnvelope(nullptr),
NoteEnabled(true),
- lfilter(nullptr), rfilter(nullptr)
+ lfilter(nullptr), rfilter(nullptr),
+ filterupdate(false)
{
- setup(spars.frequency, spars.velocity, spars.portamento, spars.note_log2_freq, false, wm, prefix);
+ setup(spars.velocity, spars.portamento, spars.note_log2_freq, false, wm, prefix);
}
-float SUBnote::setupFilters(int *pos, bool automation)
+float SUBnote::setupFilters(float basefreq, int *pos, bool automation)
{
//how much the amplitude is normalised (because the harmonics)
float reduceamp = 0.0f;
@@ -91,22 +92,21 @@ float SUBnote::setupFilters(int *pos, bool automation)
return reduceamp;
}
-void SUBnote::setup(float freq,
- float velocity,
+void SUBnote::setup(float velocity_,
int portamento_,
- float note_log2_freq,
+ float note_log2_freq_,
bool legato,
WatchManager *wm,
const char *prefix)
{
- this->velocity = velocity;
+ velocity = velocity_;
portamento = portamento_;
NoteEnabled = ON;
volume = powf(10.0, pars.Volume / 20.0f);
- volume *= VelF(velocity, pars.AmpVelocityScaleFunction);
+ volume *= VelF(velocity_, pars.AmpVelocityScaleFunction);
if(pars.PPanning != 0)
panning = pars.PPanning / 127.0f;
- else
+ else if (!legato)
panning = RND;
if(!legato) { //normal note
@@ -116,33 +116,38 @@ void SUBnote::setup(float freq,
firsttick = 1;
}
+ if(pars.Pfixedfreq == 0) {
+ note_log2_freq = note_log2_freq_;
+ }
+ else { //the fixed freq is enabled
+ const int fixedfreqET = pars.PfixedfreqET;
+ float fixedfreq_log2 = log2f(440.0f);
- if(pars.Pfixedfreq == 0)
- basefreq = freq;
- else {
- basefreq = 440.0f;
- int fixedfreqET = pars.PfixedfreqET;
- if(fixedfreqET) { //if the frequency varies according the keyboard note
- float tmp = (note_log2_freq - (69.0f / 12.0f))
- * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
+ if(fixedfreqET != 0) { //if the frequency varies according the keyboard note
+ float tmp_log2 = (note_log2_freq_ - fixedfreq_log2) *
+ (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
if(fixedfreqET <= 64)
- basefreq *= powf(2.0f, tmp);
+ fixedfreq_log2 += tmp_log2;
else
- basefreq *= powf(3.0f, tmp);
+ fixedfreq_log2 += tmp_log2 * log2f(3.0f);
}
+ note_log2_freq = fixedfreq_log2;
}
+
int BendAdj = pars.PBendAdjust - 64;
if (BendAdj % 24 == 0)
BendAdjust = BendAdj / 24;
else
BendAdjust = BendAdj / 24.0f;
- float offset_val = (pars.POffsetHz - 64)/64.0f;
+ const float offset_val = (pars.POffsetHz - 64)/64.0f;
OffsetHz = 15.0f*(offset_val * sqrtf(fabsf(offset_val)));
- float detune = getdetune(pars.PDetuneType,
+ const float detune = getdetune(pars.PDetuneType,
pars.PCoarseDetune,
pars.PDetune);
- basefreq *= powf(2.0f, detune / 1200.0f); //detune
-// basefreq*=ctl.pitchwheel.relfreq;//pitch wheel
+
+ note_log2_freq += detune / 1200.0f; //detune
+
+ const float basefreq = powf(2.0f, note_log2_freq);
int pos[MAX_SUB_HARMONICS];
int harmonics;
@@ -172,12 +177,14 @@ void SUBnote::setup(float freq,
}
//how much the amplitude is normalised (because the harmonics)
- float reduceamp = setupFilters(pos, legato);
+ const float reduceamp = setupFilters(basefreq, pos, legato);
oldreduceamp = reduceamp;
volume /= reduceamp;
oldpitchwheel = 0;
oldbandwidth = 64;
+
+ const float freq = powf(2.0f, note_log2_freq_);
if(!legato) { //normal note
if(pars.Pfixedfreq == 0)
initparameters(basefreq, wm, prefix);
@@ -185,13 +192,12 @@ void SUBnote::setup(float freq,
initparameters(basefreq / 440.0f * freq, wm, prefix);
}
else {
- if(pars.Pfixedfreq == 0)
- freq = basefreq;
- else
- freq *= basefreq / 440.0f;
-
- if(GlobalFilter)
- GlobalFilter->updateNoteFreq(basefreq);
+ if(GlobalFilter) {
+ if(pars.Pfixedfreq == 0)
+ GlobalFilter->updateNoteFreq(basefreq);
+ else
+ GlobalFilter->updateNoteFreq(basefreq / 440.0f * freq);
+ }
}
oldamplitude = newamplitude;
@@ -199,20 +205,19 @@ void SUBnote::setup(float freq,
SynthNote *SUBnote::cloneLegato(void)
{
- SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity,
+ SynthParams sp{memory, ctl, synth, time, velocity,
portamento, legato.param.note_log2_freq, true, legato.param.seed};
return memory.alloc<SUBnote>(&pars, sp);
}
-void SUBnote::legatonote(LegatoParams pars)
+void SUBnote::legatonote(const LegatoParams &pars)
{
// Manage legato stuff
if(legato.update(pars))
return;
try {
- setup(pars.frequency, pars.velocity, pars.portamento, pars.note_log2_freq,
- true, wm);
+ setup(pars.velocity, pars.portamento, pars.note_log2_freq, true, wm);
} catch (std::bad_alloc &ba) {
std::cerr << "failed to set legato note parameter in SUBnote: " << ba.what() << std::endl;
}
@@ -310,7 +315,11 @@ void SUBnote::initfilter(bpfilter &filter,
filter.amp = amp;
filter.freq = freq;
filter.bw = bw;
- computefiltercoefs(filter, freq, bw, 1.0f);
+
+ if (!automation)
+ computefiltercoefs(filter, freq, bw, 1.0f);
+ else
+ filterupdate = true;
}
/*
@@ -432,7 +441,8 @@ void SUBnote::computecurrentparameters()
rfilter = memory.valloc<bpfilter>(numstages * numharmonics);
}
- float reduceamp = setupFilters(pos, !delta_harmonics);
+ const float basefreq = powf(2.0f, note_log2_freq);
+ const float reduceamp = setupFilters(basefreq, pos, !delta_harmonics);
volume = volume*oldreduceamp/reduceamp;
oldreduceamp = reduceamp;
}
@@ -440,12 +450,13 @@ void SUBnote::computecurrentparameters()
if(FreqEnvelope || BandWidthEnvelope
|| (oldpitchwheel != ctl.pitchwheel.data)
|| (oldbandwidth != ctl.bandwidth.data)
- || portamento) {
+ || portamento
+ || filterupdate) {
float envfreq = 1.0f;
float envbw = 1.0f;
if(FreqEnvelope) {
- envfreq = FreqEnvelope->envout() / 1200;
+ envfreq = FreqEnvelope->envout() / 1200.0f;
envfreq = powf(2.0f, envfreq);
}
@@ -454,7 +465,7 @@ void SUBnote::computecurrentparameters()
//Update frequency while portamento is converging
if(portamento) {
- envfreq *= ctl.portamento.freqrap;
+ envfreq *= powf(2.0f, ctl.portamento.freqdelta_log2);
if(!ctl.portamento.used) //the portamento has finished
portamento = false;
}
@@ -481,13 +492,15 @@ void SUBnote::computecurrentparameters()
oldbandwidth = ctl.bandwidth.data;
oldpitchwheel = ctl.pitchwheel.data;
+ filterupdate = false;
}
newamplitude = volume * AmpEnvelope->envout_dB() * 2.0f;
//Filter
- if(GlobalFilter)
- GlobalFilter->update(ctl.filtercutoff.relfreq,
- ctl.filterq.relq);
+ if(GlobalFilter) {
+ const float relfreq = getFilterCutoffRelFreq();
+ GlobalFilter->update(relfreq, ctl.filterq.relq);
+ }
}
void SUBnote::computeallfiltercoefs(bpfilter *filters, float envfreq,
diff --git a/src/Synth/SUBnote.h b/src/Synth/SUBnote.h
@@ -23,12 +23,12 @@ namespace zyn {
class SUBnote:public SynthNote
{
public:
- SUBnote(const SUBnoteParameters *parameters, SynthParams &pars,
+ SUBnote(const SUBnoteParameters *parameters, const SynthParams &pars,
WatchManager *wm = 0, const char *prefix = 0);
~SUBnote();
SynthNote *cloneLegato(void);
- void legatonote(LegatoParams pars);
+ void legatonote(const LegatoParams &pars);
VecWatchPoint watch_filter,watch_amp_int, watch_legato;
int noteout(float *outl, float *outr); //note output,return 0 if the note is finished
void releasekey();
@@ -36,12 +36,11 @@ class SUBnote:public SynthNote
void entomb(void);
private:
- void setup(float freq,
- float velocity,
+ void setup(float velocity,
int portamento_,
- float note_log2_freq,
+ float note_log2_freq,
bool legato = false, WatchManager *wm = 0, const char *prefix = 0);
- float setupFilters(int *pos, bool automation);
+ float setupFilters(float basefreq, int *pos, bool automation);
void computecurrentparameters();
/*
* Initialize envelopes and global filter
@@ -58,7 +57,7 @@ class SUBnote:public SynthNote
int numharmonics; //number of harmonics (after the too higher hamonics are removed)
int firstnumharmonics; //To keep track of the first note's numharmonics value, useful in legato mode.
int start; //how the harmonics start
- float basefreq;
+ float note_log2_freq;
float BendAdjust;
float OffsetHz;
float panning;
@@ -104,6 +103,7 @@ class SUBnote:public SynthNote
int oldpitchwheel, oldbandwidth;
float velocity;
+ bool filterupdate;
};
}
diff --git a/src/Synth/SynthNote.cpp b/src/Synth/SynthNote.cpp
@@ -10,6 +10,7 @@
of the License, or (at your option) any later version.
*/
#include "SynthNote.h"
+#include "../Params/Controller.h"
#include "../Misc/Util.h"
#include "../globals.h"
#include <cstring>
@@ -18,13 +19,13 @@
namespace zyn {
-SynthNote::SynthNote(SynthParams &pars)
+SynthNote::SynthNote(const SynthParams &pars)
:memory(pars.memory),
- legato(pars.synth, pars.frequency, pars.velocity, pars.portamento,
+ legato(pars.synth, pars.velocity, pars.portamento,
pars.note_log2_freq, pars.quiet, pars.seed), ctl(pars.ctl), synth(pars.synth), time(pars.time)
{}
-SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port,
+SynthNote::Legato::Legato(const SYNTH_T &synth_, float vel, int port,
float note_log2_freq, bool quiet, prng_t seed)
:synth(synth_)
{
@@ -35,22 +36,20 @@ SynthNote::Legato::Legato(const SYNTH_T &synth_, float freq, float vel, int port
fade.length = 1; // (if something's fishy)
fade.step = (1.0f / fade.length);
decounter = -10;
- param.freq = freq;
param.vel = vel;
param.portamento = port;
param.note_log2_freq = note_log2_freq;
param.seed = seed;
- lastfreq = 0.0f;
+ lastfreq_log2 = note_log2_freq;
silent = quiet;
}
-int SynthNote::Legato::update(LegatoParams pars)
+int SynthNote::Legato::update(const LegatoParams &pars)
{
if(pars.externcall)
msg = LM_Norm;
if(msg != LM_CatchUp) {
- lastfreq = param.freq;
- param.freq = pars.frequency;
+ lastfreq_log2 = param.note_log2_freq;
param.vel = pars.velocity;
param.portamento = pars.portamento;
param.note_log2_freq = pars.note_log2_freq;
@@ -91,7 +90,7 @@ void SynthNote::Legato::apply(SynthNote ¬e, float *outl, float *outr)
// the note to the actual parameters.
decounter = -10;
msg = LM_ToNorm;
- LegatoParams pars{param.freq, param.vel, param.portamento,
+ LegatoParams pars{param.vel, param.portamento,
param.note_log2_freq, false, param.seed};
note.legatonote(pars);
break;
@@ -132,9 +131,9 @@ void SynthNote::Legato::apply(SynthNote ¬e, float *outl, float *outr)
//This freq should make this now silent note to catch-up/resync
//with the heard note for the same length it stayed at the
//previous freq during the fadeout.
- float catchupfreq = param.freq * (param.freq / lastfreq);
- LegatoParams pars{catchupfreq, param.vel, param.portamento,
- param.note_log2_freq, false, param.seed};
+ const float catchupfreq_log2 = 2.0f * param.note_log2_freq - lastfreq_log2;
+ LegatoParams pars{param.vel, param.portamento,
+ catchupfreq_log2, false, param.seed};
note.legatonote(pars);
break;
}
@@ -153,7 +152,7 @@ void SynthNote::Legato::apply(SynthNote ¬e, float *outl, float *outr)
void SynthNote::setVelocity(float velocity_) {
legato.setSilent(true); //Let legato.update(...) returns 0.
- LegatoParams pars{legato.getFreq(), velocity_,
+ LegatoParams pars{velocity_,
legato.getPortamento(), legato.getNoteLog2Freq(), true, legato.getSeed()};
try {
legatonote(pars);
@@ -163,6 +162,32 @@ void SynthNote::setVelocity(float velocity_) {
legato.setDecounter(0); //avoid chopping sound due fade-in
}
+void SynthNote::setPitch(float log2_freq_) {
+ legato.setSilent(true); //Let legato.update(...) return 0.
+ LegatoParams pars{legato.getVelocity(),
+ legato.getPortamento(), log2_freq_, true, legato.getSeed()};
+ try {
+ legatonote(pars);
+ } catch (std::bad_alloc &ba) {
+ std::cerr << "failed to set velocity to legato note: " << ba.what() << std::endl;
+ }
+ legato.setDecounter(0); //avoid chopping sound due fade-in
+}
+
+void SynthNote::setFilterCutoff(float value)
+{
+ filtercutoff_relfreq =
+ (value - 64.0f) * ctl.filtercutoff.depth / 4096.0f * 3.321928f;
+}
+
+float SynthNote::getFilterCutoffRelFreq(void)
+{
+ if (filtercutoff_relfreq.isSet() == false)
+ return (ctl.filtercutoff.relfreq);
+ else
+ return (filtercutoff_relfreq);
+}
+
float SynthNote::getRandomFloat() {
return (getRandomUint() / (INT32_MAX * 1.0f));
}
diff --git a/src/Synth/SynthNote.h b/src/Synth/SynthNote.h
@@ -26,7 +26,6 @@ struct SynthParams
const Controller &ctl;
const SYNTH_T &synth;
const AbsTime &time;
- float frequency; //Note base frequency
float velocity; //Velocity of the Note
bool portamento;//True if portamento is used for this note
float note_log2_freq; //Floating point value of the note
@@ -36,7 +35,6 @@ struct SynthParams
struct LegatoParams
{
- float frequency;
float velocity;
bool portamento;
float note_log2_freq; //Floating point value of the note
@@ -47,7 +45,7 @@ struct LegatoParams
class SynthNote
{
public:
- SynthNote(SynthParams &pars);
+ SynthNote(const SynthParams &pars);
virtual ~SynthNote() {}
/**Compute Output Samples
@@ -65,13 +63,20 @@ class SynthNote
/**Make a note die off next buffer compute*/
virtual void entomb(void) = 0;
- virtual void legatonote(LegatoParams pars) = 0;
+ virtual void legatonote(const LegatoParams &pars) = 0;
virtual SynthNote *cloneLegato(void) = 0;
/* For polyphonic aftertouch needed */
void setVelocity(float velocity_);
+ /* For per-note pitch */
+ void setPitch(float log2_freq_);
+
+ /* For per-note filter cutoff */
+ void setFilterCutoff(float);
+ float getFilterCutoffRelFreq(void);
+
/* Random numbers with own seed */
float getRandomFloat();
prng_t getRandomUint();
@@ -83,15 +88,15 @@ class SynthNote
class Legato
{
public:
- Legato(const SYNTH_T &synth_, float freq, float vel, int port,
+ Legato(const SYNTH_T &synth_, float vel, int port,
float note_log2_freq, bool quiet, prng_t seed);
void apply(SynthNote ¬e, float *outl, float *outr);
- int update(LegatoParams pars);
+ int update(const LegatoParams &pars);
private:
bool silent;
- float lastfreq;
+ float lastfreq_log2;
LegatoMsg msg;
int decounter;
struct { // Fade In/Out vars
@@ -123,6 +128,7 @@ class SynthNote
const SYNTH_T &synth;
const AbsTime &time;
WatchManager *wm;
+ smooth_float filtercutoff_relfreq;
};
}
diff --git a/src/Tests/AdNoteTest.h b/src/Tests/AdNoteTest.h
@@ -41,7 +41,7 @@ class AdNoteTest:public CxxTest::TestSuite
ADnoteParameters *defaultPreset;
Controller *controller;
Alloc memory;
- unsigned char testnote;
+ float test_freq_log2;
WatchManager *w;
float *outR, *outL;
@@ -91,9 +91,8 @@ class AdNoteTest:public CxxTest::TestSuite
controller = new Controller(*synth, time);
//lets go with.... 50! as a nice note
- testnote = 50;
- float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f);
- SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()};
+ test_freq_log2 = log2f(440.0f) + (50.0 - 69.0f) / 12.0f;
+ SynthParams pars{memory, *controller, *synth, *time, 120, 0, test_freq_log2, false, prng()};
note = new ADnote(defaultPreset, pars,w);
diff --git a/src/Tests/ControllerTest.h b/src/Tests/ControllerTest.h
@@ -38,31 +38,31 @@ class ControllerTest:public CxxTest::TestSuite
//Initialize portamento
testCtl->setportamento(127);
testCtl->portamento.time = 127;
- testCtl->initportamento(40.0f, 400.0f, false);
+ testCtl->initportamento(log2f(40.0f), log2f(400.0f), false);
//Bounds Check
while(testCtl->portamento.used) {
TS_ASSERT((0.0f <= testCtl->portamento.x)
&& (testCtl->portamento.x <= 1.0f));
- TS_ASSERT((0.1f <= testCtl->portamento.freqrap)
- && (testCtl->portamento.freqrap <= 1.0f));
+ TS_ASSERT((log2f(0.1f) <= testCtl->portamento.freqdelta_log2)
+ && (testCtl->portamento.freqdelta_log2 <= log2f(1.0f)));
testCtl->updateportamento();
}
TS_ASSERT((0.0f <= testCtl->portamento.x)
&& (testCtl->portamento.x <= 1.0f));
- TS_ASSERT((0.1f <= testCtl->portamento.freqrap)
- && (testCtl->portamento.freqrap <= 1.0f));
+ TS_ASSERT((log2f(0.1f) <= testCtl->portamento.freqdelta_log2)
+ && (testCtl->portamento.freqdelta_log2 <= log2f(1.0f)));
}
void testPortamentoValue() {
testCtl->setportamento(127);
testCtl->portamento.time = 127;
- testCtl->initportamento(40.0f, 400.0f, false);
+ testCtl->initportamento(log2f(40.0f), log2f(400.0f), false);
int i;
for(i = 0; i < 10; ++i)
testCtl->updateportamento();
//Assert that the numbers are the same as they were at release
TS_ASSERT_DELTA(testCtl->portamento.x, 0.0290249f, 0.000001f)
- TS_ASSERT_DELTA(testCtl->portamento.freqrap, 0.126122f, 0.000001f)
+ TS_ASSERT_DELTA(testCtl->portamento.freqdelta_log2, -3.2255092, 0.000001f)
}
private:
diff --git a/src/Tests/MemoryStressTest.h b/src/Tests/MemoryStressTest.h
@@ -85,8 +85,7 @@ class AdNoteTest:public CxxTest::TestSuite
void testManySimultaneousNotes() {
unsigned char testnote = 42;
- float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f);
- SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()};
+ SynthParams pars{memory, *controller, *synth, *time, 120, 0, testnote / 12.0f, false, prng()};
std::vector<ADnote*> notes;
diff --git a/src/Tests/PadNoteTest.h b/src/Tests/PadNoteTest.h
@@ -48,7 +48,7 @@ class PadNoteTest:public CxxTest::TestSuite
FFTwrapper *fft;
Controller *controller;
AbsTime *time;
- unsigned char testnote;
+ float test_freq_log2;
Alloc memory;
int interpolation;
rtosc::ThreadLink *tr;
@@ -107,9 +107,8 @@ class PadNoteTest:public CxxTest::TestSuite
controller = new Controller(*synth, time);
//lets go with.... 50! as a nice note
- testnote = 50;
- float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f);
- SynthParams pars_{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()};
+ test_freq_log2 = log2f(440.0f) + (50.0 - 69.0f) / 12.0f;
+ SynthParams pars_{memory, *controller, *synth, *time, 120, 0, test_freq_log2, false, prng()};
note = new PADnote(pars, pars_, interpolation);
}
diff --git a/src/Tests/SubNoteTest.h b/src/Tests/SubNoteTest.h
@@ -41,7 +41,7 @@ class SubNoteTest:public CxxTest::TestSuite
Master *master;
AbsTime *time;
Controller *controller;
- unsigned char testnote;
+ float test_freq_log2;
Alloc memory;
rtosc::ThreadLink *tr;
WatchManager *w;
@@ -81,10 +81,9 @@ class SubNoteTest:public CxxTest::TestSuite
controller = new Controller(*synth, time);
//lets go with.... 50! as a nice note
- testnote = 50;
- float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f);
+ test_freq_log2 = log2f(440.0f) + (50.0 - 69.0f) / 12.0f;
- SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()};
+ SynthParams pars{memory, *controller, *synth, *time, 120, 0, test_freq_log2, false, prng()};
note = new SUBnote(defaultPreset, pars, w);
this->pars = defaultPreset;
}
diff --git a/src/Tests/TriggerTest.h b/src/Tests/TriggerTest.h
@@ -42,7 +42,7 @@ class TriggerTest:public CxxTest::TestSuite
Master *master;
AbsTime *time;
Controller *controller;
- unsigned char testnote;
+ float test_freq_log2;
Alloc memory;
rtosc::ThreadLink *tr;
WatchManager *w;
@@ -74,10 +74,9 @@ class TriggerTest:public CxxTest::TestSuite
controller = new Controller(*synth, time);
//lets go with.... 50! as a nice note
- testnote = 50;
- float freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f);
+ test_freq_log2 = log2f(440.0f) + (50.0 - 69.0f) / 12.0f;
- SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()};
+ SynthParams pars{memory, *controller, *synth, *time, 120, 0, test_freq_log2, false, prng()};
note = new SUBnote(defaultPreset, pars, w);
this->pars = defaultPreset;
}
diff --git a/src/Tests/UnisonTest.h b/src/Tests/UnisonTest.h
@@ -39,11 +39,10 @@ class AdNoteTest:public CxxTest::TestSuite
ADnote *note;
FFTwrapper *fft;
Controller *controller;
- unsigned char testnote;
+ float test_freq_log2;
ADnoteParameters *params;
AbsTime *time;
Alloc memory;
- float freq;
float outR[BUF], outL[BUF];
@@ -71,8 +70,7 @@ class AdNoteTest:public CxxTest::TestSuite
controller = new Controller(*synth, time);
//lets go with.... 50! as a nice note
- testnote = 50;
- freq = 440.0f * powf(2.0f, (testnote - 69.0f) / 12.0f);
+ test_freq_log2 = log2f(440.0f) + (50.0 - 69.0f) / 12.0f;
}
void tearDown() {
@@ -98,7 +96,7 @@ class AdNoteTest:public CxxTest::TestSuite
params->VoicePar[0].Unison_vibratto_speed = e;
params->VoicePar[0].Unison_invert_phase = f;
- SynthParams pars{memory, *controller, *synth, *time, freq, 120, 0, testnote / 12.0f, false, prng()};
+ SynthParams pars{memory, *controller, *synth, *time, 120, 0, test_freq_log2, false, prng()};
note = new ADnote(params, pars);
note->noteout(outL, outR);
TS_ASSERT_DELTA(outL[80], values[0], 1.9e-5);
diff --git a/src/UI/MicrotonalUI.fl b/src/UI/MicrotonalUI.fl
@@ -198,7 +198,7 @@ if (true) {
Fl_Counter anotecounter {
label {"A" Note}
callback {
- /*if (microtonal->getnotefreq(o->value(),0)<0.0) o->textcolor(FL_RED);
+ /*if (microtonal->getnotefreq(o->value() / 12.0f,0)<0.0) o->textcolor(FL_RED);
else o->textcolor(FL_BLACK);*/
o->redraw();}
diff --git a/src/globals.h b/src/globals.h
@@ -237,6 +237,7 @@ enum ONOFFTYPE {
enum MidiControllers {
C_bankselectmsb = 0, C_pitchwheel = 1000, C_NULL = 1001,
+ C_aftertouch = 1002, C_pitch = 1003,
C_expression = 11, C_panning = 10, C_bankselectlsb = 32,
C_filtercutoff = 74, C_filterq = 71, C_bandwidth = 75, C_modwheel = 1,
C_fmamp = 76,
@@ -346,5 +347,37 @@ struct SYNTH_T {
static float numRandom(void); //defined in Util.cpp for now
};
+class smooth_float {
+private:
+ bool init;
+ float curr_value;
+ float next_value;
+public:
+ smooth_float() {
+ init = false;
+ next_value = curr_value = 0.0f;
+ };
+ smooth_float(const float value) {
+ init = true;
+ next_value = curr_value = value;
+ };
+ operator float() {
+ const float delta = (next_value - curr_value) / 128.0f;
+ curr_value += delta;
+ return (curr_value);
+ };
+ void operator =(const float value) {
+ if (init) {
+ next_value = value;
+ } else {
+ next_value = curr_value = value;
+ init = true;
+ }
+ };
+ bool isSet() const {
+ return (init);
+ };
+};
+
}
#endif