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.

341 lines
12KB

  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. configButton(POLYPHONY_PARAM, "Polyphony");
  60. configButton(RESONATOR_PARAM, "Resonator type");
  61. configParam(FREQUENCY_PARAM, 0.0, 60.0, 30.0, "Frequency");
  62. configParam(STRUCTURE_PARAM, 0.0, 1.0, 0.5, "Structure");
  63. configParam(BRIGHTNESS_PARAM, 0.0, 1.0, 0.5, "Brightness");
  64. configParam(DAMPING_PARAM, 0.0, 1.0, 0.5, "Damping");
  65. configParam(POSITION_PARAM, 0.0, 1.0, 0.5, "Position");
  66. configParam(BRIGHTNESS_MOD_PARAM, -1.0, 1.0, 0.0, "Brightness CV");
  67. configParam(FREQUENCY_MOD_PARAM, -1.0, 1.0, 0.0, "Frequency CV");
  68. configParam(DAMPING_MOD_PARAM, -1.0, 1.0, 0.0, "Damping CV");
  69. configParam(STRUCTURE_MOD_PARAM, -1.0, 1.0, 0.0, "Structure CV");
  70. configParam(POSITION_MOD_PARAM, -1.0, 1.0, 0.0, "Position CV");
  71. configInput(BRIGHTNESS_MOD_INPUT, "Brightness");
  72. configInput(FREQUENCY_MOD_INPUT, "Frequency");
  73. configInput(DAMPING_MOD_INPUT, "Damping");
  74. configInput(STRUCTURE_MOD_INPUT, "Structure");
  75. configInput(POSITION_MOD_INPUT, "Position");
  76. configInput(STRUM_INPUT, "Strum");
  77. configInput(PITCH_INPUT, "Pitch (1V/oct)");
  78. configInput(IN_INPUT, "Audio");
  79. configOutput(ODD_OUTPUT, "Odd");
  80. configOutput(EVEN_OUTPUT, "Even");
  81. configBypass(IN_INPUT, ODD_OUTPUT);
  82. configBypass(IN_INPUT, EVEN_OUTPUT);
  83. strummer.Init(0.01, 44100.0 / 24);
  84. part.Init(reverb_buffer);
  85. string_synth.Init(reverb_buffer);
  86. }
  87. void process(const ProcessArgs& args) override {
  88. // TODO
  89. // "Normalized to a pulse/burst generator that reacts to note changes on the V/OCT input."
  90. // Get input
  91. if (!inputBuffer.full()) {
  92. dsp::Frame<1> f;
  93. f.samples[0] = inputs[IN_INPUT].getVoltage() / 5.0;
  94. inputBuffer.push(f);
  95. }
  96. if (!strum) {
  97. strum = inputs[STRUM_INPUT].getVoltage() >= 1.0;
  98. }
  99. // Polyphony / model
  100. if (polyphonyTrigger.process(params[POLYPHONY_PARAM].getValue())) {
  101. polyphonyMode = (polyphonyMode + 1) % 3;
  102. }
  103. lights[POLYPHONY_GREEN_LIGHT].value = (polyphonyMode == 0 || polyphonyMode == 1) ? 1.0 : 0.0;
  104. lights[POLYPHONY_RED_LIGHT].value = (polyphonyMode == 1 || polyphonyMode == 2) ? 1.0 : 0.0;
  105. if (modelTrigger.process(params[RESONATOR_PARAM].getValue())) {
  106. resonatorModel = (rings::ResonatorModel)((resonatorModel + 1) % 3);
  107. }
  108. int modelColor = resonatorModel % 3;
  109. lights[RESONATOR_GREEN_LIGHT].value = (modelColor == 0 || modelColor == 1) ? 1.0 : 0.0;
  110. lights[RESONATOR_RED_LIGHT].value = (modelColor == 1 || modelColor == 2) ? 1.0 : 0.0;
  111. // Render frames
  112. if (outputBuffer.empty()) {
  113. float in[24] = {};
  114. // Convert input buffer
  115. {
  116. inputSrc.setRates(args.sampleRate, 48000);
  117. int inLen = inputBuffer.size();
  118. int outLen = 24;
  119. inputSrc.process(inputBuffer.startData(), &inLen, (dsp::Frame<1>*) in, &outLen);
  120. inputBuffer.startIncr(inLen);
  121. }
  122. // Polyphony
  123. int polyphony = 1 << polyphonyMode;
  124. if (part.polyphony() != polyphony)
  125. part.set_polyphony(polyphony);
  126. // Model
  127. if (easterEgg)
  128. string_synth.set_fx((rings::FxType) resonatorModel);
  129. else
  130. part.set_model(resonatorModel);
  131. // Patch
  132. rings::Patch patch;
  133. float structure = params[STRUCTURE_PARAM].getValue() + 3.3 * dsp::quadraticBipolar(params[STRUCTURE_MOD_PARAM].getValue()) * inputs[STRUCTURE_MOD_INPUT].getVoltage() / 5.0;
  134. patch.structure = clamp(structure, 0.0f, 0.9995f);
  135. 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);
  136. 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);
  137. 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);
  138. // Performance
  139. rings::PerformanceState performance_state;
  140. performance_state.note = 12.0 * inputs[PITCH_INPUT].getNormalVoltage(1 / 12.0);
  141. float transpose = params[FREQUENCY_PARAM].getValue();
  142. // Quantize transpose if pitch input is connected
  143. if (inputs[PITCH_INPUT].isConnected()) {
  144. transpose = roundf(transpose);
  145. }
  146. performance_state.tonic = 12.0 + clamp(transpose, 0.0f, 60.0f);
  147. 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);
  148. performance_state.internal_exciter = !inputs[IN_INPUT].isConnected();
  149. performance_state.internal_strum = !inputs[STRUM_INPUT].isConnected();
  150. performance_state.internal_note = !inputs[PITCH_INPUT].isConnected();
  151. // TODO
  152. // "Normalized to a step detector on the V/OCT input and a transient detector on the IN input."
  153. performance_state.strum = strum && !lastStrum;
  154. lastStrum = strum;
  155. strum = false;
  156. performance_state.chord = clamp((int) roundf(structure * (rings::kNumChords - 1)), 0, rings::kNumChords - 1);
  157. // Process audio
  158. float out[24];
  159. float aux[24];
  160. if (easterEgg) {
  161. strummer.Process(NULL, 24, &performance_state);
  162. string_synth.Process(performance_state, patch, in, out, aux, 24);
  163. }
  164. else {
  165. strummer.Process(in, 24, &performance_state);
  166. part.Process(performance_state, patch, in, out, aux, 24);
  167. }
  168. // Convert output buffer
  169. {
  170. dsp::Frame<2> outputFrames[24];
  171. for (int i = 0; i < 24; i++) {
  172. outputFrames[i].samples[0] = out[i];
  173. outputFrames[i].samples[1] = aux[i];
  174. }
  175. outputSrc.setRates(48000, args.sampleRate);
  176. int inLen = 24;
  177. int outLen = outputBuffer.capacity();
  178. outputSrc.process(outputFrames, &inLen, outputBuffer.endData(), &outLen);
  179. outputBuffer.endIncr(outLen);
  180. }
  181. }
  182. // Set output
  183. if (!outputBuffer.empty()) {
  184. dsp::Frame<2> outputFrame = outputBuffer.shift();
  185. // "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."
  186. if (outputs[ODD_OUTPUT].isConnected() && outputs[EVEN_OUTPUT].isConnected()) {
  187. outputs[ODD_OUTPUT].setVoltage(clamp(outputFrame.samples[0], -1.0, 1.0) * 5.0);
  188. outputs[EVEN_OUTPUT].setVoltage(clamp(outputFrame.samples[1], -1.0, 1.0) * 5.0);
  189. }
  190. else {
  191. float v = clamp(outputFrame.samples[0] + outputFrame.samples[1], -1.0, 1.0) * 5.0;
  192. outputs[ODD_OUTPUT].setVoltage(v);
  193. outputs[EVEN_OUTPUT].setVoltage(v);
  194. }
  195. }
  196. }
  197. json_t* dataToJson() override {
  198. json_t* rootJ = json_object();
  199. json_object_set_new(rootJ, "polyphony", json_integer(polyphonyMode));
  200. json_object_set_new(rootJ, "model", json_integer((int) resonatorModel));
  201. json_object_set_new(rootJ, "easterEgg", json_boolean(easterEgg));
  202. return rootJ;
  203. }
  204. void dataFromJson(json_t* rootJ) override {
  205. json_t* polyphonyJ = json_object_get(rootJ, "polyphony");
  206. if (polyphonyJ) {
  207. polyphonyMode = json_integer_value(polyphonyJ);
  208. }
  209. json_t* modelJ = json_object_get(rootJ, "model");
  210. if (modelJ) {
  211. resonatorModel = (rings::ResonatorModel) json_integer_value(modelJ);
  212. }
  213. json_t* easterEggJ = json_object_get(rootJ, "easterEgg");
  214. if (easterEggJ) {
  215. easterEgg = json_boolean_value(easterEggJ);
  216. }
  217. }
  218. void onReset() override {
  219. polyphonyMode = 0;
  220. resonatorModel = rings::RESONATOR_MODEL_MODAL;
  221. }
  222. void onRandomize() override {
  223. polyphonyMode = random::u32() % 3;
  224. resonatorModel = (rings::ResonatorModel)(random::u32() % 3);
  225. }
  226. };
  227. struct RingsWidget : ModuleWidget {
  228. RingsWidget(Rings* module) {
  229. setModule(module);
  230. setPanel(Svg::load(asset::plugin(pluginInstance, "res/Rings.svg")));
  231. addChild(createWidget<ScrewSilver>(Vec(15, 0)));
  232. addChild(createWidget<ScrewSilver>(Vec(180, 0)));
  233. addChild(createWidget<ScrewSilver>(Vec(15, 365)));
  234. addChild(createWidget<ScrewSilver>(Vec(180, 365)));
  235. addParam(createParam<TL1105>(Vec(14, 40), module, Rings::POLYPHONY_PARAM));
  236. addParam(createParam<TL1105>(Vec(179, 40), module, Rings::RESONATOR_PARAM));
  237. addParam(createParam<Rogan3PSWhite>(Vec(29, 72), module, Rings::FREQUENCY_PARAM));
  238. addParam(createParam<Rogan3PSWhite>(Vec(126, 72), module, Rings::STRUCTURE_PARAM));
  239. addParam(createParam<Rogan1PSWhite>(Vec(13, 158), module, Rings::BRIGHTNESS_PARAM));
  240. addParam(createParam<Rogan1PSWhite>(Vec(83, 158), module, Rings::DAMPING_PARAM));
  241. addParam(createParam<Rogan1PSWhite>(Vec(154, 158), module, Rings::POSITION_PARAM));
  242. addParam(createParam<Trimpot>(Vec(19, 229), module, Rings::BRIGHTNESS_MOD_PARAM));
  243. addParam(createParam<Trimpot>(Vec(57, 229), module, Rings::FREQUENCY_MOD_PARAM));
  244. addParam(createParam<Trimpot>(Vec(96, 229), module, Rings::DAMPING_MOD_PARAM));
  245. addParam(createParam<Trimpot>(Vec(134, 229), module, Rings::STRUCTURE_MOD_PARAM));
  246. addParam(createParam<Trimpot>(Vec(173, 229), module, Rings::POSITION_MOD_PARAM));
  247. addInput(createInput<PJ301MPort>(Vec(15, 273), module, Rings::BRIGHTNESS_MOD_INPUT));
  248. addInput(createInput<PJ301MPort>(Vec(54, 273), module, Rings::FREQUENCY_MOD_INPUT));
  249. addInput(createInput<PJ301MPort>(Vec(92, 273), module, Rings::DAMPING_MOD_INPUT));
  250. addInput(createInput<PJ301MPort>(Vec(131, 273), module, Rings::STRUCTURE_MOD_INPUT));
  251. addInput(createInput<PJ301MPort>(Vec(169, 273), module, Rings::POSITION_MOD_INPUT));
  252. addInput(createInput<PJ301MPort>(Vec(15, 316), module, Rings::STRUM_INPUT));
  253. addInput(createInput<PJ301MPort>(Vec(54, 316), module, Rings::PITCH_INPUT));
  254. addInput(createInput<PJ301MPort>(Vec(92, 316), module, Rings::IN_INPUT));
  255. addOutput(createOutput<PJ301MPort>(Vec(131, 316), module, Rings::ODD_OUTPUT));
  256. addOutput(createOutput<PJ301MPort>(Vec(169, 316), module, Rings::EVEN_OUTPUT));
  257. addChild(createLight<MediumLight<GreenRedLight>>(Vec(37, 43), module, Rings::POLYPHONY_GREEN_LIGHT));
  258. addChild(createLight<MediumLight<GreenRedLight>>(Vec(162, 43), module, Rings::RESONATOR_GREEN_LIGHT));
  259. }
  260. void appendContextMenu(Menu* menu) override {
  261. Rings* module = dynamic_cast<Rings*>(this->module);
  262. assert(module);
  263. menu->addChild(new MenuSeparator);
  264. menu->addChild(createMenuLabel("Resonator"));
  265. static const std::vector<std::string> modelLabels = {
  266. "Modal resonator",
  267. "Sympathetic strings",
  268. "Modulated/inharmonic string",
  269. "FM voice",
  270. "Quantized sympathetic strings",
  271. "Reverb string",
  272. };
  273. for (int i = 0; i < 6; i++) {
  274. menu->addChild(createCheckMenuItem(modelLabels[i], "",
  275. [=]() {return module->resonatorModel == i;},
  276. [=]() {module->resonatorModel = (rings::ResonatorModel) i;}
  277. ));
  278. }
  279. menu->addChild(new MenuSeparator);
  280. menu->addChild(createBoolMenuItem("Disastrous Peace", "",
  281. [=]() {return module->easterEgg;},
  282. [=](bool val) {module->easterEgg = val;}
  283. ));
  284. }
  285. };
  286. Model* modelRings = createModel<Rings, RingsWidget>("Rings");