zynaddsubfx

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

PADnote.cpp (15321B)


      1 /*
      2   ZynAddSubFX - a software synthesizer
      3 
      4   pADnote.cpp - The "pad" synthesizer
      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 #include <cassert>
     14 #include <cmath>
     15 #include "PADnote.h"
     16 #include "ModFilter.h"
     17 #include "Portamento.h"
     18 #include "../Misc/Config.h"
     19 #include "../Misc/Allocator.h"
     20 #include "../Params/PADnoteParameters.h"
     21 #include "../Params/Controller.h"
     22 #include "../Params/FilterParams.h"
     23 #include "../Containers/ScratchString.h"
     24 #include "../Containers/NotePool.h"
     25 #include "../Misc/Util.h"
     26 
     27 namespace zyn {
     28 
     29 PADnote::PADnote(const PADnoteParameters *parameters,
     30                  const SynthParams &pars, const int& interpolation, WatchManager *wm,
     31                  const char *prefix)
     32     :SynthNote(pars),
     33     watch_int(wm, prefix, "noteout/after_interpolation"), watch_punch(wm, prefix, "noteout/after_punch"),
     34     watch_amp_int(wm, prefix, "noteout/after_amp_interpolation"), watch_legato(wm, prefix, "noteout/after_legato"),
     35      pars(*parameters),interpolation(interpolation)
     36 {
     37     NoteGlobalPar.GlobalFilter    = nullptr;
     38     NoteGlobalPar.FilterEnvelope  = nullptr;
     39     NoteGlobalPar.FilterLfo       = nullptr;
     40 
     41     firsttime = true;
     42     setup(pars.velocity, pars.portamento, pars.note_log2_freq, false, wm, prefix);
     43 }
     44 
     45 void PADnote::setup(float velocity_,
     46                     Portamento *portamento_,
     47                     float note_log2_freq_,
     48                     bool legato,
     49                     WatchManager *wm,
     50                     const char *prefix)
     51 {
     52     portamento = portamento_;
     53     velocity   = velocity_;
     54     finished_  = false;
     55 
     56     if(pars.Pfixedfreq == 0) {
     57         note_log2_freq = note_log2_freq_;
     58     }
     59     else { //the fixed freq is enabled
     60         const int fixedfreqET = pars.PfixedfreqET;
     61         float fixedfreq_log2 = log2f(440.0f);
     62 
     63         if(fixedfreqET != 0) { //if the frequency varies according the keyboard note
     64             float tmp_log2 = (note_log2_freq_ - fixedfreq_log2) *
     65                 (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
     66             if(fixedfreqET <= 64)
     67                 fixedfreq_log2 += tmp_log2;
     68             else
     69                 fixedfreq_log2 += tmp_log2 * log2f(3.0f);
     70         }
     71         note_log2_freq = fixedfreq_log2;
     72     }
     73     const float basefreq = powf(2.0f, note_log2_freq);
     74 
     75     int BendAdj = pars.PBendAdjust - 64;
     76     if (BendAdj % 24 == 0)
     77         BendAdjust = BendAdj / 24;
     78     else
     79         BendAdjust = BendAdj / 24.0f;
     80     float offset_val = (pars.POffsetHz - 64)/64.0f;
     81     OffsetHz = 15.0f*(offset_val * sqrtf(fabsf(offset_val)));
     82     if(!legato) firsttime = true;
     83     realfreq  = basefreq;
     84     if(!legato)
     85         NoteGlobalPar.Detune = getdetune(pars.PDetuneType, pars.PCoarseDetune,
     86                                          pars.PDetune);
     87 
     88 
     89     //find out the closest note
     90     const float log2freq = note_log2_freq + NoteGlobalPar.Detune / 1200.0f;
     91     float mindist = fabsf(log2freq - log2f(pars.sample[0].basefreq + 0.0001f));
     92     nsample = 0;
     93     for(int i = 1; i < PAD_MAX_SAMPLES; ++i) {
     94         if(pars.sample[i].smp == NULL)
     95             break;
     96         const float dist = fabsf(log2freq - log2f(pars.sample[i].basefreq + 0.0001f));
     97 
     98         if(dist < mindist) {
     99             nsample = i;
    100             mindist = dist;
    101         }
    102     }
    103 
    104     int size = pars.sample[nsample].size;
    105     if(size == 0)
    106         size = 1;
    107 
    108 
    109     if(!legato) { //not sure
    110         poshi_l = (int)(RND * (size - 1));
    111         if(pars.PStereo)
    112             poshi_r = (poshi_l + size / 2) % size;
    113         else
    114             poshi_r = poshi_l;
    115         poslo = 0.0f;
    116     }
    117 
    118 
    119     if(pars.PPanning)
    120         NoteGlobalPar.Panning = pars.PPanning / 128.0f;
    121     else if(!legato)
    122         NoteGlobalPar.Panning = RND;
    123 
    124     if(!legato) {
    125         NoteGlobalPar.Fadein_adjustment =
    126             pars.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE;
    127         NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment;
    128         if(pars.PPunchStrength != 0) {
    129             NoteGlobalPar.Punch.Enabled = 1;
    130             NoteGlobalPar.Punch.t = 1.0f; //start from 1.0f and to 0.0f
    131             NoteGlobalPar.Punch.initialvalue =
    132                 ((powf(10, 1.5f * pars.PPunchStrength / 127.0f) - 1.0f)
    133                  * VelF(velocity,
    134                         pars.PPunchVelocitySensing));
    135             const float time =
    136                 powf(10, 3.0f * pars.PPunchTime / 127.0f) / 10000.0f;             //0.1f .. 100 ms
    137             const float freq = powf(2.0f, note_log2_freq_);
    138             const float stretch = powf(440.0f / freq, pars.PPunchStretch / 64.0f);
    139             NoteGlobalPar.Punch.dt = 1.0f
    140                                      / (time * synth.samplerate_f * stretch);
    141         }
    142         else
    143             NoteGlobalPar.Punch.Enabled = 0;
    144 
    145         ScratchString pre = prefix;
    146 
    147         NoteGlobalPar.FreqEnvelope =
    148             memory.alloc<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt(),
    149                     wm, (pre+"FreqEnvelope/").c_str);
    150         NoteGlobalPar.FreqLfo      =
    151             memory.alloc<LFO>(*pars.FreqLfo, basefreq, time,
    152                     wm, (pre+"FreqLfo/").c_str);
    153 
    154         NoteGlobalPar.AmpEnvelope =
    155             memory.alloc<Envelope>(*pars.AmpEnvelope, basefreq, synth.dt(),
    156                     wm, (pre+"AmpEnvelope/").c_str);
    157         NoteGlobalPar.AmpLfo      =
    158             memory.alloc<LFO>(*pars.AmpLfo, basefreq, time,
    159                     wm, (pre+"AmpLfo/").c_str);
    160     }
    161 
    162     NoteGlobalPar.Volume = 4.0f
    163                            * powf(0.1f, 3.0f * (1.0f - pars.PVolume / 96.0f))      //-60 dB .. 0 dB
    164                            * VelF(velocity, pars.PAmpVelocityScaleFunction); //velocity sensing
    165 
    166     if (!legato) {
    167         NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output
    168         globaloldamplitude = globalnewamplitude = NoteGlobalPar.Volume
    169                                                   * NoteGlobalPar.AmpEnvelope->
    170                                                   envout_dB()
    171                                                   * NoteGlobalPar.AmpLfo->amplfoout();
    172     }
    173 
    174     if(!legato) {
    175         ScratchString pre = prefix;
    176         auto &flt = NoteGlobalPar.GlobalFilter;
    177         auto &env = NoteGlobalPar.FilterEnvelope;
    178         auto &lfo = NoteGlobalPar.FilterLfo;
    179         assert(flt == nullptr);
    180         flt = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, true, basefreq);
    181 
    182         //setup mod
    183         env = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq,
    184                 synth.dt(), wm, (pre+"FilterEnvelope/").c_str);
    185         lfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time,
    186                 wm, (pre+"FilterLfo/").c_str);
    187         flt->addMod(*env);
    188         flt->addMod(*lfo);
    189     }
    190 
    191     {
    192         auto &flt = *NoteGlobalPar.GlobalFilter;
    193         flt.updateSense(velocity, pars.PFilterVelocityScale,
    194                         pars.PFilterVelocityScaleFunction);
    195         flt.updateNoteFreq(basefreq);
    196     }
    197 
    198     if(!pars.sample[nsample].smp) {
    199         finished_ = true;
    200         return;
    201     }
    202 }
    203 
    204 SynthNote *PADnote::cloneLegato(void)
    205 {
    206     SynthParams sp{memory, ctl, synth, time, velocity,
    207                    portamento, legato.param.note_log2_freq, true, legato.param.seed};
    208     return memory.alloc<PADnote>(&pars, sp, interpolation);
    209 }
    210 
    211 void PADnote::legatonote(const LegatoParams &pars)
    212 {
    213     // Manage legato stuff
    214     if(legato.update(pars))
    215         return;
    216 
    217     setup(pars.velocity, pars.portamento, pars.note_log2_freq, true);
    218 }
    219 
    220 
    221 PADnote::~PADnote()
    222 {
    223     memory.dealloc(NoteGlobalPar.FreqEnvelope);
    224     memory.dealloc(NoteGlobalPar.FreqLfo);
    225     memory.dealloc(NoteGlobalPar.AmpEnvelope);
    226     memory.dealloc(NoteGlobalPar.AmpLfo);
    227     memory.dealloc(NoteGlobalPar.GlobalFilter);
    228     memory.dealloc(NoteGlobalPar.FilterEnvelope);
    229     memory.dealloc(NoteGlobalPar.FilterLfo);
    230 }
    231 
    232 
    233 inline void PADnote::fadein(float *smps)
    234 {
    235     int zerocrossings = 0;
    236     for(int i = 1; i < synth.buffersize; ++i)
    237         if((smps[i - 1] < 0.0f) && (smps[i] > 0.0f))
    238             zerocrossings++;                                  //this is only the positive crossings
    239 
    240     float tmp = (synth.buffersize_f - 1.0f) / (zerocrossings + 1) / 3.0f;
    241     if(tmp < 8.0f)
    242         tmp = 8.0f;
    243     tmp *= NoteGlobalPar.Fadein_adjustment;
    244 
    245     int n;
    246     F2I(tmp, n); //how many samples is the fade-in
    247     if(n > synth.buffersize)
    248         n = synth.buffersize;
    249     for(int i = 0; i < n; ++i) { //fade-in
    250         float tmp = 0.5f - cosf((float)i / (float) n * PI) * 0.5f;
    251         smps[i] *= tmp;
    252     }
    253 }
    254 
    255 
    256 void PADnote::computecurrentparameters()
    257 {
    258     const float relfreq = getFilterCutoffRelFreq();
    259     const float globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout()
    260                            + NoteGlobalPar.FreqLfo->lfoout()
    261                            * ctl.modwheel.relmod + NoteGlobalPar.Detune);
    262     globaloldamplitude = globalnewamplitude;
    263     globalnewamplitude = NoteGlobalPar.Volume
    264                          * NoteGlobalPar.AmpEnvelope->envout_dB()
    265                          * NoteGlobalPar.AmpLfo->amplfoout();
    266 
    267     NoteGlobalPar.GlobalFilter->update(relfreq, ctl.filterq.relq);
    268 
    269     //compute the portamento, if it is used by this note
    270     float portamentofreqdelta_log2 = 0.0f;
    271     if(portamento) { //this voice use portamento
    272         portamentofreqdelta_log2 = portamento->freqdelta_log2;
    273         if(!portamento->active) //the portamento has finished
    274             portamento = NULL;  //this note is no longer "portamented"
    275     }
    276 
    277     realfreq =
    278         powf(2.0f, note_log2_freq + globalpitch / 12.0f + portamentofreqdelta_log2) *
    279         powf(ctl.pitchwheel.relfreq, BendAdjust) + OffsetHz;
    280 }
    281 
    282 
    283 int PADnote::Compute_Linear(float *outl,
    284                             float *outr,
    285                             int freqhi,
    286                             float freqlo)
    287 {
    288     const float *smps = pars.sample[nsample].smp;
    289     if(smps == NULL) {
    290         finished_ = true;
    291         return 1;
    292     }
    293     int size = pars.sample[nsample].size;
    294     for(int i = 0; i < synth.buffersize; ++i) {
    295         poshi_l += freqhi;
    296         poshi_r += freqhi;
    297         poslo   += freqlo;
    298         if(poslo >= 1.0f) {
    299             poshi_l += 1;
    300             poshi_r += 1;
    301             poslo   -= 1.0f;
    302         }
    303         if(poshi_l >= size)
    304             poshi_l %= size;
    305         if(poshi_r >= size)
    306             poshi_r %= size;
    307 
    308         outl[i] = smps[poshi_l] * (1.0f - poslo) + smps[poshi_l + 1] * poslo;
    309         outr[i] = smps[poshi_r] * (1.0f - poslo) + smps[poshi_r + 1] * poslo;
    310     }
    311     return 1;
    312 }
    313 int PADnote::Compute_Cubic(float *outl,
    314                            float *outr,
    315                            int freqhi,
    316                            float freqlo)
    317 {
    318     float *smps = pars.sample[nsample].smp;
    319     if(smps == NULL) {
    320         finished_ = true;
    321         return 1;
    322     }
    323     int   size = pars.sample[nsample].size;
    324     float xm1, x0, x1, x2, a, b, c;
    325     for(int i = 0; i < synth.buffersize; ++i) {
    326         poshi_l += freqhi;
    327         poshi_r += freqhi;
    328         poslo   += freqlo;
    329         if(poslo >= 1.0f) {
    330             poshi_l += 1;
    331             poshi_r += 1;
    332             poslo   -= 1.0f;
    333         }
    334         if(poshi_l >= size)
    335             poshi_l %= size;
    336         if(poshi_r >= size)
    337             poshi_r %= size;
    338 
    339 
    340         //left
    341         xm1     = smps[poshi_l];
    342         x0      = smps[poshi_l + 1];
    343         x1      = smps[poshi_l + 2];
    344         x2      = smps[poshi_l + 3];
    345         a       = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f;
    346         b       = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f;
    347         c       = (x1 - xm1) * 0.5f;
    348         outl[i] = (((a * poslo) + b) * poslo + c) * poslo + x0;
    349         //right
    350         xm1     = smps[poshi_r];
    351         x0      = smps[poshi_r + 1];
    352         x1      = smps[poshi_r + 2];
    353         x2      = smps[poshi_r + 3];
    354         a       = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f;
    355         b       = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f;
    356         c       = (x1 - xm1) * 0.5f;
    357         outr[i] = (((a * poslo) + b) * poslo + c) * poslo + x0;
    358     }
    359     return 1;
    360 }
    361 
    362 
    363 int PADnote::noteout(float *outl, float *outr)
    364 {
    365     computecurrentparameters();
    366     float *smps = pars.sample[nsample].smp;
    367     if(smps == NULL) {
    368         for(int i = 0; i < synth.buffersize; ++i) {
    369             outl[i] = 0.0f;
    370             outr[i] = 0.0f;
    371         }
    372         return 1;
    373     }
    374     float smpfreq = pars.sample[nsample].basefreq;
    375 
    376 
    377     float freqrap = realfreq / smpfreq;
    378     int   freqhi  = (int) (floor(freqrap));
    379     float freqlo  = freqrap - floorf(freqrap);
    380 
    381 
    382     if(interpolation)
    383         Compute_Cubic(outl, outr, freqhi, freqlo);
    384     else
    385         Compute_Linear(outl, outr, freqhi, freqlo);
    386 
    387     watch_int(outl,synth.buffersize);
    388 
    389     if(firsttime) {
    390         fadein(outl);
    391         fadein(outr);
    392         firsttime = false;
    393     }
    394 
    395     NoteGlobalPar.GlobalFilter->filter(outl, outr);
    396 
    397     //Apply the punch
    398     if(NoteGlobalPar.Punch.Enabled != 0)
    399         for(int i = 0; i < synth.buffersize; ++i) {
    400             float punchamp = NoteGlobalPar.Punch.initialvalue
    401                              * NoteGlobalPar.Punch.t + 1.0f;
    402             outl[i] *= punchamp;
    403             outr[i] *= punchamp;
    404             NoteGlobalPar.Punch.t -= NoteGlobalPar.Punch.dt;
    405             if(NoteGlobalPar.Punch.t < 0.0f) {
    406                 NoteGlobalPar.Punch.Enabled = 0;
    407                 break;
    408             }
    409         }
    410 
    411     watch_punch(outl,synth.buffersize);
    412 
    413     if(ABOVE_AMPLITUDE_THRESHOLD(globaloldamplitude, globalnewamplitude))
    414         // Amplitude Interpolation
    415         for(int i = 0; i < synth.buffersize; ++i) {
    416             float tmpvol = INTERPOLATE_AMPLITUDE(globaloldamplitude,
    417                                                  globalnewamplitude,
    418                                                  i,
    419                                                  synth.buffersize);
    420             outl[i] *= tmpvol * (1.0f - NoteGlobalPar.Panning);
    421             outr[i] *= tmpvol * NoteGlobalPar.Panning;
    422         }
    423     else
    424         for(int i = 0; i < synth.buffersize; ++i) {
    425             outl[i] *= globalnewamplitude * (1.0f - NoteGlobalPar.Panning);
    426             outr[i] *= globalnewamplitude * NoteGlobalPar.Panning;
    427         }
    428 
    429     watch_amp_int(outl,synth.buffersize);
    430 
    431     // Apply legato-specific sound signal modifications
    432     legato.apply(*this, outl, outr);
    433 
    434     watch_legato(outl,synth.buffersize);
    435 
    436     // Check if the global amplitude is finished.
    437     // If it does, disable the note
    438     if(NoteGlobalPar.AmpEnvelope->finished()) {
    439         for(int i = 0; i < synth.buffersize; ++i) { //fade-out
    440             float tmp = 1.0f - (float)i / synth.buffersize_f;
    441             outl[i] *= tmp;
    442             outr[i] *= tmp;
    443         }
    444         finished_ = 1;
    445     }
    446 
    447     return 1;
    448 }
    449 
    450 bool PADnote::finished() const
    451 {
    452     return finished_;
    453 }
    454 
    455 void PADnote::entomb(void)
    456 {
    457     NoteGlobalPar.AmpEnvelope->forceFinish();
    458 }
    459 
    460 void PADnote::releasekey()
    461 {
    462     NoteGlobalPar.FreqEnvelope->releasekey();
    463     NoteGlobalPar.FilterEnvelope->releasekey();
    464     NoteGlobalPar.AmpEnvelope->releasekey();
    465     NoteGlobalPar.FreqLfo->releasekey();
    466     NoteGlobalPar.FilterLfo->releasekey();
    467     NoteGlobalPar.AmpLfo->releasekey();
    468 }
    469 
    470 }