Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

432 lines
13KB

  1. /*
  2. ZynAddSubFX - a software synthesizer
  3. pADnote.cpp - The "pad" synthesizer
  4. Copyright (C) 2002-2005 Nasca Octavian Paul
  5. Author: Nasca Octavian Paul
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or (at your option) any later version.
  10. */
  11. #include <cassert>
  12. #include <cmath>
  13. #include "PADnote.h"
  14. #include "ModFilter.h"
  15. #include "../Misc/Config.h"
  16. #include "../Misc/Allocator.h"
  17. #include "../Params/PADnoteParameters.h"
  18. #include "../Params/Controller.h"
  19. #include "../Params/FilterParams.h"
  20. #include "../Misc/Util.h"
  21. PADnote::PADnote(const PADnoteParameters *parameters,
  22. SynthParams pars, const int& interpolation)
  23. :SynthNote(pars), pars(*parameters), interpolation(interpolation)
  24. {
  25. NoteGlobalPar.GlobalFilter = nullptr;
  26. NoteGlobalPar.FilterEnvelope = nullptr;
  27. NoteGlobalPar.FilterLfo = nullptr;
  28. firsttime = true;
  29. setup(pars.frequency, pars.velocity, pars.portamento, pars.note);
  30. }
  31. void PADnote::setup(float freq,
  32. float velocity_,
  33. int portamento_,
  34. int midinote,
  35. bool legato)
  36. {
  37. portamento = portamento_;
  38. velocity = velocity_;
  39. finished_ = false;
  40. if(!pars.Pfixedfreq)
  41. basefreq = freq;
  42. else {
  43. basefreq = 440.0f;
  44. int fixedfreqET = pars.PfixedfreqET;
  45. if(fixedfreqET != 0) { //if the frequency varies according the keyboard note
  46. float tmp =
  47. (midinote
  48. - 69.0f) / 12.0f
  49. * (powf(2.0f, (fixedfreqET - 1) / 63.0f) - 1.0f);
  50. if(fixedfreqET <= 64)
  51. basefreq *= powf(2.0f, tmp);
  52. else
  53. basefreq *= powf(3.0f, tmp);
  54. }
  55. }
  56. int BendAdj = pars.PBendAdjust - 64;
  57. if (BendAdj % 24 == 0)
  58. BendAdjust = BendAdj / 24;
  59. else
  60. BendAdjust = BendAdj / 24.0f;
  61. float offset_val = (pars.POffsetHz - 64)/64.0f;
  62. OffsetHz = 15.0f*(offset_val * sqrtf(fabsf(offset_val)));
  63. firsttime = true;
  64. realfreq = basefreq;
  65. if(!legato)
  66. NoteGlobalPar.Detune = getdetune(pars.PDetuneType, pars.PCoarseDetune,
  67. pars.PDetune);
  68. //find out the closest note
  69. float logfreq = logf(basefreq * powf(2.0f, NoteGlobalPar.Detune / 1200.0f));
  70. float mindist = fabs(logfreq - logf(pars.sample[0].basefreq + 0.0001f));
  71. nsample = 0;
  72. for(int i = 1; i < PAD_MAX_SAMPLES; ++i) {
  73. if(pars.sample[i].smp == NULL)
  74. break;
  75. float dist = fabs(logfreq - logf(pars.sample[i].basefreq + 0.0001f));
  76. if(dist < mindist) {
  77. nsample = i;
  78. mindist = dist;
  79. }
  80. }
  81. int size = pars.sample[nsample].size;
  82. if(size == 0)
  83. size = 1;
  84. if(!legato) { //not sure
  85. poshi_l = (int)(RND * (size - 1));
  86. if(pars.PStereo)
  87. poshi_r = (poshi_l + size / 2) % size;
  88. else
  89. poshi_r = poshi_l;
  90. poslo = 0.0f;
  91. }
  92. if(pars.PPanning == 0)
  93. NoteGlobalPar.Panning = RND;
  94. else
  95. NoteGlobalPar.Panning = pars.PPanning / 128.0f;
  96. if(!legato) {
  97. NoteGlobalPar.Fadein_adjustment =
  98. pars.Fadein_adjustment / (float)FADEIN_ADJUSTMENT_SCALE;
  99. NoteGlobalPar.Fadein_adjustment *= NoteGlobalPar.Fadein_adjustment;
  100. if(pars.PPunchStrength != 0) {
  101. NoteGlobalPar.Punch.Enabled = 1;
  102. NoteGlobalPar.Punch.t = 1.0f; //start from 1.0f and to 0.0f
  103. NoteGlobalPar.Punch.initialvalue =
  104. ((powf(10, 1.5f * pars.PPunchStrength / 127.0f) - 1.0f)
  105. * VelF(velocity,
  106. pars.PPunchVelocitySensing));
  107. const float time =
  108. powf(10, 3.0f * pars.PPunchTime / 127.0f) / 10000.0f; //0.1f .. 100 ms
  109. const float stretch = powf(440.0f / freq, pars.PPunchStretch / 64.0f);
  110. NoteGlobalPar.Punch.dt = 1.0f
  111. / (time * synth.samplerate_f * stretch);
  112. }
  113. else
  114. NoteGlobalPar.Punch.Enabled = 0;
  115. NoteGlobalPar.FreqEnvelope = memory.alloc<Envelope>(*pars.FreqEnvelope, basefreq, synth.dt());
  116. NoteGlobalPar.FreqLfo = memory.alloc<LFO>(*pars.FreqLfo, basefreq, time);
  117. NoteGlobalPar.AmpEnvelope = memory.alloc<Envelope>(*pars.AmpEnvelope, basefreq, synth.dt());
  118. NoteGlobalPar.AmpLfo = memory.alloc<LFO>(*pars.AmpLfo, basefreq, time);
  119. }
  120. NoteGlobalPar.Volume = 4.0f
  121. * powf(0.1f, 3.0f * (1.0f - pars.PVolume / 96.0f)) //-60 dB .. 0 dB
  122. * VelF(velocity, pars.PAmpVelocityScaleFunction); //velocity sensing
  123. NoteGlobalPar.AmpEnvelope->envout_dB(); //discard the first envelope output
  124. globaloldamplitude = globalnewamplitude = NoteGlobalPar.Volume
  125. * NoteGlobalPar.AmpEnvelope->
  126. envout_dB()
  127. * NoteGlobalPar.AmpLfo->amplfoout();
  128. if(!legato) {
  129. auto &flt = NoteGlobalPar.GlobalFilter;
  130. auto &env = NoteGlobalPar.FilterEnvelope;
  131. auto &lfo = NoteGlobalPar.FilterLfo;
  132. assert(flt == nullptr);
  133. flt = memory.alloc<ModFilter>(*pars.GlobalFilter, synth, time, memory, true, basefreq);
  134. //setup mod
  135. env = memory.alloc<Envelope>(*pars.FilterEnvelope, basefreq, synth.dt());
  136. lfo = memory.alloc<LFO>(*pars.FilterLfo, basefreq, time);
  137. flt->addMod(*env);
  138. flt->addMod(*lfo);
  139. }
  140. {
  141. auto &flt = *NoteGlobalPar.GlobalFilter;
  142. flt.updateSense(velocity, pars.PFilterVelocityScale,
  143. pars.PFilterVelocityScaleFunction);
  144. flt.updateNoteFreq(basefreq);
  145. }
  146. if(!pars.sample[nsample].smp) {
  147. finished_ = true;
  148. return;
  149. }
  150. }
  151. SynthNote *PADnote::cloneLegato(void)
  152. {
  153. SynthParams sp{memory, ctl, synth, time, legato.param.freq, velocity,
  154. (bool)portamento, legato.param.midinote, true};
  155. return memory.alloc<PADnote>(&pars, sp, interpolation);
  156. }
  157. void PADnote::legatonote(LegatoParams pars)
  158. {
  159. // Manage legato stuff
  160. if(legato.update(pars))
  161. return;
  162. setup(pars.frequency, pars.velocity, pars.portamento, pars.midinote, true);
  163. }
  164. PADnote::~PADnote()
  165. {
  166. memory.dealloc(NoteGlobalPar.FreqEnvelope);
  167. memory.dealloc(NoteGlobalPar.FreqLfo);
  168. memory.dealloc(NoteGlobalPar.AmpEnvelope);
  169. memory.dealloc(NoteGlobalPar.AmpLfo);
  170. memory.dealloc(NoteGlobalPar.GlobalFilter);
  171. memory.dealloc(NoteGlobalPar.FilterEnvelope);
  172. memory.dealloc(NoteGlobalPar.FilterLfo);
  173. }
  174. inline void PADnote::fadein(float *smps)
  175. {
  176. int zerocrossings = 0;
  177. for(int i = 1; i < synth.buffersize; ++i)
  178. if((smps[i - 1] < 0.0f) && (smps[i] > 0.0f))
  179. zerocrossings++; //this is only the possitive crossings
  180. float tmp = (synth.buffersize_f - 1.0f) / (zerocrossings + 1) / 3.0f;
  181. if(tmp < 8.0f)
  182. tmp = 8.0f;
  183. tmp *= NoteGlobalPar.Fadein_adjustment;
  184. int n;
  185. F2I(tmp, n); //how many samples is the fade-in
  186. if(n > synth.buffersize)
  187. n = synth.buffersize;
  188. for(int i = 0; i < n; ++i) { //fade-in
  189. float tmp = 0.5f - cosf((float)i / (float) n * PI) * 0.5f;
  190. smps[i] *= tmp;
  191. }
  192. }
  193. void PADnote::computecurrentparameters()
  194. {
  195. const float globalpitch = 0.01f * (NoteGlobalPar.FreqEnvelope->envout()
  196. + NoteGlobalPar.FreqLfo->lfoout()
  197. * ctl.modwheel.relmod + NoteGlobalPar.Detune);
  198. globaloldamplitude = globalnewamplitude;
  199. globalnewamplitude = NoteGlobalPar.Volume
  200. * NoteGlobalPar.AmpEnvelope->envout_dB()
  201. * NoteGlobalPar.AmpLfo->amplfoout();
  202. NoteGlobalPar.GlobalFilter->update(ctl.filtercutoff.relfreq,
  203. ctl.filterq.relq);
  204. //compute the portamento, if it is used by this note
  205. float portamentofreqrap = 1.0f;
  206. if(portamento) { //this voice use portamento
  207. portamentofreqrap = ctl.portamento.freqrap;
  208. if(ctl.portamento.used == 0) //the portamento has finished
  209. portamento = false; //this note is no longer "portamented"
  210. }
  211. realfreq = basefreq * portamentofreqrap
  212. * powf(2.0f, globalpitch / 12.0f)
  213. * powf(ctl.pitchwheel.relfreq, BendAdjust) + OffsetHz;
  214. }
  215. int PADnote::Compute_Linear(float *outl,
  216. float *outr,
  217. int freqhi,
  218. float freqlo)
  219. {
  220. float *smps = pars.sample[nsample].smp;
  221. if(smps == NULL) {
  222. finished_ = true;
  223. return 1;
  224. }
  225. int size = pars.sample[nsample].size;
  226. for(int i = 0; i < synth.buffersize; ++i) {
  227. poshi_l += freqhi;
  228. poshi_r += freqhi;
  229. poslo += freqlo;
  230. if(poslo >= 1.0f) {
  231. poshi_l += 1;
  232. poshi_r += 1;
  233. poslo -= 1.0f;
  234. }
  235. if(poshi_l >= size)
  236. poshi_l %= size;
  237. if(poshi_r >= size)
  238. poshi_r %= size;
  239. outl[i] = smps[poshi_l] * (1.0f - poslo) + smps[poshi_l + 1] * poslo;
  240. outr[i] = smps[poshi_r] * (1.0f - poslo) + smps[poshi_r + 1] * poslo;
  241. }
  242. return 1;
  243. }
  244. int PADnote::Compute_Cubic(float *outl,
  245. float *outr,
  246. int freqhi,
  247. float freqlo)
  248. {
  249. float *smps = pars.sample[nsample].smp;
  250. if(smps == NULL) {
  251. finished_ = true;
  252. return 1;
  253. }
  254. int size = pars.sample[nsample].size;
  255. float xm1, x0, x1, x2, a, b, c;
  256. for(int i = 0; i < synth.buffersize; ++i) {
  257. poshi_l += freqhi;
  258. poshi_r += freqhi;
  259. poslo += freqlo;
  260. if(poslo >= 1.0f) {
  261. poshi_l += 1;
  262. poshi_r += 1;
  263. poslo -= 1.0f;
  264. }
  265. if(poshi_l >= size)
  266. poshi_l %= size;
  267. if(poshi_r >= size)
  268. poshi_r %= size;
  269. //left
  270. xm1 = smps[poshi_l];
  271. x0 = smps[poshi_l + 1];
  272. x1 = smps[poshi_l + 2];
  273. x2 = smps[poshi_l + 3];
  274. a = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f;
  275. b = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f;
  276. c = (x1 - xm1) * 0.5f;
  277. outl[i] = (((a * poslo) + b) * poslo + c) * poslo + x0;
  278. //right
  279. xm1 = smps[poshi_r];
  280. x0 = smps[poshi_r + 1];
  281. x1 = smps[poshi_r + 2];
  282. x2 = smps[poshi_r + 3];
  283. a = (3.0f * (x0 - x1) - xm1 + x2) * 0.5f;
  284. b = 2.0f * x1 + xm1 - (5.0f * x0 + x2) * 0.5f;
  285. c = (x1 - xm1) * 0.5f;
  286. outr[i] = (((a * poslo) + b) * poslo + c) * poslo + x0;
  287. }
  288. return 1;
  289. }
  290. int PADnote::noteout(float *outl, float *outr)
  291. {
  292. computecurrentparameters();
  293. float *smps = pars.sample[nsample].smp;
  294. if(smps == NULL) {
  295. for(int i = 0; i < synth.buffersize; ++i) {
  296. outl[i] = 0.0f;
  297. outr[i] = 0.0f;
  298. }
  299. return 1;
  300. }
  301. float smpfreq = pars.sample[nsample].basefreq;
  302. float freqrap = realfreq / smpfreq;
  303. int freqhi = (int) (floor(freqrap));
  304. float freqlo = freqrap - floor(freqrap);
  305. if(interpolation)
  306. Compute_Cubic(outl, outr, freqhi, freqlo);
  307. else
  308. Compute_Linear(outl, outr, freqhi, freqlo);
  309. if(firsttime) {
  310. fadein(outl);
  311. fadein(outr);
  312. firsttime = false;
  313. }
  314. NoteGlobalPar.GlobalFilter->filter(outl, outr);
  315. //Apply the punch
  316. if(NoteGlobalPar.Punch.Enabled != 0)
  317. for(int i = 0; i < synth.buffersize; ++i) {
  318. float punchamp = NoteGlobalPar.Punch.initialvalue
  319. * NoteGlobalPar.Punch.t + 1.0f;
  320. outl[i] *= punchamp;
  321. outr[i] *= punchamp;
  322. NoteGlobalPar.Punch.t -= NoteGlobalPar.Punch.dt;
  323. if(NoteGlobalPar.Punch.t < 0.0f) {
  324. NoteGlobalPar.Punch.Enabled = 0;
  325. break;
  326. }
  327. }
  328. if(ABOVE_AMPLITUDE_THRESHOLD(globaloldamplitude, globalnewamplitude))
  329. // Amplitude Interpolation
  330. for(int i = 0; i < synth.buffersize; ++i) {
  331. float tmpvol = INTERPOLATE_AMPLITUDE(globaloldamplitude,
  332. globalnewamplitude,
  333. i,
  334. synth.buffersize);
  335. outl[i] *= tmpvol * NoteGlobalPar.Panning;
  336. outr[i] *= tmpvol * (1.0f - NoteGlobalPar.Panning);
  337. }
  338. else
  339. for(int i = 0; i < synth.buffersize; ++i) {
  340. outl[i] *= globalnewamplitude * NoteGlobalPar.Panning;
  341. outr[i] *= globalnewamplitude * (1.0f - NoteGlobalPar.Panning);
  342. }
  343. // Apply legato-specific sound signal modifications
  344. legato.apply(*this, outl, outr);
  345. // Check if the global amplitude is finished.
  346. // If it does, disable the note
  347. if(NoteGlobalPar.AmpEnvelope->finished()) {
  348. for(int i = 0; i < synth.buffersize; ++i) { //fade-out
  349. float tmp = 1.0f - (float)i / synth.buffersize_f;
  350. outl[i] *= tmp;
  351. outr[i] *= tmp;
  352. }
  353. finished_ = 1;
  354. }
  355. return 1;
  356. }
  357. bool PADnote::finished() const
  358. {
  359. return finished_;
  360. }
  361. void PADnote::entomb(void)
  362. {
  363. NoteGlobalPar.AmpEnvelope->forceFinish();
  364. }
  365. void PADnote::releasekey()
  366. {
  367. NoteGlobalPar.FreqEnvelope->releasekey();
  368. NoteGlobalPar.FilterEnvelope->releasekey();
  369. NoteGlobalPar.AmpEnvelope->releasekey();
  370. }