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.

ZamSynthPlugin.cpp 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. /*
  2. * ZamSynth polyphonic synthesiser
  3. * Copyright (C) 2014 Damien Zammit <damien@zamaudio.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "ZamSynthPlugin.hpp"
  18. START_NAMESPACE_DISTRHO
  19. // -----------------------------------------------------------------------
  20. ZamSynthPlugin::ZamSynthPlugin()
  21. : Plugin(paramCount, 1, 1) // 1 program, 1 state
  22. {
  23. // set default values
  24. d_setProgram(0);
  25. // reset
  26. d_deactivate();
  27. }
  28. ZamSynthPlugin::~ZamSynthPlugin()
  29. {
  30. }
  31. // -----------------------------------------------------------------------
  32. // Init
  33. void ZamSynthPlugin::d_initParameter(uint32_t index, Parameter& parameter)
  34. {
  35. switch (index)
  36. {
  37. case paramGain:
  38. parameter.hints = PARAMETER_IS_AUTOMABLE;
  39. parameter.name = "Gain";
  40. parameter.symbol = "gain";
  41. parameter.unit = "dB";
  42. parameter.ranges.def = 0.0f;
  43. parameter.ranges.min = -30.0f;
  44. parameter.ranges.max = 30.0f;
  45. break;
  46. case paramSpeed:
  47. parameter.hints = PARAMETER_IS_AUTOMABLE | PARAMETER_IS_INTEGER;
  48. parameter.name = "Speed";
  49. parameter.symbol = "speed";
  50. parameter.unit = " ";
  51. parameter.ranges.def = 10.0f;
  52. parameter.ranges.min = 1.0f;
  53. parameter.ranges.max = 20.0f;
  54. break;
  55. case paramGraph:
  56. parameter.hints = PARAMETER_IS_AUTOMABLE | PARAMETER_IS_BOOLEAN;
  57. parameter.name = "Graph toggle";
  58. parameter.symbol = "graph";
  59. parameter.unit = " ";
  60. parameter.ranges.def = 0.0f;
  61. parameter.ranges.min = 0.0f;
  62. parameter.ranges.max = 1.0f;
  63. break;
  64. }
  65. }
  66. void ZamSynthPlugin::d_initProgramName(uint32_t index, d_string& programName)
  67. {
  68. if (index != 0)
  69. return;
  70. programName = "Default";
  71. }
  72. // -----------------------------------------------------------------------
  73. // Internal data
  74. float ZamSynthPlugin::d_getParameterValue(uint32_t index) const
  75. {
  76. switch (index)
  77. {
  78. case paramGain:
  79. return gain;
  80. break;
  81. case paramSpeed:
  82. return speed;
  83. break;
  84. case paramGraph:
  85. return graph;
  86. break;
  87. default:
  88. return 0.0f;
  89. }
  90. }
  91. void ZamSynthPlugin::d_setParameterValue(uint32_t index, float value)
  92. {
  93. switch (index)
  94. {
  95. case paramGain:
  96. gain = value;
  97. break;
  98. case paramSpeed:
  99. speed = value;
  100. break;
  101. case paramGraph:
  102. graph = value;
  103. break;
  104. }
  105. }
  106. void ZamSynthPlugin::d_setProgram(uint32_t index)
  107. {
  108. if (index != 0)
  109. return;
  110. /* Default parameter values */
  111. gain = 0.0f;
  112. graph = 0.0f;
  113. speed = 10.0f;
  114. /* Default variable values */
  115. for (int i = 0; i < MAX_VOICES; i++) {
  116. voice[i].playing = false;
  117. voice[i].notenum = -1;
  118. voice[i].envpos = 0;
  119. voice[i].slowcount = 0;
  120. voice[i].curamp = 0.f;
  121. voice[i].vi = 0.f;
  122. voice[i].rampstate = 0.f;
  123. }
  124. curvoice = voice; //ptr to first voice
  125. for (int i = 0; i < AREAHEIGHT; i++) {
  126. wave_y[i] = sin(i*2.*M_PI/d_getSampleRate());//*1000
  127. }
  128. for (int i = 0; i < MAX_ENV; i++) {
  129. env_y[i] = (sin(i*2.*M_PI/d_getSampleRate()*1000./2.)) > 0.f ? sin(i*2.*M_PI/d_getSampleRate()*1000./2.) : 0.f;
  130. }
  131. /* reset filter values */
  132. d_activate();
  133. }
  134. void ZamSynthPlugin::d_setState(const char* key, const char* value)
  135. {
  136. if (strcmp(key, "waveform") == 0) {
  137. char* tmp;
  138. int i = 0;
  139. char tmpbuf[4*AREAHEIGHT+1] = {0};
  140. snprintf(tmpbuf, 4*AREAHEIGHT, "%s", value);
  141. tmp = strtok(tmpbuf, " ");
  142. while ((tmp != NULL) && (i < AREAHEIGHT)) {
  143. wave_y[i] = ((float) atoi(tmp))/AREAHEIGHT - 0.5;
  144. i++;
  145. //printf("dsp wave_y[%d]=%.2f ", i, wave_y[i]);
  146. tmp = strtok(NULL, " ");
  147. }
  148. } else if (strcmp(key, "envelope") == 0) {
  149. char* tmp;
  150. int i = 0;
  151. char tmpbuf[4*MAX_ENV+1] = {0};
  152. snprintf(tmpbuf, 4*MAX_ENV, "%s", value);
  153. tmp = strtok(tmpbuf, " ");
  154. while ((tmp != NULL) && (i < MAX_ENV)) {
  155. env_y[i] = ((float) atoi(tmp))/MAX_ENV - 0.5;
  156. i++;
  157. //printf("dsp env_y[%d]=%.2f ", i, env_y[i]);
  158. tmp = strtok(NULL, " ");
  159. }
  160. }
  161. }
  162. void ZamSynthPlugin::d_initStateKey(unsigned int index, d_string& key)
  163. {
  164. if (index == 0) key = "waveform";
  165. if (index == 1) key = "envelope";
  166. }
  167. // -----------------------------------------------------------------------
  168. // Process
  169. void ZamSynthPlugin::d_activate()
  170. {
  171. }
  172. void ZamSynthPlugin::d_deactivate()
  173. {
  174. // all values to zero
  175. }
  176. float ZamSynthPlugin::wavetable(float in)
  177. {
  178. int index = (int) ((in / (2.0 * M_PI)) * (AREAHEIGHT-1.0));
  179. return (wave_y[index]);
  180. //return (sin(in));
  181. }
  182. void ZamSynthPlugin::d_run(float**, float** outputs, uint32_t frames,
  183. const MidiEvent* midievent, uint32_t midicount)
  184. {
  185. float srate = d_getSampleRate();
  186. int slowfactor = (int) srate / (speed * 2400); // 1-20 ~ 20-1
  187. uint32_t i;
  188. float RD_0;
  189. for (i = 0; i < midicount; i++) {
  190. int type = midievent[i].buf[0] & 0xF0;
  191. int chan = midievent[i].buf[0] & 0x0F;
  192. int num = midievent[i].buf[1];
  193. int vel = midievent[i].buf[2];
  194. if (type == 0x90 && chan == 0x0) {
  195. // NOTE ON
  196. nvoices = 0;
  197. //printf("ON: Note\n");
  198. //printf("ON: begin attack\n");
  199. for (int k = 0; k < 128; k++)
  200. if (voice[k].playing)
  201. nvoices++;
  202. curvoice = &voice[nvoices];
  203. curvoice->envpos = 1; // begin attack
  204. curvoice->playing = true;
  205. curvoice->notenum = num;
  206. curvoice->vi = vel / 127.f;
  207. curvoice->curamp = curvoice->vi;
  208. curvoice->rampstate = 0;
  209. }
  210. else if (type == 0x80 && chan == 0x0) {
  211. // NOTE OFF
  212. //printf("OFF: Note\n");
  213. //find voice with current notenum
  214. nvoices = 0;
  215. for (int k = 0; k < 128; k++) {
  216. if (voice[k].playing && voice[k].notenum == num) {
  217. voice[k].envpos = MAX_ENV / 2 + 1; // begin release;
  218. }
  219. if (!voice[k].playing && voice[k].notenum == num) {
  220. voice[k].notenum = -1;
  221. }
  222. }
  223. }
  224. }
  225. float power;
  226. bool signal;
  227. float wave;
  228. float outl;
  229. float outr;
  230. for (i = 0; i < frames; i++) {
  231. signal = false;
  232. outl = outr = 0.f;
  233. power = 0.f;
  234. int k;
  235. Voice* j;
  236. // process envelope positions per sample
  237. for (k = 0; k < 128; k++) {
  238. j = &voice[k];
  239. if (j->playing) {
  240. if (j->envpos <= 0) {
  241. //silence
  242. j->curamp = 0.f;
  243. j->playing = false;
  244. j->slowcount = 0;
  245. j->envpos = 0;
  246. } else if (j->envpos > 0 && (int) j->envpos < MAX_ENV / 2) {
  247. //attack
  248. j->curamp = j->vi * env_y[j->envpos];
  249. //printf("att: %d %d curamp=%.2f\n",k,j->envpos, j->curamp);
  250. j->slowcount++;
  251. j->envpos += ((j->slowcount % slowfactor) == 0) ? 1 : 0;
  252. } else if (j->envpos > MAX_ENV / 2) {
  253. //release
  254. j->curamp = j->vi * env_y[j->envpos];
  255. //printf("rel: %d %d curamp=%.2f\n",k,j->envpos, j->curamp);
  256. j->slowcount++;
  257. j->envpos += ((j->slowcount % slowfactor) == 0) ? 1 : 0;
  258. if (j->envpos == MAX_ENV) {
  259. //end of release
  260. j->envpos = 0;
  261. j->slowcount = 0;
  262. j->curamp = 0.f;
  263. j->vi = 0.f;
  264. j->playing = false;
  265. //printf("killed, n=%d\n",k);
  266. }
  267. } else {
  268. //sustain
  269. j->curamp = j->vi * env_y[MAX_ENV/2];
  270. //printf("sustain...\n");
  271. }
  272. }
  273. }
  274. for (k = 0; k < 128; k++) {
  275. float rampfreq;
  276. if (voice[k].curamp < 0.f && voice[k].playing) printf("WTF NEG\n");
  277. if (!voice[k].playing) continue;
  278. signal = true;
  279. rampfreq = 440.0*powf(2.0, (voice[k].notenum-48.0-36)/12.);
  280. // ramp sawtooth
  281. RD_0 = 1.4247585730565955E-4*srate/44100.*rampfreq + voice[k].rampstate;
  282. if (RD_0>6.283185307179586) {RD_0 -= 6.283185307179586;}
  283. if (RD_0<-6.283185307179586) {RD_0 += 6.283185307179586;}
  284. voice[k].rampstate = RD_0;
  285. // wavetable
  286. wave = wavetable(voice[k].rampstate);
  287. power += sqrt(voice[k].curamp);
  288. outl += wave*voice[k].curamp/5.;
  289. outr += wave*voice[k].curamp/5.;
  290. }
  291. if (signal) {
  292. //outl;
  293. //outr;
  294. outputs[0][i] = outl*from_dB(gain);
  295. outputs[1][i] = outr*from_dB(gain);
  296. } else {
  297. outputs[0][i] = 0.f;
  298. outputs[1][i] = 0.f;
  299. }
  300. }
  301. }
  302. // -----------------------------------------------------------------------
  303. Plugin* createPlugin()
  304. {
  305. return new ZamSynthPlugin();
  306. }
  307. // -----------------------------------------------------------------------
  308. END_NAMESPACE_DISTRHO