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.

357 lines
8.4KB

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