zynaddsubfx

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

Echo.cpp (7660B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   Echo.cpp - Echo effect
      5   Copyright (C) 2002-2005 Nasca Octavian Paul
      6   Copyright (C) 2009-2010 Mark McCurry
      7   Author: Nasca Octavian Paul
      8           Mark McCurry
      9 
     10   This program is free software; you can redistribute it and/or
     11   modify it under the terms of the GNU General Public License
     12   as published by the Free Software Foundation; either version 2
     13   of the License, or (at your option) any later version.
     14 */
     15 
     16 #include <cmath>
     17 #include <rtosc/ports.h>
     18 #include <rtosc/port-sugar.h>
     19 #include "../Misc/Allocator.h"
     20 #include "Echo.h"
     21 
     22 #define MAX_DELAY 2
     23 
     24 namespace zyn {
     25 
     26 #define rObject Echo
     27 #define rBegin [](const char *msg, rtosc::RtData &d) {
     28 #define rEnd }
     29 
     30 rtosc::Ports Echo::ports = {
     31     {"preset::i", rOptions(Echo 1, Echo 2, Echo 3, Simple Echo, Canyon, Panning Echo 1,
     32                            Panning Echo 2, Panning Echo 3, Feedback Echo)
     33                   rDefault(0)
     34                   rProp(alias)
     35                   rProp(parameter)
     36                   rDoc("Instrument Presets"), 0,
     37                   rBegin;
     38                   rObject *o = (rObject*)d.obj;
     39                   if(rtosc_narguments(msg))
     40                       o->setpreset(rtosc_argument(msg, 0).i);
     41                   else
     42                       d.reply(d.loc, "i", o->Ppreset);
     43                   rEnd},
     44     rPresetForVolume,
     45     rEffParVol(rDefaultDepends(presetOfVolume), rDefault(67),
     46         rPresetsAt(6, 81, 81, 62),
     47         rPresetsAt(16, 33, 33, 33, 33, 33, 33, 40, 40, 31)),
     48     rEffParPan(rDefaultDepends(preset), rPresetsAt(2, 75, 60, 60, 64, 60, 60)),
     49     rEffPar(Pdelay,   2, rShort("delay"), rLinear(0, 127),
     50             rPresets(35, 21, 60, 44, 102, 44, 46, 26, 28),
     51             "Length of Echo"),
     52     rEffPar(Plrdelay, 3, rShort("lr delay"),
     53             rPresetsAt(4, 50, 17, 118, 100, 64), rDefault(64),
     54             "Difference In Left/Right Delay"),
     55     rEffPar(Plrcross, 4, rShort("cross"),
     56             rPresetsAt(5, 0, 100, 127, 100), rDefault(30),
     57             "Left/Right Crossover"),
     58     rEffPar(Pfb,      5, rShort("feedback"),
     59             rPresets(59, 59, 59, 0, 82, 82, 68, 67, 90),
     60             "Echo Feedback"),
     61     rEffPar(Phidamp,  6, rShort("damp"),
     62             rPresets(0, 0, 10, 0, 48, 24, 18, 36, 55),
     63             "Dampen High Frequencies"),
     64 };
     65 #undef rBegin
     66 #undef rEnd
     67 #undef rObject
     68 
     69 Echo::Echo(EffectParams pars)
     70     :Effect(pars),
     71       Pvolume(50),
     72       Pdelay(60),
     73       Plrdelay(100),
     74       Pfb(40),
     75       Phidamp(60),
     76       delayTime(1),
     77       lrdelay(0),
     78       avgDelay(0),
     79       delay(memory.valloc<float>(MAX_DELAY * pars.srate),
     80             memory.valloc<float>(MAX_DELAY * pars.srate)),
     81       old(0.0f),
     82       pos(0),
     83       delta(1),
     84       ndelta(1)
     85 {
     86     initdelays();
     87     setpreset(Ppreset);
     88 }
     89 
     90 Echo::~Echo()
     91 {
     92     memory.devalloc(delay.l);
     93     memory.devalloc(delay.r);
     94 }
     95 
     96 //Cleanup the effect
     97 void Echo::cleanup(void)
     98 {
     99     memset(delay.l, 0, MAX_DELAY * samplerate * sizeof(float));
    100     memset(delay.r, 0, MAX_DELAY * samplerate * sizeof(float));
    101     old = Stereo<float>(0.0f);
    102 }
    103 
    104 inline int max(int a, int b)
    105 {
    106     return a > b ? a : b;
    107 }
    108 
    109 //Initialize the delays
    110 void Echo::initdelays(void)
    111 {
    112     cleanup();
    113     //number of seconds to delay left chan
    114     float dl = avgDelay - lrdelay;
    115 
    116     //number of seconds to delay right chan
    117     float dr = avgDelay + lrdelay;
    118 
    119     ndelta.l = max(1, (int) (dl * samplerate));
    120     ndelta.r = max(1, (int) (dr * samplerate));
    121     delta = ndelta;
    122 }
    123 
    124 //Effect output
    125 void Echo::out(const Stereo<float *> &input)
    126 {
    127     for(int i = 0; i < buffersize; ++i) {
    128         float ldl = delay.l[pos.l];
    129         float rdl = delay.r[pos.r];
    130         ldl = ldl * (1.0f - lrcross) + rdl * lrcross;
    131         rdl = rdl * (1.0f - lrcross) + ldl * lrcross;
    132 
    133         efxoutl[i] = ldl * 2.0f;
    134         efxoutr[i] = rdl * 2.0f;
    135 
    136         ldl = input.l[i] * pangainL - ldl * fb;
    137         rdl = input.r[i] * pangainR - rdl * fb;
    138 
    139         //LowPass Filter
    140         old.l = delay.l[(pos.l + delta.l) % (MAX_DELAY * samplerate)] =
    141                     ldl * hidamp + old.l * (1.0f - hidamp);
    142         old.r = delay.r[(pos.r + delta.r) % (MAX_DELAY * samplerate)] =
    143                     rdl * hidamp + old.r * (1.0f - hidamp);
    144 
    145         //increment
    146         ++pos.l; // += delta.l;
    147         ++pos.r; // += delta.r;
    148 
    149         //ensure that pos is still in bounds
    150         pos.l %= MAX_DELAY * samplerate;
    151         pos.r %= MAX_DELAY * samplerate;
    152 
    153         //adjust delay if needed
    154         delta.l = (15 * delta.l + ndelta.l) / 16;
    155         delta.r = (15 * delta.r + ndelta.r) / 16;
    156     }
    157 }
    158 
    159 
    160 //Parameter control
    161 void Echo::setvolume(unsigned char _Pvolume)
    162 {
    163     Pvolume = _Pvolume;
    164 
    165     if(insertion == 0) {
    166         if (Pvolume == 0) {
    167             outvolume = 0.0f;
    168         } else {
    169             outvolume = powf(0.01f, (1.0f - Pvolume / 127.0f)) * 4.0f;
    170         }
    171         volume    = 1.0f;
    172     }
    173     else
    174         volume = outvolume = Pvolume / 127.0f;
    175     if(Pvolume == 0)
    176         cleanup();
    177 }
    178 
    179 void Echo::setdelay(unsigned char _Pdelay)
    180 {
    181     Pdelay   = _Pdelay;
    182     avgDelay = (Pdelay / 127.0f * 1.5f); //0 .. 1.5 sec
    183     initdelays();
    184 }
    185 
    186 void Echo::setlrdelay(unsigned char _Plrdelay)
    187 {
    188     float tmp;
    189     Plrdelay = _Plrdelay;
    190     tmp      =
    191         (powf(2.0f, fabsf(Plrdelay - 64.0f) / 64.0f * 9.0f) - 1.0f) / 1000.0f;
    192     if(Plrdelay < 64.0f)
    193         tmp = -tmp;
    194     lrdelay = tmp;
    195     initdelays();
    196 }
    197 
    198 void Echo::setfb(unsigned char _Pfb)
    199 {
    200     Pfb = _Pfb;
    201     fb  = Pfb / 128.0f;
    202 }
    203 
    204 void Echo::sethidamp(unsigned char _Phidamp)
    205 {
    206     Phidamp = _Phidamp;
    207     hidamp  = 1.0f - Phidamp / 127.0f;
    208 }
    209 
    210 unsigned char Echo::getpresetpar(unsigned char npreset, unsigned int npar)
    211 {
    212 #define	PRESET_SIZE 7
    213 #define	NUM_PRESETS 9
    214     static const unsigned char presets[NUM_PRESETS][PRESET_SIZE] = {
    215         {67, 64, 35,  64,  30,  59, 0 }, //Echo 1
    216         {67, 64, 21,  64,  30,  59, 0 }, //Echo 2
    217         {67, 75, 60,  64,  30,  59, 10}, //Echo 3
    218         {67, 60, 44,  64,  30,  0,  0 }, //Simple Echo
    219         {67, 60, 102, 50,  30,  82, 48}, //Canyon
    220         {67, 64, 44,  17,  0,   82, 24}, //Panning Echo 1
    221         {81, 60, 46,  118, 100, 68, 18}, //Panning Echo 2
    222         {81, 60, 26,  100, 127, 67, 36}, //Panning Echo 3
    223         {62, 64, 28,  64,  100, 90, 55}  //Feedback Echo
    224     };
    225     if(npreset < NUM_PRESETS && npar < PRESET_SIZE) {
    226         if(npar == 0 && insertion != 0) {
    227             /* lower the volume if this is insertion effect */
    228             return presets[npreset][npar] / 2;
    229         }
    230         return presets[npreset][npar];
    231     }
    232     return 0;
    233 }
    234 
    235 void Echo::setpreset(unsigned char npreset)
    236 {
    237     if(npreset >= NUM_PRESETS)
    238         npreset = NUM_PRESETS - 1;
    239     for(int n = 0; n != 128; n++)
    240         changepar(n, getpresetpar(npreset, n));
    241     Ppreset = npreset;
    242 }
    243 
    244 void Echo::changepar(int npar, unsigned char value)
    245 {
    246     switch(npar) {
    247         case 0:
    248             setvolume(value);
    249             break;
    250         case 1:
    251             setpanning(value);
    252             break;
    253         case 2:
    254             setdelay(value);
    255             break;
    256         case 3:
    257             setlrdelay(value);
    258             break;
    259         case 4:
    260             setlrcross(value);
    261             break;
    262         case 5:
    263             setfb(value);
    264             break;
    265         case 6:
    266             sethidamp(value);
    267             break;
    268     }
    269 }
    270 
    271 unsigned char Echo::getpar(int npar) const
    272 {
    273     switch(npar) {
    274         case 0:  return Pvolume;
    275         case 1:  return Ppanning;
    276         case 2:  return Pdelay;
    277         case 3:  return Plrdelay;
    278         case 4:  return Plrcross;
    279         case 5:  return Pfb;
    280         case 6:  return Phidamp;
    281         default: return 0; // in case of bogus parameter number
    282     }
    283 }
    284 
    285 }