External plugins for 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.

241 lines
6.9KB

  1. /*
  2. * DISTRHO Kars Plugin, based on karplong by Chris Cannam.
  3. * Copyright (C) 2015-2019 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "DistrhoPluginKars.hpp"
  17. #include "DistrhoPluginUtils.hpp"
  18. START_NAMESPACE_DISTRHO
  19. // -----------------------------------------------------------------------
  20. DistrhoPluginKars::DistrhoPluginKars()
  21. : Plugin(paramCount, 0, 0), // 0 programs, 0 states
  22. fSustain(false),
  23. fRelease(0.01),
  24. fVolume(75.0f),
  25. fSampleRate(getSampleRate()),
  26. fBlockStart(0)
  27. {
  28. for (int i=kMaxNotes; --i >= 0;)
  29. {
  30. fNotes[i].voice = i;
  31. fNotes[i].setSampleRate(fSampleRate);
  32. }
  33. }
  34. // -----------------------------------------------------------------------
  35. // Init
  36. void DistrhoPluginKars::initParameter(uint32_t index, Parameter& parameter)
  37. {
  38. switch (index)
  39. {
  40. case paramSustain:
  41. parameter.hints = kParameterIsAutomatable|kParameterIsBoolean;
  42. parameter.name = "Sustain";
  43. parameter.symbol = "sustain";
  44. parameter.ranges.def = 0.0f;
  45. parameter.ranges.min = 0.0f;
  46. parameter.ranges.max = 1.0f;
  47. break;
  48. case paramRelease:
  49. parameter.hints = kParameterIsAutomatable;
  50. parameter.name = "Release";
  51. parameter.symbol = "release";
  52. parameter.unit = "s";
  53. parameter.ranges.def = 0.01f;
  54. parameter.ranges.min = 0.0f;
  55. parameter.ranges.max = 5.0f;
  56. break;
  57. case paramVolume:
  58. parameter.hints = kParameterIsAutomatable;
  59. parameter.name = "Volume";
  60. parameter.symbol = "volume";
  61. parameter.unit = "%";
  62. parameter.ranges.def = 75.0f;
  63. parameter.ranges.min = 0.0f;
  64. parameter.ranges.max = 100.0f;
  65. break;
  66. }
  67. }
  68. // -----------------------------------------------------------------------
  69. // Internal data
  70. float DistrhoPluginKars::getParameterValue(uint32_t index) const
  71. {
  72. switch (index)
  73. {
  74. case paramSustain: return fSustain ? 1.0f : 0.0f;
  75. case paramRelease: return fRelease;
  76. case paramVolume: return fVolume;
  77. }
  78. return 0.0f;
  79. }
  80. void DistrhoPluginKars::setParameterValue(uint32_t index, float value)
  81. {
  82. switch (index)
  83. {
  84. case paramSustain:
  85. fSustain = value > 0.5f;
  86. break;
  87. case paramRelease:
  88. fRelease = value;
  89. break;
  90. case paramVolume:
  91. fVolume = value;
  92. break;
  93. }
  94. }
  95. // -----------------------------------------------------------------------
  96. // Process
  97. void DistrhoPluginKars::activate()
  98. {
  99. fBlockStart = 0;
  100. for (int i=kMaxNotes; --i >= 0;)
  101. {
  102. fNotes[i].on = kNoteNull;
  103. fNotes[i].off = kNoteNull;
  104. fNotes[i].velocity = 0;
  105. }
  106. }
  107. void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
  108. {
  109. uint8_t note, velo;
  110. std::memset(outputs[0], 0, sizeof(float)*frames);
  111. for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();)
  112. {
  113. for (uint32_t i=0; i<amsh.midiEventCount; ++i)
  114. {
  115. if (amsh.midiEvents[i].size > MidiEvent::kDataSize)
  116. continue;
  117. const uint8_t* data = amsh.midiEvents[i].data;
  118. const uint8_t status = data[0] & 0xF0;
  119. switch (status)
  120. {
  121. case 0x90:
  122. note = data[1];
  123. velo = data[2];
  124. DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes
  125. if (velo > 0)
  126. {
  127. fNotes[note].on = fBlockStart;
  128. fNotes[note].off = kNoteNull;
  129. fNotes[note].velocity = velo;
  130. break;
  131. }
  132. // fall through
  133. case 0x80:
  134. note = data[1];
  135. DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes
  136. fNotes[note].off = fBlockStart;
  137. break;
  138. }
  139. }
  140. float* const out = amsh.outputs[0];
  141. for (int i=kMaxNotes; --i >= 0;)
  142. {
  143. if (fNotes[i].on != kNoteNull)
  144. addSamples(out, i, amsh.frames);
  145. }
  146. fBlockStart += amsh.frames;
  147. }
  148. }
  149. void DistrhoPluginKars::addSamples(float* out, int voice, uint32_t frames)
  150. {
  151. const uint32_t start = fBlockStart;
  152. Note& note(fNotes[voice]);
  153. if (start < note.on)
  154. return;
  155. if (start == note.on)
  156. {
  157. for (int i=note.sizei; --i >= 0;)
  158. note.wavetable[i] = (float(rand()) / float(RAND_MAX)) * 2.0f - 1.0f;
  159. }
  160. const float vgain = float(note.velocity) / 127.0f;
  161. bool decay;
  162. float gain, sample;
  163. uint32_t index, size;
  164. for (uint32_t i=0, s=start-note.on; i<frames; ++i, ++s)
  165. {
  166. gain = vgain;
  167. if ((! fSustain) && note.off != kNoteNull && note.off < i+start)
  168. {
  169. // reuse index and size to save some performance.
  170. // actual values are release and dist
  171. index = 1 + uint32_t(fRelease * fSampleRate); // release, not index
  172. size = i + start - note.off; // dist, not size
  173. if (size > index)
  174. {
  175. note.on = kNoteNull;
  176. break;
  177. }
  178. gain = gain * float(index - size) / float(index);
  179. }
  180. size = uint32_t(note.sizei);
  181. decay = s > size;
  182. index = s % size;
  183. sample = note.wavetable[index];
  184. if (decay)
  185. {
  186. if (index == 0)
  187. sample += note.wavetable[size-1];
  188. else
  189. sample += note.wavetable[index-1];
  190. note.wavetable[index] = sample/2;
  191. }
  192. out[i] += gain * sample * (fVolume / 100.0f);
  193. }
  194. }
  195. // -----------------------------------------------------------------------
  196. Plugin* createPlugin()
  197. {
  198. return new DistrhoPluginKars();
  199. }
  200. // -----------------------------------------------------------------------
  201. END_NAMESPACE_DISTRHO