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:
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: