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.

362 lines
14KB

  1. #include "plugin.hpp"
  2. #include "elements/dsp/part.h"
  3. struct Elements : Module {
  4. enum ParamIds {
  5. CONTOUR_PARAM,
  6. BOW_PARAM,
  7. BLOW_PARAM,
  8. STRIKE_PARAM,
  9. COARSE_PARAM,
  10. FINE_PARAM,
  11. FM_PARAM,
  12. FLOW_PARAM,
  13. MALLET_PARAM,
  14. GEOMETRY_PARAM,
  15. BRIGHTNESS_PARAM,
  16. BOW_TIMBRE_PARAM,
  17. BLOW_TIMBRE_PARAM,
  18. STRIKE_TIMBRE_PARAM,
  19. DAMPING_PARAM,
  20. POSITION_PARAM,
  21. SPACE_PARAM,
  22. BOW_TIMBRE_MOD_PARAM,
  23. FLOW_MOD_PARAM,
  24. BLOW_TIMBRE_MOD_PARAM,
  25. MALLET_MOD_PARAM,
  26. STRIKE_TIMBRE_MOD_PARAM,
  27. DAMPING_MOD_PARAM,
  28. GEOMETRY_MOD_PARAM,
  29. POSITION_MOD_PARAM,
  30. BRIGHTNESS_MOD_PARAM,
  31. SPACE_MOD_PARAM,
  32. PLAY_PARAM,
  33. NUM_PARAMS
  34. };
  35. enum InputIds {
  36. NOTE_INPUT,
  37. FM_INPUT,
  38. GATE_INPUT,
  39. STRENGTH_INPUT,
  40. BLOW_INPUT,
  41. STRIKE_INPUT,
  42. BOW_TIMBRE_MOD_INPUT,
  43. FLOW_MOD_INPUT,
  44. BLOW_TIMBRE_MOD_INPUT,
  45. MALLET_MOD_INPUT,
  46. STRIKE_TIMBRE_MOD_INPUT,
  47. DAMPING_MOD_INPUT,
  48. GEOMETRY_MOD_INPUT,
  49. POSITION_MOD_INPUT,
  50. BRIGHTNESS_MOD_INPUT,
  51. SPACE_MOD_INPUT,
  52. NUM_INPUTS
  53. };
  54. enum OutputIds {
  55. AUX_OUTPUT,
  56. MAIN_OUTPUT,
  57. NUM_OUTPUTS
  58. };
  59. enum LightIds {
  60. GATE_LIGHT,
  61. EXCITER_LIGHT,
  62. RESONATOR_LIGHT,
  63. NUM_LIGHTS
  64. };
  65. dsp::SampleRateConverter<2> inputSrc;
  66. dsp::SampleRateConverter<2> outputSrc;
  67. dsp::DoubleRingBuffer<dsp::Frame<2>, 256> inputBuffer;
  68. dsp::DoubleRingBuffer<dsp::Frame<2>, 256> outputBuffer;
  69. uint16_t reverb_buffer[32768] = {};
  70. elements::Part* part;
  71. bool easterEgg = false;
  72. Elements() {
  73. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  74. configParam(CONTOUR_PARAM, 0.0, 1.0, 1.0, "Envelope contour");
  75. configParam(BOW_PARAM, 0.0, 1.0, 0.0, "Bow exciter");
  76. configParam(BLOW_PARAM, 0.0, 1.0, 0.0, "Blow exciter");
  77. configParam(STRIKE_PARAM, 0.0, 1.0, 0.5, "Percussive noise amount");
  78. configParam(COARSE_PARAM, -30.0, 30.0, 0.0, "Coarse frequency adjustment");
  79. configParam(FINE_PARAM, -2.0, 2.0, 0.0, "Fine frequency adjustment");
  80. configParam(FM_PARAM, -1.0, 1.0, 0.0, "FM input attenuverter");
  81. configParam(FLOW_PARAM, 0.0, 1.0, 0.5, "Air flow noise color");
  82. configParam(MALLET_PARAM, 0.0, 1.0, 0.5, "Percussive noise type");
  83. configParam(GEOMETRY_PARAM, 0.0, 1.0, 0.5, "Resonator geometry");
  84. configParam(BRIGHTNESS_PARAM, 0.0, 1.0, 0.5, "Brightness");
  85. configParam(BOW_TIMBRE_PARAM, 0.0, 1.0, 0.5, "Bow timbre");
  86. configParam(BLOW_TIMBRE_PARAM, 0.0, 1.0, 0.5, "Blow timbre");
  87. configParam(STRIKE_TIMBRE_PARAM, 0.0, 1.0, 0.5, "Strike timbre");
  88. configParam(DAMPING_PARAM, 0.0, 1.0, 0.5, "Energy dissipation speed");
  89. configParam(POSITION_PARAM, 0.0, 1.0, 0.5, "Excitation position");
  90. configParam(SPACE_PARAM, 0.0, 2.0, 0.0, "Reverb space");
  91. configParam(BOW_TIMBRE_MOD_PARAM, -1.0, 1.0, 0.0, "Bow timbre attenuverter");
  92. configParam(FLOW_MOD_PARAM, -1.0, 1.0, 0.0, "Air flow noise attenuverter");
  93. configParam(BLOW_TIMBRE_MOD_PARAM, -1.0, 1.0, 0.0, "Blow timbre attenuverter");
  94. configParam(MALLET_MOD_PARAM, -1.0, 1.0, 0.0, "Percussive noise attenuverter");
  95. configParam(STRIKE_TIMBRE_MOD_PARAM, -1.0, 1.0, 0.0, "Strike timbre attenuverter");
  96. configParam(DAMPING_MOD_PARAM, -1.0, 1.0, 0.0, "Energy dissipation speed attenuverter");
  97. configParam(GEOMETRY_MOD_PARAM, -1.0, 1.0, 0.0, "Resonator geometry attenuverter");
  98. configParam(POSITION_MOD_PARAM, -1.0, 1.0, 0.0, "Excitation position attenuverter");
  99. configParam(BRIGHTNESS_MOD_PARAM, -1.0, 1.0, 0.0, "Brightness attenuverter");
  100. configParam(SPACE_MOD_PARAM, -2.0, 2.0, 0.0, "Reverb space attenuverter");
  101. configParam(PLAY_PARAM, 0.0, 1.0, 0.0, "Play");
  102. part = new elements::Part();
  103. // In the Mutable Instruments code, Part doesn't initialize itself, so zero it here.
  104. memset(part, 0, sizeof(*part));
  105. part->Init(reverb_buffer);
  106. // Just some random numbers
  107. uint32_t seed[3] = {1, 2, 3};
  108. part->Seed(seed, 3);
  109. }
  110. ~Elements() {
  111. delete part;
  112. }
  113. void process(const ProcessArgs& args) override {
  114. // Get input
  115. if (!inputBuffer.full()) {
  116. dsp::Frame<2> inputFrame;
  117. inputFrame.samples[0] = inputs[BLOW_INPUT].getVoltage() / 5.0;
  118. inputFrame.samples[1] = inputs[STRIKE_INPUT].getVoltage() / 5.0;
  119. inputBuffer.push(inputFrame);
  120. }
  121. // Render frames
  122. if (outputBuffer.empty()) {
  123. float blow[16] = {};
  124. float strike[16] = {};
  125. float main[16];
  126. float aux[16];
  127. // Convert input buffer
  128. {
  129. inputSrc.setRates(args.sampleRate, 32000);
  130. dsp::Frame<2> inputFrames[16];
  131. int inLen = inputBuffer.size();
  132. int outLen = 16;
  133. inputSrc.process(inputBuffer.startData(), &inLen, inputFrames, &outLen);
  134. inputBuffer.startIncr(inLen);
  135. for (int i = 0; i < outLen; i++) {
  136. blow[i] = inputFrames[i].samples[0];
  137. strike[i] = inputFrames[i].samples[1];
  138. }
  139. }
  140. // Set patch from parameters
  141. elements::Patch* p = part->mutable_patch();
  142. p->exciter_envelope_shape = params[CONTOUR_PARAM].getValue();
  143. p->exciter_bow_level = params[BOW_PARAM].getValue();
  144. p->exciter_blow_level = params[BLOW_PARAM].getValue();
  145. p->exciter_strike_level = params[STRIKE_PARAM].getValue();
  146. #define BIND(_p, _m, _i) clamp(params[_p].getValue() + 3.3f*dsp::quadraticBipolar(params[_m].getValue())*inputs[_i].getVoltage()/5.0f, 0.0f, 0.9995f)
  147. p->exciter_bow_timbre = BIND(BOW_TIMBRE_PARAM, BOW_TIMBRE_MOD_PARAM, BOW_TIMBRE_MOD_INPUT);
  148. p->exciter_blow_meta = BIND(FLOW_PARAM, FLOW_MOD_PARAM, FLOW_MOD_INPUT);
  149. p->exciter_blow_timbre = BIND(BLOW_TIMBRE_PARAM, BLOW_TIMBRE_MOD_PARAM, BLOW_TIMBRE_MOD_INPUT);
  150. p->exciter_strike_meta = BIND(MALLET_PARAM, MALLET_MOD_PARAM, MALLET_MOD_INPUT);
  151. p->exciter_strike_timbre = BIND(STRIKE_TIMBRE_PARAM, STRIKE_TIMBRE_MOD_PARAM, STRIKE_TIMBRE_MOD_INPUT);
  152. p->resonator_geometry = BIND(GEOMETRY_PARAM, GEOMETRY_MOD_PARAM, GEOMETRY_MOD_INPUT);
  153. p->resonator_brightness = BIND(BRIGHTNESS_PARAM, BRIGHTNESS_MOD_PARAM, BRIGHTNESS_MOD_INPUT);
  154. p->resonator_damping = BIND(DAMPING_PARAM, DAMPING_MOD_PARAM, DAMPING_MOD_INPUT);
  155. p->resonator_position = BIND(POSITION_PARAM, POSITION_MOD_PARAM, POSITION_MOD_INPUT);
  156. p->space = clamp(params[SPACE_PARAM].getValue() + params[SPACE_MOD_PARAM].getValue() * inputs[SPACE_MOD_INPUT].getVoltage() / 5.0f, 0.0f, 2.0f);
  157. // Get performance inputs
  158. elements::PerformanceState performance;
  159. performance.note = 12.0 * inputs[NOTE_INPUT].getVoltage() + roundf(params[COARSE_PARAM].getValue()) + params[FINE_PARAM].getValue() + 69.0;
  160. performance.modulation = 3.3 * dsp::quarticBipolar(params[FM_PARAM].getValue()) * 49.5 * inputs[FM_INPUT].getVoltage() / 5.0;
  161. performance.gate = params[PLAY_PARAM].getValue() >= 1.0 || inputs[GATE_INPUT].getVoltage() >= 1.0;
  162. performance.strength = clamp(1.0 - inputs[STRENGTH_INPUT].getVoltage() / 5.0f, 0.0f, 1.0f);
  163. // Generate audio
  164. part->Process(performance, blow, strike, main, aux, 16);
  165. // Convert output buffer
  166. {
  167. dsp::Frame<2> outputFrames[16];
  168. for (int i = 0; i < 16; i++) {
  169. outputFrames[i].samples[0] = main[i];
  170. outputFrames[i].samples[1] = aux[i];
  171. }
  172. outputSrc.setRates(32000, args.sampleRate);
  173. int inLen = 16;
  174. int outLen = outputBuffer.capacity();
  175. outputSrc.process(outputFrames, &inLen, outputBuffer.endData(), &outLen);
  176. outputBuffer.endIncr(outLen);
  177. }
  178. // Set lights
  179. lights[GATE_LIGHT].setBrightness(performance.gate ? 0.75 : 0.0);
  180. lights[EXCITER_LIGHT].setBrightness(part->exciter_level());
  181. lights[RESONATOR_LIGHT].setBrightness(part->resonator_level());
  182. }
  183. // Set output
  184. if (!outputBuffer.empty()) {
  185. dsp::Frame<2> outputFrame = outputBuffer.shift();
  186. outputs[AUX_OUTPUT].setVoltage(5.0 * outputFrame.samples[0]);
  187. outputs[MAIN_OUTPUT].setVoltage(5.0 * outputFrame.samples[1]);
  188. }
  189. }
  190. json_t* dataToJson() override {
  191. json_t* rootJ = json_object();
  192. json_object_set_new(rootJ, "model", json_integer(getModel()));
  193. return rootJ;
  194. }
  195. void dataFromJson(json_t* rootJ) override {
  196. json_t* modelJ = json_object_get(rootJ, "model");
  197. if (modelJ) {
  198. setModel(json_integer_value(modelJ));
  199. }
  200. }
  201. int getModel() {
  202. return (int)part->resonator_model();
  203. }
  204. void setModel(int model) {
  205. part->set_resonator_model((elements::ResonatorModel)model);
  206. }
  207. void setEasterEgg() {
  208. part->set_easter_egg(easterEgg);
  209. }
  210. };
  211. struct ElementsModalItem : MenuItem {
  212. Elements* elements;
  213. int model;
  214. void onAction(const event::Action& e) override {
  215. elements->setModel(model);
  216. }
  217. void step() override {
  218. rightText = CHECKMARK(elements->getModel() == model);
  219. MenuItem::step();
  220. }
  221. };
  222. struct ElementsEasterEggItem : MenuItem {
  223. Elements* elements;
  224. void onAction(const event::Action& e) override {
  225. elements->easterEgg = !elements->easterEgg;
  226. elements->setEasterEgg();
  227. }
  228. void step() override {
  229. rightText = (elements->easterEgg) ? "✔" : "";
  230. MenuItem::step();
  231. }
  232. };
  233. struct ElementsWidget : ModuleWidget {
  234. ElementsWidget(Elements* module) {
  235. setModule(module);
  236. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Elements.svg")));
  237. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  238. addChild(createWidget<ScrewSilver>(Vec(480, 0)));
  239. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  240. addChild(createWidget<ScrewSilver>(Vec(480, 365)));
  241. addParam(createParam<Rogan1PSWhite>(Vec(28, 42), module, Elements::CONTOUR_PARAM));
  242. addParam(createParam<Rogan1PSWhite>(Vec(99, 42), module, Elements::BOW_PARAM));
  243. addParam(createParam<Rogan1PSRed>(Vec(169, 42), module, Elements::BLOW_PARAM));
  244. addParam(createParam<Rogan1PSGreen>(Vec(239, 42), module, Elements::STRIKE_PARAM));
  245. addParam(createParam<Rogan1PSWhite>(Vec(310, 42), module, Elements::COARSE_PARAM));
  246. addParam(createParam<Rogan1PSWhite>(Vec(381, 42), module, Elements::FINE_PARAM));
  247. addParam(createParam<Rogan1PSWhite>(Vec(451, 42), module, Elements::FM_PARAM));
  248. addParam(createParam<Rogan3PSRed>(Vec(115, 116), module, Elements::FLOW_PARAM));
  249. addParam(createParam<Rogan3PSGreen>(Vec(212, 116), module, Elements::MALLET_PARAM));
  250. addParam(createParam<Rogan3PSWhite>(Vec(326, 116), module, Elements::GEOMETRY_PARAM));
  251. addParam(createParam<Rogan3PSWhite>(Vec(423, 116), module, Elements::BRIGHTNESS_PARAM));
  252. addParam(createParam<Rogan1PSWhite>(Vec(99, 202), module, Elements::BOW_TIMBRE_PARAM));
  253. addParam(createParam<Rogan1PSRed>(Vec(170, 202), module, Elements::BLOW_TIMBRE_PARAM));
  254. addParam(createParam<Rogan1PSGreen>(Vec(239, 202), module, Elements::STRIKE_TIMBRE_PARAM));
  255. addParam(createParam<Rogan1PSWhite>(Vec(310, 202), module, Elements::DAMPING_PARAM));
  256. addParam(createParam<Rogan1PSWhite>(Vec(380, 202), module, Elements::POSITION_PARAM));
  257. addParam(createParam<Rogan1PSWhite>(Vec(451, 202), module, Elements::SPACE_PARAM));
  258. addParam(createParam<Trimpot>(Vec(104.5, 273), module, Elements::BOW_TIMBRE_MOD_PARAM));
  259. addParam(createParam<Trimpot>(Vec(142.5, 273), module, Elements::FLOW_MOD_PARAM));
  260. addParam(createParam<Trimpot>(Vec(181.5, 273), module, Elements::BLOW_TIMBRE_MOD_PARAM));
  261. addParam(createParam<Trimpot>(Vec(219.5, 273), module, Elements::MALLET_MOD_PARAM));
  262. addParam(createParam<Trimpot>(Vec(257.5, 273), module, Elements::STRIKE_TIMBRE_MOD_PARAM));
  263. addParam(createParam<Trimpot>(Vec(315.5, 273), module, Elements::DAMPING_MOD_PARAM));
  264. addParam(createParam<Trimpot>(Vec(354.5, 273), module, Elements::GEOMETRY_MOD_PARAM));
  265. addParam(createParam<Trimpot>(Vec(392.5, 273), module, Elements::POSITION_MOD_PARAM));
  266. addParam(createParam<Trimpot>(Vec(430.5, 273), module, Elements::BRIGHTNESS_MOD_PARAM));
  267. addParam(createParam<Trimpot>(Vec(469.5, 273), module, Elements::SPACE_MOD_PARAM));
  268. addInput(createInput<PJ301MPort>(Vec(20, 178), module, Elements::NOTE_INPUT));
  269. addInput(createInput<PJ301MPort>(Vec(55, 178), module, Elements::FM_INPUT));
  270. addInput(createInput<PJ301MPort>(Vec(20, 224), module, Elements::GATE_INPUT));
  271. addInput(createInput<PJ301MPort>(Vec(55, 224), module, Elements::STRENGTH_INPUT));
  272. addInput(createInput<PJ301MPort>(Vec(20, 270), module, Elements::BLOW_INPUT));
  273. addInput(createInput<PJ301MPort>(Vec(55, 270), module, Elements::STRIKE_INPUT));
  274. addOutput(createOutput<PJ301MPort>(Vec(20, 316), module, Elements::AUX_OUTPUT));
  275. addOutput(createOutput<PJ301MPort>(Vec(55, 316), module, Elements::MAIN_OUTPUT));
  276. addInput(createInput<PJ301MPort>(Vec(101, 316), module, Elements::BOW_TIMBRE_MOD_INPUT));
  277. addInput(createInput<PJ301MPort>(Vec(139, 316), module, Elements::FLOW_MOD_INPUT));
  278. addInput(createInput<PJ301MPort>(Vec(178, 316), module, Elements::BLOW_TIMBRE_MOD_INPUT));
  279. addInput(createInput<PJ301MPort>(Vec(216, 316), module, Elements::MALLET_MOD_INPUT));
  280. addInput(createInput<PJ301MPort>(Vec(254, 316), module, Elements::STRIKE_TIMBRE_MOD_INPUT));
  281. addInput(createInput<PJ301MPort>(Vec(312, 316), module, Elements::DAMPING_MOD_INPUT));
  282. addInput(createInput<PJ301MPort>(Vec(350, 316), module, Elements::GEOMETRY_MOD_INPUT));
  283. addInput(createInput<PJ301MPort>(Vec(389, 316), module, Elements::POSITION_MOD_INPUT));
  284. addInput(createInput<PJ301MPort>(Vec(427, 316), module, Elements::BRIGHTNESS_MOD_INPUT));
  285. addInput(createInput<PJ301MPort>(Vec(466, 316), module, Elements::SPACE_MOD_INPUT));
  286. addParam(createParam<CKD6>(Vec(36, 116), module, Elements::PLAY_PARAM));
  287. struct GateLight : YellowLight {
  288. GateLight() {
  289. box.size = Vec(28 - 6, 28 - 6);
  290. bgColor = color::BLACK_TRANSPARENT;
  291. }
  292. };
  293. addChild(createLight<GateLight>(Vec(36 + 3, 116 + 3), module, Elements::GATE_LIGHT));
  294. addChild(createLight<MediumLight<GreenLight>>(Vec(184, 165), module, Elements::EXCITER_LIGHT));
  295. addChild(createLight<MediumLight<RedLight>>(Vec(395, 165), module, Elements::RESONATOR_LIGHT));
  296. }
  297. void appendContextMenu(Menu* menu) override {
  298. Elements* elements = dynamic_cast<Elements*>(module);
  299. assert(elements);
  300. menu->addChild(new MenuSeparator);
  301. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Alternative models"));
  302. menu->addChild(construct<ElementsModalItem>(&MenuItem::text, "Original", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 0));
  303. menu->addChild(construct<ElementsModalItem>(&MenuItem::text, "Non-linear string", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 1));
  304. menu->addChild(construct<ElementsModalItem>(&MenuItem::text, "Chords", &ElementsModalItem::elements, elements, &ElementsModalItem::model, 2));
  305. menu->addChild(new MenuSeparator);
  306. menu->addChild(construct<ElementsEasterEggItem>(&MenuItem::text, "Easter egg", &ElementsEasterEggItem::elements, elements));
  307. }
  308. };
  309. Model* modelElements = createModel<Elements, ElementsWidget>("Elements");