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.

270 lines
9.3KB

  1. #include "dsp/digital.hpp"
  2. #include "util/math.hpp"
  3. #include "qwelk.hpp"
  4. #define CHANNELS 8
  5. namespace rack_plugin_Qwelk {
  6. struct ModuleAutomaton : Module {
  7. enum ParamIds {
  8. PARAM_SCAN,
  9. PARAM_STEP,
  10. PARAM_CELL,
  11. NUM_PARAMS = PARAM_CELL + CHANNELS * 2
  12. };
  13. enum InputIds {
  14. INPUT_SCAN,
  15. INPUT_STEP,
  16. INPUT_RULE,
  17. NUM_INPUTS = INPUT_RULE + 8
  18. };
  19. enum OutputIds {
  20. OUTPUT_COUNT,
  21. OUTPUT_NUMBER,
  22. OUTPUT_CELL,
  23. NUM_OUTPUTS = OUTPUT_CELL + CHANNELS
  24. };
  25. enum LightIds {
  26. LIGHT_POS_SCAN,
  27. LIGHT_NEG_SCAN,
  28. LIGHT_STEP,
  29. LIGHT_MUTE,
  30. NUM_LIGHTS = LIGHT_MUTE + CHANNELS * 2
  31. };
  32. int fun = 0;
  33. int scan = 1;
  34. int scan_sign = 0;
  35. SchmittTrigger trig_step_input;
  36. SchmittTrigger trig_step_manual;
  37. SchmittTrigger trig_scan_manual;
  38. SchmittTrigger trig_scan_input;
  39. SchmittTrigger trig_cells[CHANNELS*2];
  40. int states[CHANNELS*2] {};
  41. const float output_volt = 5.0;
  42. const float output_volt_uni = output_volt * 2;
  43. ModuleAutomaton() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  44. void step() override;
  45. json_t *toJson() override {
  46. json_t *rootJ = json_object();
  47. json_object_set_new(rootJ, "scan", json_integer(scan));
  48. json_object_set_new(rootJ, "fun", json_integer(fun));
  49. json_t *statesJ = json_array();
  50. for (int i = 0; i < CHANNELS*2; i++) {
  51. json_t *stateJ = json_integer(states[i]);
  52. json_array_append_new(statesJ, stateJ);
  53. }
  54. json_object_set_new(rootJ, "states", statesJ);
  55. return rootJ;
  56. }
  57. void fromJson(json_t *rootJ) override {
  58. json_t *scanJ = json_object_get(rootJ, "scan");
  59. if (scanJ)
  60. scan = json_integer_value(scanJ);
  61. json_t *funJ = json_object_get(rootJ, "fun");
  62. if (funJ)
  63. fun = json_integer_value(funJ);
  64. // gates
  65. json_t *statesJ = json_object_get(rootJ, "states");
  66. if (statesJ) {
  67. for (int i = 0; i < 8; i++) {
  68. json_t *gateJ = json_array_get(statesJ, i);
  69. if (gateJ)
  70. states[i] = json_integer_value(gateJ);
  71. }
  72. }
  73. }
  74. void reset() override {
  75. fun = 0;
  76. scan = 1;
  77. for (int i = 0; i < CHANNELS * 2; i++)
  78. states[i] = 0;
  79. }
  80. void randomize() override {
  81. scan = (randomUniform() > 0.5) ? 1 : -1;
  82. for (int i = 0; i < CHANNELS; i++)
  83. states[i] = (randomUniform() > 0.5);
  84. }
  85. };
  86. void ModuleAutomaton::step()
  87. {
  88. int nextstep = 0;
  89. if (trig_step_manual.process(params[PARAM_STEP].value)
  90. || trig_step_input.process(inputs[INPUT_STEP].value))
  91. nextstep = 1;
  92. // determine scan direction
  93. int scan_input_sign = (int)sgn(inputs[INPUT_SCAN].normalize(scan));
  94. if (scan_input_sign != scan_sign)
  95. scan = scan_sign = scan_input_sign;
  96. // manual tinkering with step?
  97. if (trig_scan_manual.process(params[PARAM_SCAN].value))
  98. scan *= -1;
  99. if (nextstep) {
  100. int rule = 0;
  101. // read rule from inputs
  102. for (int i = 0; i < CHANNELS; ++i)
  103. if (inputs[INPUT_RULE + i].active && inputs[INPUT_RULE + i].value > 0.0)
  104. rule |= 1 << i;
  105. // copy prev state to output cells
  106. for (int i = 0; i < CHANNELS; ++i)
  107. states[CHANNELS + i] = states[i];
  108. // determine the next gen
  109. for (int i = 0; i < CHANNELS; ++i) {
  110. int sum = 0;
  111. int tl = i == 0 ? CHANNELS - 1 : i - 1;
  112. int tm = i;
  113. int tr = i < CHANNELS - 1 ? i + (1 - fun) : 0;
  114. sum |= states[CHANNELS + tr] ? (1 << 0) : 0;
  115. sum |= states[CHANNELS + tm] ? (1 << 1) : 0;
  116. sum |= states[CHANNELS + tl] ? (1 << 2) : 0;
  117. states[i] = (rule & (1 << sum)) != 0 ? 1 : 0;
  118. }
  119. }
  120. // handle manual tinkering with the state
  121. for (int i = 0; i < CHANNELS * 2; ++i)
  122. if (trig_cells[i].process(params[PARAM_CELL + i].value))
  123. states[i] ^= 1;
  124. int count = 0, number = 0;
  125. for (int i = 0; i < CHANNELS; ++i) {
  126. count += states[i + CHANNELS];
  127. if (scan >= 0)
  128. number |= ((1 << i) * states[CHANNELS + i]);
  129. else
  130. number |= ((1 << (CHANNELS - 1 - i)) * states[CHANNELS + i]);
  131. }
  132. // individual gate output
  133. for (int i = 0; i < CHANNELS; ++i)
  134. outputs[OUTPUT_CELL + i].value = states[i + CHANNELS] ? output_volt : 0.0;
  135. // number of LIVE cells
  136. outputs[OUTPUT_COUNT].value = ((float)count / (float)CHANNELS) * output_volt_uni;
  137. // the binary number LIVE cells represent
  138. outputs[OUTPUT_NUMBER].value = ((float)number / (float)((1 << CHANNELS) - 1)) * output_volt_uni;
  139. // indicate step direction
  140. lights[LIGHT_POS_SCAN].setBrightness(scan < 0 ? 0.0 : 0.9);
  141. lights[LIGHT_NEG_SCAN].setBrightness(scan < 0 ? 0.9 : 0.0);
  142. // indicate next generation
  143. lights[LIGHT_STEP].setBrightness(trig_step_manual.isHigh() || trig_step_input.isHigh() ? 0.9 : 0.0);
  144. // blink according to state
  145. for (int i = 0; i < CHANNELS * 2; ++i)
  146. lights[LIGHT_MUTE + i].setBrightness(states[i] ? 0.9 : 0.0);
  147. }
  148. template <typename _BASE>
  149. struct MuteLight : _BASE {
  150. MuteLight()
  151. {
  152. this->box.size = mm2px(Vec(6, 6));
  153. }
  154. };
  155. struct WidgetAutomaton : ModuleWidget {
  156. WidgetAutomaton(ModuleAutomaton *module);
  157. Menu *createContextMenu() override;
  158. };
  159. WidgetAutomaton::WidgetAutomaton(ModuleAutomaton *module) : ModuleWidget(module) {
  160. setPanel(SVG::load(assetPlugin(plugin, "res/Automaton.svg")));
  161. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  162. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 0)));
  163. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  164. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 30, 365)));
  165. const float ypad = 27.5;
  166. const float tlpy = 1.75;
  167. const float lghx = box.size.x / 2.0;
  168. const float tlpx = 2.25;
  169. const float dist = 25;
  170. float ytop = 55;
  171. addInput(Port::create<PJ301MPort>( Vec(lghx - dist * 2 , ytop - ypad ), Port::INPUT, module, ModuleAutomaton::INPUT_SCAN));
  172. addParam(ParamWidget::create<LEDBezel>( Vec(lghx + dist , ytop - ypad ), module, ModuleAutomaton::PARAM_SCAN, 0.0, 1.0, 0.0));
  173. addChild(ModuleLightWidget::create<MuteLight<GreenRedLight>>( Vec(lghx + dist + tlpx , ytop - ypad + tlpy ), module, ModuleAutomaton::LIGHT_POS_SCAN));
  174. ytop += ypad;
  175. addInput(Port::create<PJ301MPort>( Vec(lghx - dist * 2 , ytop - ypad ), Port::INPUT, module, ModuleAutomaton::INPUT_STEP));
  176. addParam(ParamWidget::create<LEDBezel>( Vec(lghx + dist , ytop - ypad ), module, ModuleAutomaton::PARAM_STEP, 0.0, 1.0, 0.0));
  177. addChild(ModuleLightWidget::create<MuteLight<GreenLight>>(Vec(lghx + dist + tlpx , ytop - ypad + tlpy ), module, ModuleAutomaton::LIGHT_STEP));
  178. for (int i = 0; i < CHANNELS; ++i) {
  179. addInput(Port::create<PJ301MPort>( Vec(lghx - dist * 2 , ytop + ypad * i ), Port::INPUT, module, ModuleAutomaton::INPUT_RULE + i));
  180. addParam(ParamWidget::create<LEDBezel>( Vec(lghx - dist , ytop + ypad * i ), module, ModuleAutomaton::PARAM_CELL + i, 0.0, 1.0, 0.0));
  181. addChild(ModuleLightWidget::create<MuteLight<GreenLight>>(Vec(lghx - dist + tlpx , ytop + ypad * i + tlpy), module, ModuleAutomaton::LIGHT_MUTE + i));
  182. addParam(ParamWidget::create<LEDBezel>( Vec(lghx , ytop + ypad * i ), module, ModuleAutomaton::PARAM_CELL + CHANNELS + i, 0.0, 1.0, 0.0));
  183. addChild(ModuleLightWidget::create<MuteLight<GreenLight>>(Vec(lghx + tlpx , ytop + ypad * i + tlpy), module, ModuleAutomaton::LIGHT_MUTE + CHANNELS + i));
  184. addOutput(Port::create<PJ301MPort>( Vec(lghx + dist , ytop + ypad * i ), Port::OUTPUT, module, ModuleAutomaton::OUTPUT_CELL + i));
  185. }
  186. const float output_y = ytop + ypad * CHANNELS;
  187. addOutput(Port::create<PJ301MPort>(Vec(lghx + dist, output_y ), Port::OUTPUT, module, ModuleAutomaton::OUTPUT_NUMBER));
  188. addOutput(Port::create<PJ301MPort>(Vec(lghx + dist, output_y + ypad ), Port::OUTPUT, module, ModuleAutomaton::OUTPUT_COUNT));
  189. }
  190. struct MenuItemFun : MenuItem {
  191. ModuleAutomaton *automaton;
  192. void onAction(EventAction &e) override
  193. {
  194. automaton->fun ^= 1;
  195. }
  196. void step () override
  197. {
  198. rightText = (automaton->fun) ? "✔" : "";
  199. }
  200. };
  201. Menu *WidgetAutomaton::createContextMenu()
  202. {
  203. Menu *menu = ModuleWidget::createContextMenu();
  204. MenuLabel *spacer = new MenuLabel();
  205. menu->addChild(spacer);
  206. ModuleAutomaton *automaton = dynamic_cast<ModuleAutomaton *>(module);
  207. assert(automaton);
  208. MenuItemFun *item = new MenuItemFun();
  209. item->text = "FUN";
  210. item->automaton = automaton;
  211. menu->addChild(item);
  212. return menu;
  213. }
  214. } // namespace rack_plugin_Qwelk
  215. using namespace rack_plugin_Qwelk;
  216. RACK_PLUGIN_MODEL_INIT(Qwelk, Automaton) {
  217. Model *modelAutomaton = Model::create<ModuleAutomaton, WidgetAutomaton>(
  218. TOSTRING(SLUG), "Automaton", "Automaton", SEQUENCER_TAG);
  219. return modelAutomaton;
  220. }