zynaddsubfx

ZynAddSubFX open source synthesizer
Log | Files | Refs | Submodules | LICENSE

commit 58307dd9f67d5c83f07068255be53a610868dbe4
parent ebbbfe8a7a69823143ee218d2e49ab0ad3136619
Author: Hans Petter Selasky <hps@selasky.org>
Date:   Sat,  4 Apr 2020 23:05:22 +0200

Switch portamento code to using logarithmic frequencies.

Signed-off-by: Hans Petter Selasky <hps@selasky.org>

Diffstat:
Msrc/Misc/Part.cpp | 16++++++++--------
Msrc/Misc/Part.h | 2+-
Msrc/Params/Controller.cpp | 60++++++++++++++++++++++++------------------------------------
Msrc/Params/Controller.h | 13++++++-------
Msrc/Synth/ADnote.cpp | 17++++++-----------
Msrc/Synth/PADnote.cpp | 10+++++-----
Msrc/Synth/SUBnote.cpp | 4++--
Msrc/Tests/ControllerTest.h | 14+++++++-------
8 files changed, 59 insertions(+), 77 deletions(-)

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(); @@ -502,22 +502,22 @@ bool Part::NoteOnInternal(note_t note, //Compute Note Parameters const float vel = getVelocity(velocity, Pvelsns, Pveloffs); - const float notebasefreq = powf(2.0f, note_log2_freq); //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) { diff --git a/src/Misc/Part.h b/src/Misc/Part.h @@ -211,7 +211,7 @@ class Part 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/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,58 +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; } @@ -345,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/Synth/ADnote.cpp b/src/Synth/ADnote.cpp @@ -1101,11 +1101,10 @@ void ADnote::computecurrentparameters() 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" } @@ -1152,11 +1151,11 @@ void ADnote::computecurrentparameters() if(NoteVoicePar[nvoice].FreqEnvelope) voicepitch += NoteVoicePar[nvoice].FreqEnvelope->envout() / 100.0f; - voicefreq = getvoicebasefreq(nvoice, + 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 */ @@ -1169,13 +1168,9 @@ void ADnote::computecurrentparameters() FMrelativepitch += 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/PADnote.cpp b/src/Synth/PADnote.cpp @@ -266,16 +266,16 @@ void PADnote::computecurrentparameters() 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 = portamentofreqrap - * powf(2.0f, note_log2_freq + 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/SUBnote.cpp b/src/Synth/SUBnote.cpp @@ -456,7 +456,7 @@ void SUBnote::computecurrentparameters() float envbw = 1.0f; if(FreqEnvelope) { - envfreq = FreqEnvelope->envout() / 1200; + envfreq = FreqEnvelope->envout() / 1200.0f; envfreq = powf(2.0f, envfreq); } @@ -465,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; } 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: