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.

339 lines
12KB

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