zynaddsubfx

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

LFO.cpp (9702B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   LFO.cpp - LFO implementation
      5   Copyright (C) 2002-2005 Nasca Octavian Paul
      6   Author: Nasca Octavian Paul
      7 
      8   This program is free software; you can redistribute it and/or
      9   modify it under the terms of the GNU General Public License
     10   as published by the Free Software Foundation; either version 2
     11   of the License, or (at your option) any later version.
     12 */
     13 
     14 #include "LFO.h"
     15 #include "../Params/LFOParams.h"
     16 #include "../Misc/Util.h"
     17 
     18 #include <cstdlib>
     19 #include <cstdio>
     20 #include <cmath>
     21 
     22 namespace zyn {
     23 
     24 LFO::LFO(const LFOParams &lfopars_, float basefreq_, const AbsTime &t, WatchManager *m,
     25         const char *watch_prefix)
     26     :first_half(-1),
     27     time(t),
     28     delayTime(t, lfopars_.delay), //0..4 sec
     29     deterministic(!lfopars_.Pfreqrand),
     30     dt(t.dt()),
     31     lfopars(lfopars_),
     32     basefreq(basefreq_),
     33     watchOut(m, watch_prefix, "out")
     34 {
     35     updatePars();
     36 
     37     if(!lfopars.Pcontinous) {
     38         if(!lfopars.Pstartphase)
     39             phase = RND;
     40         else
     41             phase = 0.0f;
     42     }
     43     else {
     44         phase = fmod((float)t.time() * phaseInc, 1.0f);
     45     }
     46 
     47     lfornd = limit(lfopars.Prandomness / 127.0f, 0.0f, 1.0f);
     48     lfofreqrnd = powf(lfopars.Pfreqrand / 127.0f, 2.0f) * 4.0f;
     49 
     50     switch(lfopars.fel) {
     51         case consumer_location_type_t::amp:
     52             lfointensity = lfopars.Pintensity / 127.0f;
     53             break;
     54         case consumer_location_type_t::filter:
     55             lfointensity = lfopars.Pintensity / 127.0f * 4.0f;
     56             break; //in octave
     57         case consumer_location_type_t::freq:
     58         case consumer_location_type_t::unspecified:
     59             lfointensity = powf(2, lfopars.Pintensity / 127.0f * 11.0f) - 1.0f; //in centi
     60             phase -= 0.25f; //chance the starting phase
     61             break;
     62     }
     63 
     64     lfo_state=lfo_state_type::delaying;
     65 
     66     rampUp = 0.0f;
     67     rampDown = 1.0f;
     68 
     69     amp1     = (1 - lfornd) + lfornd * RND;
     70     amp2     = (1 - lfornd) + lfornd * RND;
     71     incrnd   = nextincrnd = 1.0f;
     72     computeNextFreqRnd();
     73     computeNextFreqRnd(); //twice because I want incrnd & nextincrnd to be random
     74     z1 = 0.0;
     75     z2 = 0.0;
     76 }
     77 
     78 LFO::~LFO()
     79 {}
     80 
     81 void LFO::updatePars()
     82 {
     83     waveShape = lfopars.PLFOtype;
     84     int stretch = lfopars.Pstretch;
     85     if(stretch == 0)
     86         stretch = 1;
     87 
     88     // stretch max 2x/octave
     89     const float lfostretch = powf(basefreq / 440.0f, (stretch - 64.0f) / 63.0f);
     90 
     91     float lfofreq;
     92     if (!lfopars.numerator || !lfopars.denominator) {
     93         lfofreq = lfopars.freq * lfostretch;
     94     } else {
     95         tempo = time.tempo;
     96         lfofreq = float(tempo) * float(lfopars.denominator)/(240.0f * float(lfopars.numerator));
     97     }
     98     phaseInc = fabsf(lfofreq) * dt;
     99 
    100     //Limit the Frequency(or else...)
    101     if(phaseInc > 0.49999999f)
    102         phaseInc = 0.499999999f;
    103 }
    104 
    105 float LFO::baseOut(const char waveShape, const float phase)
    106 {
    107     float lfo_out;
    108     switch(waveShape) {
    109         case LFO_TRIANGLE:
    110             if(phase >= 0.0f && phase < 0.25f)
    111                 return 4.0f * phase;
    112             else if(phase > 0.25f && phase < 0.75f)
    113                 return 2 - 4 * phase;
    114             else
    115                 return 4.0f * phase - 4.0f;
    116             break;
    117         case LFO_SQUARE:
    118             if(phase < 0.5f)
    119                 lfo_out = -1;
    120             else
    121                 lfo_out = 1;
    122 
    123             return biquad(lfo_out);
    124             break;
    125         case LFO_RAMPUP:    return (phase - 0.5f) * 2.0f;
    126         case LFO_RAMPDOWN:  return (0.5f - phase) * 2.0f;
    127         case LFO_EXP_DOWN1: return powf(0.05f, phase) * 2.0f - 1.0f;
    128         case LFO_EXP_DOWN2: return powf(0.001f, phase) * 2.0f - 1.0f;
    129         case LFO_RANDOM:
    130             if ((phase < 0.5) != first_half) {
    131                 first_half = phase < 0.5;
    132                 last_random = 2*RND-1;
    133             }
    134             return biquad(last_random);
    135             break;
    136         default:
    137             return cosf(phase * 2.0f * PI); //LFO_SINE
    138     }
    139 }
    140 
    141 float LFO::biquad(float input)
    142 {
    143     float output;
    144     if (lfopars.Pcutoff!=cutoff ) // calculate coeffs only if cutoff changed
    145     {
    146         cutoff = lfopars.Pcutoff;
    147         if (cutoff != 127) // at cutoff 127 we bypass filter, no coeffs needed
    148         {
    149             // calculate biquad coefficients
    150             FcAbs = (cutoff + 7.0f)*(cutoff + 7.0f)/ 450.56f; // max value < 40
    151             K = tan(PI * limit(FcAbs * dt,0.001f,0.4f)); // FcRel * dt_ max 40 * 0.01 = 0.4,
    152             // LIMIT in case of LFO sampling frequency lower than 100 Hz
    153 
    154             norm = 1 / (1 + K / 0.7071f + K * K);
    155             a0 = K * K * norm;
    156             a1 = 2 * a0;
    157             a2 = a0;
    158             b1 = 2 * (K * K - 1) * norm;
    159             b2 = (1 - K / 0.7071f + K * K) * norm;
    160         }
    161     }
    162     if (cutoff != 127) // at cutoff 127 we bypass filter, nothing to do
    163     {
    164         // lp filter the (s&h) random LFO
    165         output = limit(input * a0 + z1, -1.0f, 1.0f);
    166         z1 = input * a1 + z2 - b1 * output;
    167         z2 = input * a2 - b2 * output;
    168     }
    169     return (cutoff==127) ? input : output; // at cutoff 127 bypass filter
    170 }
    171 
    172 void LFO::releasekey()
    173 {
    174     if (lfopars.fadeout==10.0f) { // deactivated
    175         fadeOutDuration = 0.0f;
    176         return;
    177     }
    178     // store current ramp value in case of release while fading in
    179     rampOnRelease = rampUp;
    180     // burn in the current amount of outStartValue
    181     // therefor multiply its current reverse ramping factor
    182     // and divide by current ramp factor. It will be multiplied during fading out.
    183     outStartValue *= (1.0f - rampOnRelease);
    184     // store current time
    185     releaseTimestamp = lfopars.time->time();
    186     // calculate fade out duration in frames
    187     fadeOutDuration = lfopars.fadeout * lfopars.time->framesPerSec();
    188     // set fadeout state
    189     lfo_state = lfo_state_type::fadingOut;
    190 }
    191 float LFO::lfoout()
    192 {
    193     //update internals
    194     if ( ! lfopars.time || lfopars.last_update_timestamp == lfopars.time->time())
    195     {
    196         updatePars();
    197         switch(lfopars.fel) {
    198 
    199             case consumer_location_type_t::amp:
    200                 lfointensity = lfopars.Pintensity / 127.0f; // [0...1]
    201                 break;
    202             case consumer_location_type_t::filter:
    203                 lfointensity = lfopars.Pintensity / 127.0f * 4.0f; // [0...4] octaves
    204                 break;
    205             case consumer_location_type_t::freq:
    206             case consumer_location_type_t::unspecified:
    207                 lfointensity = powf(2, lfopars.Pintensity / 127.0f * 11.0f) - 1.0f; // [0...2047] cent
    208                 break;
    209         }
    210     }
    211 
    212     // refresh freq if tempo has changed
    213     if (lfopars.numerator && lfopars.denominator && tempo != time.tempo) {
    214         tempo = time.tempo;
    215         float lfofreq = float(tempo) * float(lfopars.denominator)/(240.0f * float(lfopars.numerator));
    216         phaseInc = fabsf(lfofreq) * dt;
    217     }
    218     float phaseWithStartphase = fmod(phase + (lfopars.Pstartphase + 63.0f) / 127.0f, 1.0f);
    219     float out = baseOut(waveShape, phaseWithStartphase);
    220     if(waveShape == LFO_SINE || waveShape == LFO_TRIANGLE)
    221         out *= lfointensity * (amp1 + phaseWithStartphase * (amp2 - amp1));
    222     else
    223         out *= lfointensity * amp2;
    224 
    225 
    226     // handle lfo state (delay, fade in, fade out)
    227     switch(lfo_state) {
    228         case delaying:
    229 
    230             outStartValue = out; // keep start value to prevent jump
    231             if (delayTime.inFuture()) {
    232                 return out;
    233             }else{
    234                 fadeInTimestamp = lfopars.time->time();
    235                 fadeInDuration = lfopars.fadein * lfopars.time->framesPerSec();
    236                 lfo_state = lfo_state_type::fadingIn;
    237             }
    238 
    239             break;
    240 
    241         case fadingIn:
    242 
    243             if (fadeInDuration && rampUp < 1.0) {
    244                 rampUp = ((float)(lfopars.time->time() - fadeInTimestamp) / (float)fadeInDuration);
    245                 rampUp *= rampUp; // square for soft start
    246 
    247             }
    248             else {
    249                 rampUp = 1.0f;
    250                 lfo_state = lfo_state_type::running;
    251             }
    252 
    253             out *= rampUp;
    254             out += outStartValue * (1.0f-rampUp);
    255 
    256             break;
    257 
    258         case fadingOut:
    259             if(fadeOutDuration && rampDown) {// no division by zero, please
    260                 rampDown = 1.0f - ( (float)(lfopars.time->time() - releaseTimestamp) / (float)fadeOutDuration );
    261                 rampDown *= rampDown; // square for soft end
    262             }
    263             else // no ramp down
    264                 rampDown = 0.0f;
    265 
    266 
    267             out *= rampOnRelease * rampDown;
    268             out += outStartValue*rampDown;
    269 
    270             break;
    271 
    272         case running:
    273         default:
    274             break;
    275     }
    276 
    277     //Start oscillating
    278     if(deterministic)
    279         phase += phaseInc;
    280     else {
    281         const float tmp = (incrnd * (1.0f - phase) + nextincrnd * phase);
    282         phase += phaseInc * tmp;
    283     }
    284     if(phase >= 1) {
    285         phase    = fmod(phase, 1.0f);
    286         amp1 = amp2;
    287         amp2 = (1 - lfornd) + lfornd * RND;
    288 
    289         computeNextFreqRnd();
    290     }
    291 
    292     float watch_data[2] = {phaseWithStartphase, out};
    293     watchOut(watch_data, 2);
    294 
    295     return out;
    296 }
    297 
    298 /*
    299  * LFO out (for amplitude)
    300  */
    301 float LFO::amplfoout()
    302 {
    303     return limit(1.0f - lfointensity + lfoout(), -1.0f, 1.0f);
    304 }
    305 
    306 
    307 void LFO::computeNextFreqRnd()
    308 {
    309     if(deterministic)
    310         return;
    311     incrnd     = nextincrnd;
    312     lfofreqrnd = powf(lfopars.Pfreqrand / 127.0f, 2.0f) * 4.0f;
    313     // old implementation:
    314     // nextincrnd = powf(0.5f, lfofreqrnd) + RND * (powf(2.0f, lfofreqrnd) - 1.0f);
    315     // problem with that old implementation is that it changes the center frequency.
    316     // the new one doesn't
    317     const float rndValue = lfofreqrnd*(RND*2.0f - 1.0);
    318     nextincrnd = powf(2.0f, rndValue);
    319 }
    320 
    321 }