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 }