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.

DistrhoPluginNekobi.cpp 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. /*
  2. * DISTRHO Nekobi Plugin, based on Nekobee by Sean Bolton and others.
  3. * Copyright (C) 2004 Sean Bolton and others
  4. * Copyright (C) 2013-2014 Filipe Coelho <falktx@falktx.com>
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License as
  8. * published by the Free Software Foundation; either version 2 of
  9. * the License, or any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * For a full copy of the GNU General Public License see the LICENSE file.
  17. */
  18. #include "DistrhoPluginNekobi.hpp"
  19. extern "C" {
  20. #include "nekobee-src/nekobee_synth.c"
  21. #include "nekobee-src/nekobee_voice.c"
  22. #include "nekobee-src/nekobee_voice_render.c"
  23. #include "nekobee-src/minblep_tables.c"
  24. // -----------------------------------------------------------------------
  25. // mutual exclusion
  26. bool dssp_voicelist_mutex_trylock(nekobee_synth_t* const synth)
  27. {
  28. /* Attempt the mutex lock */
  29. if (pthread_mutex_trylock(&synth->voicelist_mutex) != 0)
  30. {
  31. synth->voicelist_mutex_grab_failed = 1;
  32. return false;
  33. }
  34. /* Clean up if a previous mutex grab failed */
  35. if (synth->voicelist_mutex_grab_failed)
  36. {
  37. nekobee_synth_all_voices_off(synth);
  38. synth->voicelist_mutex_grab_failed = 0;
  39. }
  40. return true;
  41. }
  42. bool dssp_voicelist_mutex_lock(nekobee_synth_t* const synth)
  43. {
  44. return (pthread_mutex_lock(&synth->voicelist_mutex) == 0);
  45. }
  46. bool dssp_voicelist_mutex_unlock(nekobee_synth_t* const synth)
  47. {
  48. return (pthread_mutex_unlock(&synth->voicelist_mutex) == 0);
  49. }
  50. // -----------------------------------------------------------------------
  51. // nekobee_handle_raw_event
  52. void nekobee_handle_raw_event(nekobee_synth_t* const synth, const uint8_t size, const uint8_t* const data)
  53. {
  54. if (size != 3)
  55. return;
  56. switch (data[0] & 0xf0)
  57. {
  58. case 0x80:
  59. nekobee_synth_note_off(synth, data[1], data[2]);
  60. break;
  61. case 0x90:
  62. if (data[2] > 0)
  63. nekobee_synth_note_on(synth, data[1], data[2]);
  64. else
  65. nekobee_synth_note_off(synth, data[1], 64); /* shouldn't happen, but... */
  66. break;
  67. case 0xB0:
  68. nekobee_synth_control_change(synth, data[1], data[2]);
  69. break;
  70. default:
  71. break;
  72. }
  73. }
  74. } /* extern "C" */
  75. START_NAMESPACE_DISTRHO
  76. // -----------------------------------------------------------------------
  77. DistrhoPluginNekobi::DistrhoPluginNekobi()
  78. : Plugin(paramCount, 0, 0) // 0 programs, 0 states
  79. {
  80. nekobee_init_tables();
  81. // init synth
  82. fSynth.sample_rate = d_getSampleRate();
  83. fSynth.deltat = 1.0f / (float)d_getSampleRate();
  84. fSynth.nugget_remains = 0;
  85. fSynth.note_id = 0;
  86. fSynth.polyphony = XSYNTH_DEFAULT_POLYPHONY;
  87. fSynth.voices = XSYNTH_DEFAULT_POLYPHONY;
  88. fSynth.monophonic = XSYNTH_MONO_MODE_ONCE;
  89. fSynth.glide = 0;
  90. fSynth.last_noteon_pitch = 0.0f;
  91. fSynth.vcf_accent = 0.0f;
  92. fSynth.vca_accent = 0.0f;
  93. for (int i=0; i<8; ++i)
  94. fSynth.held_keys[i] = -1;
  95. fSynth.voice = nekobee_voice_new();
  96. fSynth.voicelist_mutex_grab_failed = 0;
  97. pthread_mutex_init(&fSynth.voicelist_mutex, nullptr);
  98. fSynth.channel_pressure = 0;
  99. fSynth.pitch_wheel_sensitivity = 0;
  100. fSynth.pitch_wheel = 0;
  101. for (int i=0; i<128; ++i)
  102. {
  103. fSynth.key_pressure[i] = 0;
  104. fSynth.cc[i] = 0;
  105. }
  106. fSynth.cc[7] = 127; // full volume
  107. fSynth.mod_wheel = 1.0f;
  108. fSynth.pitch_bend = 1.0f;
  109. fSynth.cc_volume = 1.0f;
  110. // Default values
  111. fParams.waveform = 0.0f;
  112. fParams.tuning = 0.0f;
  113. fParams.cutoff = 25.0f;
  114. fParams.resonance = 25.0f;
  115. fParams.envMod = 50.0f;
  116. fParams.decay = 75.0f;
  117. fParams.accent = 25.0f;
  118. fParams.volume = 75.0f;
  119. // Internal stuff
  120. fSynth.waveform = 0.0f;
  121. fSynth.tuning = 1.0f;
  122. fSynth.cutoff = 5.0f;
  123. fSynth.resonance = 0.8f;
  124. fSynth.envmod = 0.3f;
  125. fSynth.decay = 0.0002f;
  126. fSynth.accent = 0.3f;
  127. fSynth.volume = 0.75f;
  128. // reset
  129. d_deactivate();
  130. }
  131. DistrhoPluginNekobi::~DistrhoPluginNekobi()
  132. {
  133. std::free(fSynth.voice);
  134. }
  135. // -----------------------------------------------------------------------
  136. // Init
  137. void DistrhoPluginNekobi::d_initParameter(uint32_t index, Parameter& parameter)
  138. {
  139. switch (index)
  140. {
  141. case paramWaveform:
  142. parameter.hints = kParameterIsAutomable|kParameterIsBoolean;
  143. parameter.name = "Waveform";
  144. parameter.symbol = "waveform";
  145. parameter.ranges.def = 0.0f;
  146. parameter.ranges.min = 0.0f;
  147. parameter.ranges.max = 1.0f;
  148. break;
  149. case paramTuning:
  150. parameter.hints = kParameterIsAutomable; // was 0.5 <-> 2.0, log
  151. parameter.name = "Tuning";
  152. parameter.symbol = "tuning";
  153. parameter.ranges.def = 0.0f;
  154. parameter.ranges.min = -12.0f;
  155. parameter.ranges.max = 12.0f;
  156. break;
  157. case paramCutoff:
  158. parameter.hints = kParameterIsAutomable; // modified x2.5
  159. parameter.name = "Cutoff";
  160. parameter.symbol = "cutoff";
  161. parameter.unit = "%";
  162. parameter.ranges.def = 25.0f;
  163. parameter.ranges.min = 0.0f;
  164. parameter.ranges.max = 100.0f;
  165. break;
  166. case paramResonance:
  167. parameter.hints = kParameterIsAutomable; // modified x100
  168. parameter.name = "VCF Resonance";
  169. parameter.symbol = "resonance";
  170. parameter.unit = "%";
  171. parameter.ranges.def = 25.0f;
  172. parameter.ranges.min = 0.0f;
  173. parameter.ranges.max = 95.0f;
  174. break;
  175. case paramEnvMod:
  176. parameter.hints = kParameterIsAutomable; // modified x100
  177. parameter.name = "Env Mod";
  178. parameter.symbol = "env_mod";
  179. parameter.unit = "%";
  180. parameter.ranges.def = 50.0f;
  181. parameter.ranges.min = 0.0f;
  182. parameter.ranges.max = 100.0f;
  183. break;
  184. case paramDecay:
  185. parameter.hints = kParameterIsAutomable; // was 0.000009 <-> 0.0005, log
  186. parameter.name = "Decay";
  187. parameter.symbol = "decay";
  188. parameter.unit = "%";
  189. parameter.ranges.def = 75.0f;
  190. parameter.ranges.min = 0.0f;
  191. parameter.ranges.max = 100.0f;
  192. break;
  193. case paramAccent:
  194. parameter.hints = kParameterIsAutomable; // modified x100
  195. parameter.name = "Accent";
  196. parameter.symbol = "accent";
  197. parameter.unit = "%";
  198. parameter.ranges.def = 25.0f;
  199. parameter.ranges.min = 0.0f;
  200. parameter.ranges.max = 100.0f;
  201. break;
  202. case paramVolume:
  203. parameter.hints = kParameterIsAutomable; // modified x100
  204. parameter.name = "Volume";
  205. parameter.symbol = "volume";
  206. parameter.unit = "%";
  207. parameter.ranges.def = 75.0f;
  208. parameter.ranges.min = 0.0f;
  209. parameter.ranges.max = 100.0f;
  210. break;
  211. }
  212. }
  213. // -----------------------------------------------------------------------
  214. // Internal data
  215. float DistrhoPluginNekobi::d_getParameterValue(uint32_t index) const
  216. {
  217. switch (index)
  218. {
  219. case paramWaveform:
  220. return fParams.waveform;
  221. case paramTuning:
  222. return fParams.tuning;
  223. case paramCutoff:
  224. return fParams.cutoff;
  225. case paramResonance:
  226. return fParams.resonance;
  227. case paramEnvMod:
  228. return fParams.envMod;
  229. case paramDecay:
  230. return fParams.decay;
  231. case paramAccent:
  232. return fParams.accent;
  233. case paramVolume:
  234. return fParams.volume;
  235. }
  236. return 0.0f;
  237. }
  238. void DistrhoPluginNekobi::d_setParameterValue(uint32_t index, float value)
  239. {
  240. switch (index)
  241. {
  242. case paramWaveform:
  243. fParams.waveform = value;
  244. fSynth.waveform = value;
  245. DISTRHO_SAFE_ASSERT(fSynth.waveform == 0.0f || fSynth.waveform == 1.0f);
  246. break;
  247. case paramTuning:
  248. fParams.tuning = value;
  249. fSynth.tuning = (value+12.0f)/24.0f * 1.5 + 0.5f; // FIXME: log?
  250. DISTRHO_SAFE_ASSERT(fSynth.tuning >= 0.5f && fSynth.tuning <= 2.0f);
  251. break;
  252. case paramCutoff:
  253. fParams.cutoff = value;
  254. fSynth.cutoff = value/2.5f;
  255. DISTRHO_SAFE_ASSERT(fSynth.cutoff >= 0.0f && fSynth.cutoff <= 40.0f);
  256. break;
  257. case paramResonance:
  258. fParams.resonance = value;
  259. fSynth.resonance = value/100.0f;
  260. DISTRHO_SAFE_ASSERT(fSynth.resonance >= 0.0f && fSynth.resonance <= 0.95f);
  261. break;
  262. case paramEnvMod:
  263. fParams.envMod = value;
  264. fSynth.envmod = value/100.0f;
  265. DISTRHO_SAFE_ASSERT(fSynth.envmod >= 0.0f && fSynth.envmod <= 1.0f);
  266. break;
  267. case paramDecay:
  268. fParams.decay = value;
  269. fSynth.decay = value/100.0f * 0.000491f + 0.000009f; // FIXME: log?
  270. DISTRHO_SAFE_ASSERT(fSynth.decay >= 0.000009f && fSynth.decay <= 0.0005f);
  271. break;
  272. case paramAccent:
  273. fParams.accent = value;
  274. fSynth.accent = value/100.0f;
  275. DISTRHO_SAFE_ASSERT(fSynth.accent >= 0.0f && fSynth.accent <= 1.0f);
  276. break;
  277. case paramVolume:
  278. fParams.volume = value;
  279. fSynth.volume = value/100.0f;
  280. DISTRHO_SAFE_ASSERT(fSynth.volume >= 0.0f && fSynth.volume <= 1.0f);
  281. break;
  282. }
  283. }
  284. // -----------------------------------------------------------------------
  285. // Process
  286. void DistrhoPluginNekobi::d_activate()
  287. {
  288. fSynth.nugget_remains = 0;
  289. fSynth.note_id = 0;
  290. if (fSynth.voice != nullptr)
  291. nekobee_synth_all_voices_off(&fSynth);
  292. }
  293. void DistrhoPluginNekobi::d_deactivate()
  294. {
  295. if (fSynth.voice != nullptr)
  296. nekobee_synth_all_voices_off(&fSynth);
  297. }
  298. void DistrhoPluginNekobi::d_run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount)
  299. {
  300. uint32_t framesDone = 0;
  301. uint32_t curEventIndex = 0;
  302. uint32_t burstSize;
  303. float* out = outputs[0];
  304. if (fSynth.voice == nullptr || ! dssp_voicelist_mutex_trylock(&fSynth))
  305. {
  306. std::memset(out, 0, sizeof(float)*frames);
  307. return;
  308. }
  309. while (framesDone < frames)
  310. {
  311. if (fSynth.nugget_remains == 0)
  312. fSynth.nugget_remains = XSYNTH_NUGGET_SIZE;
  313. /* process any ready events */
  314. while (curEventIndex < midiEventCount && framesDone == midiEvents[curEventIndex].frame)
  315. {
  316. if (midiEvents[curEventIndex].size > MidiEvent::kDataSize)
  317. continue;
  318. nekobee_handle_raw_event(&fSynth, midiEvents[curEventIndex].size, midiEvents[curEventIndex].data);
  319. curEventIndex++;
  320. }
  321. /* calculate the sample count (burstSize) for the next nekobee_voice_render() call to be the smallest of:
  322. * - control calculation quantization size (XSYNTH_NUGGET_SIZE, in samples)
  323. * - the number of samples remaining in an already-begun nugget (synth->nugget_remains)
  324. * - the number of samples until the next event is ready
  325. * - the number of samples left in this run
  326. */
  327. burstSize = XSYNTH_NUGGET_SIZE;
  328. /* we're still in the middle of a nugget, so reduce the burst size
  329. * to end when the nugget ends */
  330. if (fSynth.nugget_remains < burstSize)
  331. burstSize = fSynth.nugget_remains;
  332. /* reduce burst size to end when next event is ready */
  333. if (curEventIndex < midiEventCount && midiEvents[curEventIndex].frame - framesDone < burstSize)
  334. burstSize = midiEvents[curEventIndex].frame - framesDone;
  335. /* reduce burst size to end at end of this run */
  336. if (frames - framesDone < burstSize)
  337. burstSize = frames - framesDone;
  338. /* render the burst */
  339. nekobee_synth_render_voices(&fSynth, out + framesDone, burstSize, (burstSize == fSynth.nugget_remains));
  340. framesDone += burstSize;
  341. fSynth.nugget_remains -= burstSize;
  342. }
  343. dssp_voicelist_mutex_unlock(&fSynth);
  344. }
  345. // -----------------------------------------------------------------------
  346. Plugin* createPlugin()
  347. {
  348. return new DistrhoPluginNekobi();
  349. }
  350. // -----------------------------------------------------------------------
  351. END_NAMESPACE_DISTRHO