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.

331 lines
8.4KB

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