Collection of DPF-based plugins for packaging
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.

421 lines
13KB

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