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.

315 lines
7.5KB

  1. #include "util/common.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "AH.hpp"
  4. #include "Core.hpp"
  5. #include "UI.hpp"
  6. namespace rack_plugin_AmalgamatedHarmonics {
  7. struct Ruckus : AHModule {
  8. enum ParamIds {
  9. ENUMS(DIV_PARAM,16),
  10. ENUMS(PROB_PARAM,16),
  11. ENUMS(SHIFT_PARAM,16),
  12. ENUMS(XMUTE_PARAM,16),
  13. ENUMS(YMUTE_PARAM,4),
  14. NUM_PARAMS
  15. };
  16. enum InputIds {
  17. TRIG_INPUT,
  18. RESET_INPUT,
  19. NUM_INPUTS
  20. };
  21. enum OutputIds {
  22. ENUMS(XOUT_OUTPUT,4),
  23. ENUMS(YOUT_OUTPUT,4),
  24. NUM_OUTPUTS
  25. };
  26. enum LightIds {
  27. ENUMS(XMUTE_LIGHT,4),
  28. ENUMS(YMUTE_LIGHT,4),
  29. NUM_LIGHTS
  30. };
  31. Ruckus() : AHModule(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  32. reset();
  33. }
  34. void step() override;
  35. json_t *toJson() override {
  36. json_t *rootJ = json_object();
  37. // gates
  38. json_t *xMutesJ = json_array();
  39. json_t *yMutesJ = json_array();
  40. for (int i = 0; i < 4; i++) {
  41. json_t *xMuteJ = json_integer((int) xMute[i]);
  42. json_array_append_new(xMutesJ, xMuteJ);
  43. json_t *yMuteJ = json_integer((int) yMute[i]);
  44. json_array_append_new(yMutesJ, yMuteJ);
  45. }
  46. json_object_set_new(rootJ, "xMutes", xMutesJ);
  47. json_object_set_new(rootJ, "yMutes", yMutesJ);
  48. return rootJ;
  49. }
  50. void fromJson(json_t *rootJ) override {
  51. // gates
  52. json_t *xMutesJ = json_object_get(rootJ, "xMutes");
  53. if (xMutesJ) {
  54. for (int i = 0; i < 4; i++) {
  55. json_t *xMuteJ = json_array_get(xMutesJ, i);
  56. if (xMuteJ)
  57. xMute[i] = !!json_integer_value(xMuteJ);
  58. }
  59. }
  60. json_t *yMutesJ = json_object_get(rootJ, "yMutes");
  61. if (yMutesJ) {
  62. for (int i = 0; i < 4; i++) {
  63. json_t *yMuteJ = json_array_get(yMutesJ, i);
  64. if (yMuteJ)
  65. yMute[i] = !!json_integer_value(yMuteJ);
  66. }
  67. }
  68. }
  69. enum ParamType {
  70. DIV_TYPE,
  71. SHIFT_TYPE,
  72. PROB_TYPE
  73. };
  74. void receiveEvent(ParamEvent e) override {
  75. if (receiveEvents) {
  76. switch(e.pType) {
  77. case ParamType::DIV_TYPE:
  78. paramState = "> Division: " + std::to_string((int)e.value);
  79. break;
  80. case ParamType::SHIFT_TYPE:
  81. paramState = "> Beat shift: " + std::to_string((int)e.value);
  82. break;
  83. case ParamType::PROB_TYPE:
  84. paramState = "> Probability: " + std::to_string(e.value * 100.0f).substr(0,6) + "%";
  85. break;
  86. default:
  87. paramState = "> UNK:" + std::to_string(e.value).substr(0,6);
  88. }
  89. }
  90. keepStateDisplay = 0;
  91. }
  92. void reset() override {
  93. for (int i = 0; i < 4; i++) {
  94. xMute[i] = true;
  95. yMute[i] = true;
  96. }
  97. }
  98. Core core;
  99. AHPulseGenerator xGate[4];
  100. AHPulseGenerator yGate[4];
  101. bool xMute[4] = {true, true, true, true};
  102. bool yMute[4] = {true, true, true, true};
  103. SchmittTrigger xLockTrigger[4];
  104. SchmittTrigger yLockTrigger[4];
  105. SchmittTrigger inTrigger;
  106. SchmittTrigger resetTrigger;
  107. int division[16];
  108. int shift[16];
  109. float prob[16];
  110. unsigned int beatCounter = 0;
  111. };
  112. void Ruckus::step() {
  113. AHModule::step();
  114. for (int i = 0; i < 4; i++) {
  115. if (xLockTrigger[i].process(params[XMUTE_PARAM + i].value)) {
  116. xMute[i] = !xMute[i];
  117. }
  118. if (yLockTrigger[i].process(params[YMUTE_PARAM + i].value)) {
  119. yMute[i] = !yMute[i];
  120. }
  121. }
  122. for (int i = 0; i < 16; i++) {
  123. division[i] = params[DIV_PARAM + i].value;
  124. prob[i] = params[PROB_PARAM + i].value;
  125. shift[i] = params[SHIFT_PARAM + i].value;
  126. }
  127. if (resetTrigger.process(inputs[RESET_INPUT].value)) {
  128. beatCounter = 0;
  129. }
  130. if (inTrigger.process(inputs[TRIG_INPUT].value)) {
  131. beatCounter++;
  132. for (int y = 0; y < 4; y++) {
  133. for (int x = 0; x < 4; x++) {
  134. int i = y * 4 + x;
  135. if(division[i] == 0) { // 0 == skip
  136. continue;
  137. }
  138. int target = beatCounter + shift[i];
  139. if (target < 0) { // shifted into negative count
  140. continue;
  141. }
  142. if (target % division[i] == 0) {
  143. if (randomUniform() < prob[i]) {
  144. xGate[x].trigger(Core::TRIGGER);
  145. yGate[y].trigger(Core::TRIGGER);
  146. }
  147. }
  148. }
  149. }
  150. }
  151. for (int i = 0; i < 4; i++) {
  152. if (xGate[i].process(delta) && xMute[i]) {
  153. outputs[XOUT_OUTPUT + i].value = 10.0f;
  154. } else {
  155. outputs[XOUT_OUTPUT + i].value = 0.0f;
  156. }
  157. lights[XMUTE_LIGHT + i].value = xMute[i] ? 1.0 : 0.0;
  158. if (yGate[i].process(delta) && yMute[i]) {
  159. outputs[YOUT_OUTPUT + i].value = 10.0f;
  160. } else {
  161. outputs[YOUT_OUTPUT + i].value = 0.0f;
  162. }
  163. lights[YMUTE_LIGHT + i].value = yMute[i] ? 1.0 : 0.0;
  164. }
  165. }
  166. struct RuckusWidget : ModuleWidget {
  167. RuckusWidget(Ruckus *module);
  168. };
  169. RuckusWidget::RuckusWidget(Ruckus *module) : ModuleWidget(module) {
  170. UI ui;
  171. box.size = Vec(390, 380);
  172. {
  173. SVGPanel *panel = new SVGPanel();
  174. panel->box.size = box.size;
  175. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Ruckus.svg")));
  176. addChild(panel);
  177. }
  178. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  179. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  180. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  181. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  182. {
  183. StateDisplay *display = new StateDisplay();
  184. display->module = module;
  185. display->box.pos = Vec(30, 335);
  186. display->box.size = Vec(100, 140);
  187. addChild(display);
  188. }
  189. //299.5 329.7
  190. Vec a = ui.getPosition(UI::PORT, 6, 5, false, false);
  191. a.x = 312.0;
  192. //325.5 329.7
  193. Vec b = ui.getPosition(UI::PORT, 7, 5, false, false);
  194. b.x = 352.0;
  195. addInput(Port::create<PJ301MPort>(a, Port::INPUT, module, Ruckus::TRIG_INPUT));
  196. addInput(Port::create<PJ301MPort>(b, Port::INPUT, module, Ruckus::RESET_INPUT));
  197. float xd = 18.0f;
  198. float yd = 20.0f;
  199. for (int y = 0; y < 4; y++) {
  200. for (int x = 0; x < 4; x++) {
  201. int i = y * 4 + x;
  202. Vec v = ui.getPosition(UI::KNOB, 1 + x * 2, y * 2, true, true);
  203. AHKnobSnap *divW = ParamWidget::create<AHKnobSnap>(v, module, Ruckus::DIV_PARAM + i, 0, 64, 0);
  204. AHParamWidget::set<AHKnobSnap>(divW, Ruckus::DIV_TYPE, i);
  205. addParam(divW);
  206. AHTrimpotNoSnap *probW = ParamWidget::create<AHTrimpotNoSnap>(Vec(v.x + xd, v.y + yd), module, Ruckus::PROB_PARAM + i, 0.0f, 1.0f, 1.0f);
  207. AHParamWidget::set<AHTrimpotNoSnap>(probW, Ruckus::PROB_TYPE, i);
  208. addParam(probW);
  209. AHTrimpotSnap *shiftW = ParamWidget::create<AHTrimpotSnap>(Vec(v.x - xd + 4, v.y + yd), module, Ruckus::SHIFT_PARAM + i, -64.0f, 64.0f, 0.0f);
  210. AHParamWidget::set<AHTrimpotSnap>(shiftW, Ruckus::SHIFT_TYPE, i);
  211. addParam(shiftW);
  212. }
  213. }
  214. float d = 12.0f;
  215. for (int x = 0; x < 4; x++) {
  216. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT, 1 + x * 2, 8, true, true), Port::OUTPUT, module, Ruckus::XOUT_OUTPUT + x));
  217. Vec bVec = ui.getPosition(UI::BUTTON, 1 + x * 2, 7, true, true);
  218. bVec.y = bVec.y + d;
  219. addParam(ParamWidget::create<AHButton>(bVec, module, Ruckus::XMUTE_PARAM + x, 0.0, 1.0, 0.0));
  220. Vec lVec = ui.getPosition(UI::LIGHT, 1 + x * 2, 7, true, true);
  221. lVec.y = lVec.y + d;
  222. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(lVec, module, Ruckus::XMUTE_LIGHT + x));
  223. }
  224. for (int y = 0; y < 4; y++) {
  225. addOutput(Port::create<PJ301MPort>(ui.getPosition(UI::PORT,9, y * 2, true, true), Port::OUTPUT, module, Ruckus::YOUT_OUTPUT + y));
  226. Vec bVec = ui.getPosition(UI::BUTTON, 8, y * 2, true, true);
  227. bVec.x = bVec.x + d;
  228. addParam(ParamWidget::create<AHButton>(bVec, module, Ruckus::YMUTE_PARAM + y, 0.0, 1.0, 0.0));
  229. Vec lVec = ui.getPosition(UI::LIGHT, 8, y * 2, true, true);
  230. lVec.x = lVec.x + d;
  231. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(lVec, module, Ruckus::YMUTE_LIGHT + y));
  232. }
  233. }
  234. } // namespace rack_plugin_AmalgamatedHarmonics
  235. using namespace rack_plugin_AmalgamatedHarmonics;
  236. RACK_PLUGIN_MODEL_INIT(AmalgamatedHarmonics, Ruckus) {
  237. Model *modelRuckus = Model::create<Ruckus, RuckusWidget>( "Amalgamated Harmonics", "Ruckus", "Ruckus", SEQUENCER_TAG);
  238. return modelRuckus;
  239. }