commit a0c59218af9957ea4af0288882ed115a9453da65
parent 76f68a08f01d3a3cd4fd4bc0b75650ef4a9d4f99
Author: friedolino78 <34608315+friedolino78@users.noreply.github.com>
Date: Wed, 23 Jun 2021 21:14:50 +0200
Add LFO fade in feature (#114)
Diffstat:
9 files changed, 180 insertions(+), 21 deletions(-)
diff --git a/src/Params/LFOParams.cpp b/src/Params/LFOParams.cpp
@@ -80,6 +80,14 @@ static const rtosc::Ports _ports = {
rLinear(0.0, 4.0), rDefaultDepends(loc), rDefault(0),
rPreset(ad_voice_amp, 0.94),
"Delay before LFO start\n0..4 second delay"),
+ rParamF(fadein, rShort("fadein"), rSpecial(disable), rUnit(S),
+ rLinear(0.0, 10.0f), rDefault(0.0f),
+ "Time to ramp up LFO amplitude\n \
+ 0..10 seconds"),
+ rParamF(fadeout, rShort("fadeout"), rSpecial(disable), rUnit(S),
+ rLinear(0.001, 10.0f), rDefault(10.0f),
+ "Time to ramp down LFO amplitude on key release\n \
+ 0..10 seconds, 10=off (default)"),
{"Pdelay::i", rShort("delay") rLinear(0,127)
rDoc("Delay before LFO start\n0..4 second delay"), NULL,
[](const char *msg, RtData &d)
@@ -91,6 +99,7 @@ static const rtosc::Ports _ports = {
obj->delay = 4.0f * rtosc_argument(msg, 0).i / 127.0f;
}
}},
+
rToggle(Pcontinous, rShort("c"), rDefault(false),
"Enable for global operation"),
rParamZyn(Pstretch, rShort("str"), rCentered, rDefault(64),
@@ -145,7 +154,7 @@ void LFOParams::setup()
// TODO: reuse
LFOParams::LFOParams(const AbsTime *time_) :
- LFOParams(2.65, 0, 0, 127, 0, 0, 0, 0, loc_unspecified, time_)
+ LFOParams(2.65, 0, 0, 127, 0, 0, 0.0, 0.0, 10.0, 0, loc_unspecified, time_)
{
}
@@ -155,7 +164,9 @@ LFOParams::LFOParams(float freq_,
char Pcutoff_,
char PLFOtype_,
char Prandomness_,
- float Pdelay_,
+ float delay_,
+ float fadein_,
+ float fadeout_,
char Pcontinous_,
consumer_location_t loc,
const AbsTime *time_) : loc(loc),
@@ -167,7 +178,9 @@ LFOParams::LFOParams(float freq_,
Dcutoff = Pcutoff_;
DLFOtype = PLFOtype_;
Drandomness = Prandomness_;
- Ddelay = Pdelay_;
+ Ddelay = delay_;
+ Dfadein = fadein_;
+ Dfadeout = fadeout_;
Dcontinous = Pcontinous_;
setup();
@@ -189,6 +202,8 @@ LFOParams::LFOParams(consumer_location_t loc,
DLFOtype = PLFOtype_;
Drandomness = Prandomness_;
Ddelay = delay_;
+ Dfadein = 0.0f;
+ Dfadeout = 10.0f;
Dcontinous = Pcontinous_;
};
@@ -198,8 +213,8 @@ LFOParams::LFOParams(consumer_location_t loc,
case ad_global_freq: init(3.71, 0, 64, 127, 0, 0, 0, 0); break;
case ad_global_filter: init(6.49, 0, 64, 127, 0, 0, 0, 0); break;
case ad_voice_amp: init(11.25, 32, 64, 127, 0, 0, 0.94, 0); break;
- case ad_voice_freq: init(1.19, 40, 0, 127, 0, 0, 0, 0); break;
- case ad_voice_filter: init(1.19, 20, 64, 127, 0, 0, 0, 0); break;
+ case ad_voice_freq: init(1.19, 40, 0, 127, 0, 0, 0, 0); break;
+ case ad_voice_filter: init(1.19, 20, 64, 127, 0, 0, 0, 0); break;
default: throw std::logic_error("Invalid LFO consumer location");
}
@@ -218,6 +233,8 @@ void LFOParams::defaults()
PLFOtype = DLFOtype;
Prandomness = Drandomness;
delay = Ddelay;
+ fadein = Dfadein;
+ fadeout = Dfadeout;
Pcontinous = Dcontinous;
Pfreqrand = 0;
Pstretch = 64;
@@ -234,6 +251,8 @@ void LFOParams::add2XML(XMLwrapper& xml)
xml.addpar("randomness_amplitude", Prandomness);
xml.addpar("randomness_frequency", Pfreqrand);
xml.addparreal("delay", delay);
+ xml.addparreal("fadein", fadein);
+ xml.addparreal("fadeout", fadeout);
xml.addpar("stretch", Pstretch);
xml.addparbool("continous", Pcontinous);
}
@@ -247,7 +266,7 @@ void LFOParams::getfromXML(XMLwrapper& xml)
}
Pintensity = xml.getpar127("intensity", Pintensity);
Pstartphase = xml.getpar127("start_phase", Pstartphase);
- Pcutoff = xml.getpar127("cutoff", Pcutoff);
+ Pcutoff = xml.getpar127("cutoff", Pcutoff);
PLFOtype = xml.getpar127("lfo_type", PLFOtype);
Prandomness = xml.getpar127("randomness_amplitude", Prandomness);
Pfreqrand = xml.getpar127("randomness_frequency", Pfreqrand);
@@ -257,6 +276,12 @@ void LFOParams::getfromXML(XMLwrapper& xml)
delay = 4.0f * xml.getpar127("delay", (int)delay *127.0f/4.0f)
/ 127.0f;
}
+ if (xml.hasparreal("fadein")) {
+ fadein = xml.getparreal("fadein", fadein);
+ }
+ if (xml.hasparreal("fadeout")) {
+ fadeout = xml.getparreal("fadeout", fadeout);
+ }
Pstretch = xml.getpar127("stretch", Pstretch);
Pcontinous = xml.getparbool("continous", Pcontinous);
}
@@ -272,6 +297,8 @@ void LFOParams::paste(LFOParams &x)
COPY(Prandomness);
COPY(Pfreqrand);
COPY(delay);
+ COPY(fadein);
+ COPY(fadeout);
COPY(Pcontinous);
COPY(Pstretch);
diff --git a/src/Params/LFOParams.h b/src/Params/LFOParams.h
@@ -44,6 +44,8 @@ class LFOParams:public Presets
char PLFOtype_,
char Prandomness_,
float delay_,
+ float fadein_,
+ float fadeout_,
char Pcontinous,
consumer_location_t loc,
const AbsTime* time_ = nullptr);
@@ -64,6 +66,8 @@ class LFOParams:public Presets
unsigned char Prandomness; /**<randomness (0=off)*/
unsigned char Pfreqrand; /**<frequency randomness (0=off)*/
float delay; /**<delay (0=off)*/
+ float fadein; /**<fadein, relative to delay*/
+ float fadeout; /**<fadeout on key release (10.0=off)*/
unsigned char Pcontinous; /**<1 if LFO is continous*/
unsigned char Pstretch; /**<how the LFO is "stretched" according the note frequency (64=no stretch)*/
@@ -87,6 +91,8 @@ class LFOParams:public Presets
unsigned char DLFOtype;
unsigned char Drandomness;
float Ddelay;
+ float Dfadein;
+ float Dfadeout;
unsigned char Dcontinous;
};
diff --git a/src/Synth/ADnote.cpp b/src/Synth/ADnote.cpp
@@ -1936,6 +1936,9 @@ void ADnote::releasekey()
NoteGlobalPar.FreqEnvelope->releasekey();
NoteGlobalPar.FilterEnvelope->releasekey();
NoteGlobalPar.AmpEnvelope->releasekey();
+ NoteGlobalPar.FreqLfo->releasekey();
+ NoteGlobalPar.FilterLfo->releasekey();
+ NoteGlobalPar.AmpLfo->releasekey();
}
/*
diff --git a/src/Synth/LFO.cpp b/src/Synth/LFO.cpp
@@ -28,7 +28,8 @@ LFO::LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t, WatchManage
waveShape(lfopars.PLFOtype),
deterministic(!lfopars.Pfreqrand),
dt_(t.dt()),
- lfopars_(lfopars), basefreq_(basefreq),
+ lfopars_(lfopars),
+ basefreq_(basefreq),
watchOut(m, watch_prefix, "out")
{
int stretch = lfopars.Pstretch;
@@ -74,6 +75,11 @@ LFO::LFO(const LFOParams &lfopars, float basefreq, const AbsTime &t, WatchManage
break;
}
+ lfo_state=lfo_state_type::delaying;
+
+ rampUp = 0.0f;
+ rampDown = 1.0f;
+
amp1 = (1 - lfornd) + lfornd * RND;
amp2 = (1 - lfornd) + lfornd * RND;
incrnd = nextincrnd = 1.0f;
@@ -154,46 +160,113 @@ float LFO::biquad(float input)
return (cutoff==127) ? input : output; // at cutoff 127 bypass filter
}
+void LFO::releasekey()
+{
+ if (lfopars_.fadeout==10.0f) { // deactivated
+ fadeOutDuration = 0.0f;
+ return;
+ }
+ // store current ramp value in case of release while fading in
+ rampOnRelease = rampUp;
+ // burn in the current amount of outStartValue
+ // therefor multiply its current reverse ramping factor
+ // and divide by current ramp factor. It will be multiplied during fading out.
+ outStartValue *= (1.0f - rampOnRelease);
+ // store current time
+ releaseTimestamp = lfopars_.time->time();
+ // calculate fade out duration in frames
+ fadeOutDuration = lfopars_.fadeout * lfopars_.time->framesPerSec();
+ // set fadeout state
+ lfo_state = lfo_state_type::fadingOut;
+}
float LFO::lfoout()
{
- //update internals XXX TODO cleanup
+ //update internals
if ( ! lfopars_.time || lfopars_.last_update_timestamp == lfopars_.time->time())
{
waveShape = lfopars_.PLFOtype;
int stretch = lfopars_.Pstretch;
if(stretch == 0)
stretch = 1;
+
const float lfostretch = powf(basefreq_ / 440.0f, (stretch - 64.0f) / 63.0f);
float lfofreq = lfopars_.freq * lfostretch;
-
phaseInc = fabsf(lfofreq) * dt_;
switch(lfopars_.fel) {
case consumer_location_type_t::amp:
- lfointensity = lfopars_.Pintensity / 127.0f;
+ lfointensity = lfopars_.Pintensity / 127.0f; // [0...1]
break;
case consumer_location_type_t::filter:
- lfointensity = lfopars_.Pintensity / 127.0f * 4.0f;
- break; //in octave
+ lfointensity = lfopars_.Pintensity / 127.0f * 4.0f; // [0...4] octaves
+ break;
case consumer_location_type_t::freq:
case consumer_location_type_t::unspecified:
- lfointensity = powf(2, lfopars_.Pintensity / 127.0f * 11.0f) - 1.0f; //in centi
- //x -= 0.25f; //chance the starting phase
+ lfointensity = powf(2, lfopars_.Pintensity / 127.0f * 11.0f) - 1.0f; // [0...2047] cent
break;
}
}
-
+
float out = baseOut(waveShape, phase);
-
+ // Apply intensity
if(waveShape == LFO_SINE || waveShape == LFO_TRIANGLE)
out *= lfointensity * (amp1 + phase * (amp2 - amp1));
else
out *= lfointensity * amp2;
+
+
+ // handle lfo state (delay, fade in, fade out)
+ switch(lfo_state) {
+ case delaying:
+
+ outStartValue = out; // keep start value to prevent jump
+ if (delayTime.inFuture()) {
+ return out;
+ }else{
+ fadeInTimestamp = lfopars_.time->time();
+ fadeInDuration = lfopars_.fadein * lfopars_.time->framesPerSec();
+ lfo_state = lfo_state_type::fadingIn;
+ }
+
+ break;
- if(delayTime.inFuture())
- return out;
+ case fadingIn:
+
+ if (fadeInDuration && rampUp < 1.0) {
+ rampUp = ((float)(lfopars_.time->time() - fadeInTimestamp) / (float)fadeInDuration);
+ rampUp *= rampUp; // square for soft start
+
+ }
+ else {
+ rampUp = 1.0f;
+ lfo_state = lfo_state_type::running;
+ }
+
+ out *= rampUp;
+ out += outStartValue * (1.0f-rampUp);
+
+ break;
+
+ case fadingOut:
+ if(fadeOutDuration && rampDown) {// no division by zero, please
+ rampDown = 1.0f - ( (float)(lfopars_.time->time() - releaseTimestamp) / (float)fadeOutDuration );
+ rampDown *= rampDown; // square for soft end
+ }
+ else // no ramp down
+ rampDown = 0.0f;
+
+
+ out *= rampOnRelease * rampDown;
+ out += outStartValue*rampDown;
+
+ break;
+
+ case running:
+ default:
+ break;
+ }
//Start oscillating
if(deterministic)
@@ -209,7 +282,7 @@ float LFO::lfoout()
computeNextFreqRnd();
}
-
+
float watch_data[2] = {phase, out};
watchOut(watch_data, 2);
diff --git a/src/Synth/LFO.h b/src/Synth/LFO.h
@@ -18,6 +18,8 @@
#include "../Misc/Time.h"
#include "WatchPoint.h"
+
+
namespace zyn {
/**Class for creating Low Frequency Oscillators*/
@@ -35,9 +37,20 @@ class LFO
float lfoout();
float amplfoout();
+ void releasekey();
private:
+ typedef enum lfo_state_type{
+ delaying,
+ fadingIn,
+ running,
+ fadingOut
+ } lfo_state_type;
+
float baseOut(const char waveShape, const float phase);
float biquad(float input);
+ lfo_state_type lfo_state;
+
+ //~ float out;
//Phase of Oscillator
float phase;
//Phase Increment Per Frame
@@ -60,6 +73,18 @@ class LFO
//Delay before starting
RelTime delayTime;
+ int64_t fadeInDuration;
+ //Timestamp of begin fadein
+ int64_t fadeInTimestamp;
+ //Timestamp of noteoff
+ int64_t releaseTimestamp;
+ //Time to ramp out
+
+ int64_t fadeOutDuration;
+ float rampUp, rampDown, rampOnRelease;
+ // store the constant out value before oscillating starts
+ float outStartValue = 0.0;
+
char waveShape;
//If After initialization there are no calls to random number gen.
diff --git a/src/Synth/PADnote.cpp b/src/Synth/PADnote.cpp
@@ -461,6 +461,9 @@ void PADnote::releasekey()
NoteGlobalPar.FreqEnvelope->releasekey();
NoteGlobalPar.FilterEnvelope->releasekey();
NoteGlobalPar.AmpEnvelope->releasekey();
+ NoteGlobalPar.FreqLfo->releasekey();
+ NoteGlobalPar.FilterLfo->releasekey();
+ NoteGlobalPar.AmpLfo->releasekey();
}
}
diff --git a/src/Tests/AdNoteTest.cpp b/src/Tests/AdNoteTest.cpp
@@ -219,7 +219,7 @@ class AdNoteTest
TS_ASSERT_EQUAL_INT(sampleCount, 30208);
- lfop = new LFOParams();
+ lfop = new LFOParams(time);
lfop->fel = zyn::consumer_location_type_t::amp;
lfop->freq = 2.0f;
lfop->delay = 0.0f;
diff --git a/src/Tests/WatchTest.cpp b/src/Tests/WatchTest.cpp
@@ -51,7 +51,7 @@ class WatchTest
s = new SYNTH_T;
at = new AbsTime(*s);
w = new WatchManager(tr);
- par = new LFOParams;
+ par = new LFOParams(at);
l = new LFO(*par, 440.0, *at, w);
}
diff --git a/src/Tests/guitar-adnote.xmz b/src/Tests/guitar-adnote.xmz
@@ -100,6 +100,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</AMPLITUDE_LFO>
@@ -134,6 +136,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0.598425" exact_value="0x3F193265" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</FREQUENCY_LFO>
@@ -175,6 +179,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</FILTER_LFO>
@@ -273,6 +279,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0.944882" exact_value="0x3F71E3C8" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</AMPLITUDE_LFO>
@@ -312,6 +320,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="yes" />
</FREQUENCY_LFO>
@@ -355,6 +365,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</FILTER_LFO>
@@ -524,6 +536,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="yes" />
</FREQUENCY_LFO>
@@ -611,6 +625,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="yes" />
</FREQUENCY_LFO>
@@ -3707,6 +3723,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</AMPLITUDE_LFO>
@@ -3744,6 +3762,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</FREQUENCY_LFO>
@@ -3785,6 +3805,8 @@ version-revision="6" ZynAddSubFX-author="Nasca Octavian Paul">
<par name="randomness_amplitude" value="0" />
<par name="randomness_frequency" value="0" />
<par_real name="delay" value="0" exact_value="0x00000000" />
+<par_real name="fadein" value="0" exact_value="0x00000000" />
+<par_real name="fadeout" value="10" exact_value="0x41200000" />
<par name="stretch" value="64" />
<par_bool name="continous" value="no" />
</FILTER_LFO>