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.

335 lines
13KB

  1. #include "plugin.hpp"
  2. #include "rings/dsp/part.h"
  3. #include "rings/dsp/strummer.h"
  4. #include "rings/dsp/string_synth_part.h"
  5. struct Rings : Module {
  6. enum ParamIds {
  7. POLYPHONY_PARAM,
  8. RESONATOR_PARAM,
  9. FREQUENCY_PARAM,
  10. STRUCTURE_PARAM,
  11. BRIGHTNESS_PARAM,
  12. DAMPING_PARAM,
  13. POSITION_PARAM,
  14. BRIGHTNESS_MOD_PARAM,
  15. FREQUENCY_MOD_PARAM,
  16. DAMPING_MOD_PARAM,
  17. STRUCTURE_MOD_PARAM,
  18. POSITION_MOD_PARAM,
  19. NUM_PARAMS
  20. };
  21. enum InputIds {
  22. BRIGHTNESS_MOD_INPUT,
  23. FREQUENCY_MOD_INPUT,
  24. DAMPING_MOD_INPUT,
  25. STRUCTURE_MOD_INPUT,
  26. POSITION_MOD_INPUT,
  27. STRUM_INPUT,
  28. PITCH_INPUT,
  29. IN_INPUT,
  30. NUM_INPUTS
  31. };
  32. enum OutputIds {
  33. ODD_OUTPUT,
  34. EVEN_OUTPUT,
  35. NUM_OUTPUTS
  36. };
  37. enum LightIds {
  38. POLYPHONY_GREEN_LIGHT, POLYPHONY_RED_LIGHT,
  39. RESONATOR_GREEN_LIGHT, RESONATOR_RED_LIGHT,
  40. NUM_LIGHTS
  41. };
  42. dsp::SampleRateConverter<1> inputSrc;
  43. dsp::SampleRateConverter<2> outputSrc;
  44. dsp::DoubleRingBuffer<dsp::Frame<1>, 256> inputBuffer;
  45. dsp::DoubleRingBuffer<dsp::Frame<2>, 256> outputBuffer;
  46. uint16_t reverb_buffer[32768] = {};
  47. rings::Part part;
  48. rings::StringSynthPart string_synth;
  49. rings::Strummer strummer;
  50. bool strum = false;
  51. bool lastStrum = false;
  52. dsp::SchmittTrigger polyphonyTrigger;
  53. dsp::SchmittTrigger modelTrigger;
  54. int polyphonyMode = 0;
  55. rings::ResonatorModel resonatorModel = rings::RESONATOR_MODEL_MODAL;
  56. bool easterEgg = false;
  57. Rings() {
  58. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  59. configParam(POLYPHONY_PARAM, 0.0, 1.0, 0.0, "Polyphony");
  60. configParam(RESONATOR_PARAM, 0.0, 1.0, 0.0, "Resonator type");
  61. configParam(FREQUENCY_PARAM, 0.0, 60.0, 30.0, "Coarse frequency adjustment");
  62. configParam(STRUCTURE_PARAM, 0.0, 1.0, 0.5, "Harmonic structure");
  63. configParam(BRIGHTNESS_PARAM, 0.0, 1.0, 0.5, "Brightness");
  64. configParam(DAMPING_PARAM, 0.0, 1.0, 0.5, "Decay time");
  65. configParam(POSITION_PARAM, 0.0, 1.0, 0.5, "Excitation position");
  66. configParam(BRIGHTNESS_MOD_PARAM, -1.0, 1.0, 0.0, "Brightness attenuverter");
  67. configParam(FREQUENCY_MOD_PARAM, -1.0, 1.0, 0.0, "Frequency attenuverter");
  68. configParam(DAMPING_MOD_PARAM, -1.0, 1.0, 0.0, "Damping attenuverter");
  69. configParam(STRUCTURE_MOD_PARAM, -1.0, 1.0, 0.0, "Structure attenuverter");
  70. configParam(POSITION_MOD_PARAM, -1.0, 1.0, 0.0, "Position attenuverter");
  71. strummer.Init(0.01, 44100.0 / 24);
  72. part.Init(reverb_buffer);
  73. string_synth.Init(reverb_buffer);
  74. }
  75. void process(const ProcessArgs& args) override {
  76. // TODO
  77. // "Normalized to a pulse/burst generator that reacts to note changes on the V/OCT input."
  78. // Get input
  79. if (!inputBuffer.full()) {
  80. dsp::Frame<1> f;
  81. f.samples[0] = inputs[IN_INPUT].getVoltage() / 5.0;
  82. inputBuffer.push(f);
  83. }
  84. if (!strum) {
  85. strum = inputs[STRUM_INPUT].getVoltage() >= 1.0;
  86. }
  87. // Polyphony / model
  88. if (polyphonyTrigger.process(params[POLYPHONY_PARAM].getValue())) {
  89. polyphonyMode = (polyphonyMode + 1) % 3;
  90. }
  91. lights[POLYPHONY_GREEN_LIGHT].value = (polyphonyMode == 0 || polyphonyMode == 1) ? 1.0 : 0.0;
  92. lights[POLYPHONY_RED_LIGHT].value = (polyphonyMode == 1 || polyphonyMode == 2) ? 1.0 : 0.0;
  93. if (modelTrigger.process(params[RESONATOR_PARAM].getValue())) {
  94. resonatorModel = (rings::ResonatorModel)((resonatorModel + 1) % 3);
  95. }
  96. int modelColor = resonatorModel % 3;
  97. lights[RESONATOR_GREEN_LIGHT].value = (modelColor == 0 || modelColor == 1) ? 1.0 : 0.0;
  98. lights[RESONATOR_RED_LIGHT].value = (modelColor == 1 || modelColor == 2) ? 1.0 : 0.0;
  99. // Render frames
  100. if (outputBuffer.empty()) {
  101. float in[24] = {};
  102. // Convert input buffer
  103. {
  104. inputSrc.setRates(args.sampleRate, 48000);
  105. int inLen = inputBuffer.size();
  106. int outLen = 24;
  107. inputSrc.process(inputBuffer.startData(), &inLen, (dsp::Frame<1>*) in, &outLen);
  108. inputBuffer.startIncr(inLen);
  109. }
  110. // Polyphony
  111. int polyphony = 1 << polyphonyMode;
  112. if (part.polyphony() != polyphony)
  113. part.set_polyphony(polyphony);
  114. // Model
  115. if (easterEgg)
  116. string_synth.set_fx((rings::FxType) resonatorModel);
  117. else
  118. part.set_model(resonatorModel);
  119. // Patch
  120. rings::Patch patch;
  121. float structure = params[STRUCTURE_PARAM].getValue() + 3.3 * dsp::quadraticBipolar(params[STRUCTURE_MOD_PARAM].getValue()) * inputs[STRUCTURE_MOD_INPUT].getVoltage() / 5.0;
  122. patch.structure = clamp(structure, 0.0f, 0.9995f);
  123. patch.brightness = clamp(params[BRIGHTNESS_PARAM].getValue() + 3.3 * dsp::quadraticBipolar(params[BRIGHTNESS_MOD_PARAM].getValue()) * inputs[BRIGHTNESS_MOD_INPUT].getVoltage() / 5.0, 0.0f, 1.0f);
  124. patch.damping = clamp(params[DAMPING_PARAM].getValue() + 3.3 * dsp::quadraticBipolar(params[DAMPING_MOD_PARAM].getValue()) * inputs[DAMPING_MOD_INPUT].getVoltage() / 5.0, 0.0f, 0.9995f);
  125. patch.position = clamp(params[POSITION_PARAM].getValue() + 3.3 * dsp::quadraticBipolar(params[POSITION_MOD_PARAM].getValue()) * inputs[POSITION_MOD_INPUT].getVoltage() / 5.0, 0.0f, 0.9995f);
  126. // Performance
  127. rings::PerformanceState performance_state;
  128. performance_state.note = 12.0 * inputs[PITCH_INPUT].getNormalVoltage(1 / 12.0);
  129. float transpose = params[FREQUENCY_PARAM].getValue();
  130. // Quantize transpose if pitch input is connected
  131. if (inputs[PITCH_INPUT].isConnected()) {
  132. transpose = roundf(transpose);
  133. }
  134. performance_state.tonic = 12.0 + clamp(transpose, 0.0f, 60.0f);
  135. performance_state.fm = clamp(48.0 * 3.3 * dsp::quarticBipolar(params[FREQUENCY_MOD_PARAM].getValue()) * inputs[FREQUENCY_MOD_INPUT].getNormalVoltage(1.0) / 5.0, -48.0f, 48.0f);
  136. performance_state.internal_exciter = !inputs[IN_INPUT].isConnected();
  137. performance_state.internal_strum = !inputs[STRUM_INPUT].isConnected();
  138. performance_state.internal_note = !inputs[PITCH_INPUT].isConnected();
  139. // TODO
  140. // "Normalized to a step detector on the V/OCT input and a transient detector on the IN input."
  141. performance_state.strum = strum && !lastStrum;
  142. lastStrum = strum;
  143. strum = false;
  144. performance_state.chord = clamp((int) roundf(structure * (rings::kNumChords - 1)), 0, rings::kNumChords - 1);
  145. // Process audio
  146. float out[24];
  147. float aux[24];
  148. if (easterEgg) {
  149. strummer.Process(NULL, 24, &performance_state);
  150. string_synth.Process(performance_state, patch, in, out, aux, 24);
  151. }
  152. else {
  153. strummer.Process(in, 24, &performance_state);
  154. part.Process(performance_state, patch, in, out, aux, 24);
  155. }
  156. // Convert output buffer
  157. {
  158. dsp::Frame<2> outputFrames[24];
  159. for (int i = 0; i < 24; i++) {
  160. outputFrames[i].samples[0] = out[i];
  161. outputFrames[i].samples[1] = aux[i];
  162. }
  163. outputSrc.setRates(48000, args.sampleRate);
  164. int inLen = 24;
  165. int outLen = outputBuffer.capacity();
  166. outputSrc.process(outputFrames, &inLen, outputBuffer.endData(), &outLen);
  167. outputBuffer.endIncr(outLen);
  168. }
  169. }
  170. // Set output
  171. if (!outputBuffer.empty()) {
  172. dsp::Frame<2> outputFrame = outputBuffer.shift();
  173. // "Note that you need to insert a jack into each output to split the signals: when only one jack is inserted, both signals are mixed together."
  174. if (outputs[ODD_OUTPUT].isConnected() && outputs[EVEN_OUTPUT].isConnected()) {
  175. outputs[ODD_OUTPUT].setVoltage(clamp(outputFrame.samples[0], -1.0, 1.0) * 5.0);
  176. outputs[EVEN_OUTPUT].setVoltage(clamp(outputFrame.samples[1], -1.0, 1.0) * 5.0);
  177. }
  178. else {
  179. float v = clamp(outputFrame.samples[0] + outputFrame.samples[1], -1.0, 1.0) * 5.0;
  180. outputs[ODD_OUTPUT].setVoltage(v);
  181. outputs[EVEN_OUTPUT].setVoltage(v);
  182. }
  183. }
  184. }
  185. json_t* dataToJson() override {
  186. json_t* rootJ = json_object();
  187. json_object_set_new(rootJ, "polyphony", json_integer(polyphonyMode));
  188. json_object_set_new(rootJ, "model", json_integer((int) resonatorModel));
  189. json_object_set_new(rootJ, "easterEgg", json_boolean(easterEgg));
  190. return rootJ;
  191. }
  192. void dataFromJson(json_t* rootJ) override {
  193. json_t* polyphonyJ = json_object_get(rootJ, "polyphony");
  194. if (polyphonyJ) {
  195. polyphonyMode = json_integer_value(polyphonyJ);
  196. }
  197. json_t* modelJ = json_object_get(rootJ, "model");
  198. if (modelJ) {
  199. resonatorModel = (rings::ResonatorModel) json_integer_value(modelJ);
  200. }
  201. json_t* easterEggJ = json_object_get(rootJ, "easterEgg");
  202. if (easterEggJ) {
  203. easterEgg = json_boolean_value(easterEggJ);
  204. }
  205. }
  206. void onReset() override {
  207. polyphonyMode = 0;
  208. resonatorModel = rings::RESONATOR_MODEL_MODAL;
  209. }
  210. void onRandomize() override {
  211. polyphonyMode = random::u32() % 3;
  212. resonatorModel = (rings::ResonatorModel)(random::u32() % 3);
  213. }
  214. };
  215. struct RingsWidget : ModuleWidget {
  216. RingsWidget(Rings* module) {
  217. setModule(module);
  218. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/Rings.svg")));
  219. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  220. addChild(createWidget<ScrewSilver>(Vec(180, 0)));
  221. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  222. addChild(createWidget<ScrewSilver>(Vec(180, 365)));
  223. addParam(createParam<TL1105>(Vec(14, 40), module, Rings::POLYPHONY_PARAM));
  224. addParam(createParam<TL1105>(Vec(179, 40), module, Rings::RESONATOR_PARAM));
  225. addParam(createParam<Rogan3PSWhite>(Vec(29, 72), module, Rings::FREQUENCY_PARAM));
  226. addParam(createParam<Rogan3PSWhite>(Vec(126, 72), module, Rings::STRUCTURE_PARAM));
  227. addParam(createParam<Rogan1PSWhite>(Vec(13, 158), module, Rings::BRIGHTNESS_PARAM));
  228. addParam(createParam<Rogan1PSWhite>(Vec(83, 158), module, Rings::DAMPING_PARAM));
  229. addParam(createParam<Rogan1PSWhite>(Vec(154, 158), module, Rings::POSITION_PARAM));
  230. addParam(createParam<Trimpot>(Vec(19, 229), module, Rings::BRIGHTNESS_MOD_PARAM));
  231. addParam(createParam<Trimpot>(Vec(57, 229), module, Rings::FREQUENCY_MOD_PARAM));
  232. addParam(createParam<Trimpot>(Vec(96, 229), module, Rings::DAMPING_MOD_PARAM));
  233. addParam(createParam<Trimpot>(Vec(134, 229), module, Rings::STRUCTURE_MOD_PARAM));
  234. addParam(createParam<Trimpot>(Vec(173, 229), module, Rings::POSITION_MOD_PARAM));
  235. addInput(createInput<PJ301MPort>(Vec(15, 273), module, Rings::BRIGHTNESS_MOD_INPUT));
  236. addInput(createInput<PJ301MPort>(Vec(54, 273), module, Rings::FREQUENCY_MOD_INPUT));
  237. addInput(createInput<PJ301MPort>(Vec(92, 273), module, Rings::DAMPING_MOD_INPUT));
  238. addInput(createInput<PJ301MPort>(Vec(131, 273), module, Rings::STRUCTURE_MOD_INPUT));
  239. addInput(createInput<PJ301MPort>(Vec(169, 273), module, Rings::POSITION_MOD_INPUT));
  240. addInput(createInput<PJ301MPort>(Vec(15, 316), module, Rings::STRUM_INPUT));
  241. addInput(createInput<PJ301MPort>(Vec(54, 316), module, Rings::PITCH_INPUT));
  242. addInput(createInput<PJ301MPort>(Vec(92, 316), module, Rings::IN_INPUT));
  243. addOutput(createOutput<PJ301MPort>(Vec(131, 316), module, Rings::ODD_OUTPUT));
  244. addOutput(createOutput<PJ301MPort>(Vec(169, 316), module, Rings::EVEN_OUTPUT));
  245. addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 43), module, Rings::POLYPHONY_GREEN_LIGHT));
  246. addChild(createLight<MediumLight<GreenRedLight>>(Vec(162, 43), module, Rings::RESONATOR_GREEN_LIGHT));
  247. }
  248. void appendContextMenu(Menu* menu) override {
  249. Rings* rings = dynamic_cast<Rings*>(module);
  250. assert(rings);
  251. struct RingsModelItem : MenuItem {
  252. Rings* rings;
  253. rings::ResonatorModel model;
  254. void onAction(const event::Action& e) override {
  255. rings->resonatorModel = model;
  256. }
  257. void step() override {
  258. rightText = (rings->resonatorModel == model) ? "✔" : "";
  259. MenuItem::step();
  260. }
  261. };
  262. struct RingsEasterEggItem : MenuItem {
  263. Rings* rings;
  264. void onAction(const event::Action& e) override {
  265. rings->easterEgg = !rings->easterEgg;
  266. }
  267. void step() override {
  268. rightText = (rings->easterEgg) ? "✔" : "";
  269. MenuItem::step();
  270. }
  271. };
  272. menu->addChild(new MenuSeparator);
  273. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Resonator"));
  274. menu->addChild(construct<RingsModelItem>(&MenuItem::text, "Modal resonator", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_MODAL));
  275. menu->addChild(construct<RingsModelItem>(&MenuItem::text, "Sympathetic strings", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_SYMPATHETIC_STRING));
  276. menu->addChild(construct<RingsModelItem>(&MenuItem::text, "Modulated/inharmonic string", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_STRING));
  277. menu->addChild(construct<RingsModelItem>(&MenuItem::text, "FM voice", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_FM_VOICE));
  278. menu->addChild(construct<RingsModelItem>(&MenuItem::text, "Quantized sympathetic strings", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_SYMPATHETIC_STRING_QUANTIZED));
  279. menu->addChild(construct<RingsModelItem>(&MenuItem::text, "Reverb string", &RingsModelItem::rings, rings, &RingsModelItem::model, rings::RESONATOR_MODEL_STRING_AND_REVERB));
  280. menu->addChild(new MenuSeparator);
  281. menu->addChild(construct<RingsEasterEggItem>(&MenuItem::text, "Disastrous Peace", &RingsEasterEggItem::rings, rings));
  282. }
  283. };
  284. Model* modelRings = createModel<Rings, RingsWidget>("Rings");