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.

439 lines
13KB

  1. #include "LFSR.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "TriadexEngine.hpp"
  4. namespace rack_plugin_alto777_LFSR {
  5. struct Amuse : Module {
  6. enum ParamIds {
  7. /* 8 40 step slide fader switches */
  8. ENUMS(SLIDEPOT_PARAM, 8),
  9. RATE_PARAM,
  10. RUN_HOLD_PARAM,
  11. STEP_PARAM,
  12. RESET_BUTTON_PARAM,
  13. TRIG_EDGES_PARAM,
  14. REST_NORMAL_PARAM,
  15. DELTA_ALL_PARAM,
  16. NUM_PARAMS
  17. };
  18. enum InputIds {
  19. CLOCK_CV_INPUT,
  20. EXT_CLOCK_INPUT,
  21. RUN_HOLD_INPUT,
  22. RESET_INPUT,
  23. NUM_INPUTS
  24. };
  25. enum OutputIds {
  26. NOTE_OUTPUT, /* as tempered CV 0-3 volts */
  27. TRIGGER_OUTPUT, /* 1ms pulse on clock edges (now configurable) */
  28. CLOCK_OUTPUT, /* from the synthetic clock */
  29. NUM_OUTPUTS
  30. };
  31. enum LightIds {
  32. RUN_HOLD_LAMP,
  33. RESET_LAMP,
  34. STEP_LAMP,
  35. /* Triadex state machine lamps */
  36. ENUMS(TRIADEX_LAMP, 40),
  37. NUM_LIGHTS
  38. };
  39. /* clock edge detector and Triadex test state */
  40. bool clockState = false; /* we watch the clock for edges, this is state for that */
  41. bool runnable = false; /* don't until we see a reset or intializing */
  42. // bool wasRunning = false;
  43. // bool gateIn = false;
  44. PulseGenerator resetPulse;
  45. SchmittTrigger resetTrigger;
  46. bool running = true;
  47. SchmittTrigger runningTrigger;
  48. SchmittTrigger resetButtonTrigger;
  49. SchmittTrigger resetInputTrigger;
  50. PulseGenerator triggerPulse;
  51. /* Triadex control vectors */
  52. int interval[4];
  53. int theme[4];
  54. float phase = 0.0;
  55. float blinkPhase = 0.0;
  56. SchmittTrigger clockTrigger;
  57. TriadexEngine theTriadex;
  58. float previousNote = 0.123456f;
  59. Amuse() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  60. void step() override;
  61. void onReset() override {
  62. theTriadex.reset();
  63. theTriadex.setIntervalAndTheme(interval, theme);
  64. }
  65. void onCreate() override {
  66. theTriadex.reset();
  67. theTriadex.setIntervalAndTheme(interval, theme);
  68. }
  69. void myInit() {
  70. // theTriadex.reset();
  71. theTriadex.setIntervalAndTheme(interval, theme);
  72. runnable = true;
  73. }
  74. /**/
  75. json_t *toJson() override {
  76. json_t *rootJ = json_object();
  77. // running
  78. json_object_set_new(rootJ, "running", json_boolean(running));
  79. // state
  80. json_t *state = json_array();
  81. int temp[4];
  82. theTriadex.getTriadexState(temp);
  83. for (int i = 0; i < 4; i++) {
  84. json_array_insert_new(state, i, json_integer((int) temp[i]));
  85. }
  86. json_object_set_new(rootJ, "state", state);
  87. return rootJ;
  88. }
  89. void fromJson(json_t *rootJ) override {
  90. // running
  91. json_t *runningJ = json_object_get(rootJ, "running");
  92. if (runningJ)
  93. running = json_is_true(runningJ);
  94. // gates
  95. json_t *state = json_object_get(rootJ, "state");
  96. if (state) {
  97. int temp[4];
  98. for (int i = 0; i < 4; i++) {
  99. json_t *wha = json_array_get(state, i);
  100. if (wha)
  101. temp[i] = json_integer_value(wha);
  102. }
  103. theTriadex.setTriadexState(temp);
  104. }
  105. }
  106. /**/
  107. };
  108. struct tinyLEDButton : SVGSwitch, MomentarySwitch {
  109. tinyLEDButton() {
  110. addFrame(SVG::load(assetPlugin(plugin, "res/tinyLEDButton.svg")));
  111. }
  112. };
  113. template <typename BASE>
  114. struct bigLight : BASE {
  115. bigLight() {
  116. this->box.size = mm2px(Vec(6.0, 6.0));
  117. }
  118. };
  119. struct my2Switch : SVGSwitch, ToggleSwitch {
  120. my2Switch() {
  121. addFrame(SVG::load(assetPlugin(plugin, "res/togSwitch0ff.svg")));
  122. addFrame(SVG::load(assetPlugin(plugin, "res/togSwitch0n.svg")));
  123. }
  124. };
  125. struct myOther2Switch : SVGSwitch, ToggleSwitch {
  126. myOther2Switch() {
  127. addFrame(SVG::load(assetPlugin(plugin, "res/myCKSS_1.svg")));
  128. addFrame(SVG::load(assetPlugin(plugin, "res/myCKSS_0.svg")));
  129. }
  130. };
  131. struct my3Switch : SVGSwitch, ToggleSwitch {
  132. my3Switch() {
  133. addFrame(SVG::load(assetPlugin(plugin, "res/myCKSSThree_0.svg")));
  134. addFrame(SVG::load(assetPlugin(plugin, "res/myCKSSThree_1.svg")));
  135. addFrame(SVG::load(assetPlugin(plugin, "res/myCKSSThree_2.svg")));
  136. }
  137. };
  138. void Amuse::step() {
  139. if (!runnable) myInit();
  140. // bool running = (params[RUN_HOLD_PARAM].value > 0.0f) && (!(inputs[RUN_HOLD_INPUT].value > 0.0f));
  141. if (runningTrigger.process(params[RUN_HOLD_PARAM].value))
  142. running = !running;
  143. if (inputs[RUN_HOLD_INPUT].active)
  144. running = inputs[RUN_HOLD_INPUT].value <= 0.0f;
  145. lights[RUN_HOLD_LAMP].value = running ? 1.0f : 0.0;
  146. /*
  147. if (!wasRunning && running) phase = 0.0;
  148. if (wasRunning && !running) clockState = 0;
  149. wasRunning = running;
  150. */
  151. bool zeroEm = false;
  152. if (resetButtonTrigger.process(params[RESET_BUTTON_PARAM].value) || resetInputTrigger.process(inputs[RESET_INPUT].value))
  153. zeroEm = true;
  154. float deltaTime = 1.0 / engineGetSampleRate();
  155. // bool clockState persistent...
  156. bool risingEdge = 0;
  157. bool fallingEdge = 0;
  158. bool stepTime = 0;
  159. if (zeroEm) {
  160. theTriadex.reset();
  161. resetPulse.trigger(0.166);
  162. }
  163. lights[RESET_LAMP].value = resetPulse.process(deltaTime) ? 1.0f : 0.0;
  164. bool syntheticClock = false;
  165. if (running) {
  166. if (inputs[EXT_CLOCK_INPUT].active) {
  167. // External clock
  168. if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value))
  169. phase = 0.0;
  170. syntheticClock = clockTrigger.isHigh();
  171. }
  172. else {
  173. // Internal clock
  174. float clockTime = powf(2.0f, params[RATE_PARAM].value + inputs[CLOCK_CV_INPUT].value);
  175. phase += clockTime * engineGetSampleTime();
  176. if (phase >= 1.0)
  177. phase -= 1.0;
  178. syntheticClock = (phase < 0.5f);
  179. }
  180. }
  181. else { /* not running */
  182. // clockState = 0;
  183. syntheticClock = params[STEP_PARAM].value > 0.0;
  184. lights[STEP_LAMP].value = syntheticClock ? 1.0 : 0.0;
  185. }
  186. if (syntheticClock) {
  187. if (!clockState) {
  188. fallingEdge = 1;
  189. clockState = 1;
  190. }
  191. }
  192. else {
  193. if (clockState) {
  194. risingEdge = 1;
  195. clockState = 0;
  196. }
  197. }
  198. stepTime = risingEdge || fallingEdge;
  199. /* load the control vectors */
  200. for (int i = 0; i < 4; i++) {
  201. theme[i] = params[SLIDEPOT_PARAM + i].value + 0.05;
  202. interval[i] = params[SLIDEPOT_PARAM + 7 - i].value + 0.05;
  203. }
  204. if (runnable) { /* just my kludgy one-shot */
  205. if (stepTime) {
  206. theTriadex.halfStep();
  207. float theNote = theTriadex.getNote();
  208. bool doTrigger = 0;
  209. if (params[DELTA_ALL_PARAM].value > 0.0f) doTrigger = 1; /* but still on edges */
  210. else doTrigger = fabs(theNote - previousNote) > 0.01f;
  211. if (doTrigger) {
  212. if (params[TRIG_EDGES_PARAM].value > 1.1f) doTrigger = fallingEdge;
  213. else if (params[TRIG_EDGES_PARAM].value < 0.9f) doTrigger = risingEdge;
  214. else doTrigger = 1;
  215. }
  216. if ((theNote < 0.01) && params[REST_NORMAL_PARAM].value <= 0.0f) doTrigger = 0;
  217. else { /* leave trigger unchanged but for sure update the note */
  218. outputs[NOTE_OUTPUT].value = theNote;
  219. previousNote = theNote;
  220. }
  221. if (doTrigger)
  222. triggerPulse.trigger(0.001); /* std 1 mS */
  223. }
  224. }
  225. outputs[TRIGGER_OUTPUT].value = triggerPulse.process(deltaTime) ? 10.0f : 0.0;
  226. outputs[CLOCK_OUTPUT].value = clockState ? 10.0f : 0.0;
  227. /* curious */
  228. for (int i = 0; i < 39; i++) lights[TRIADEX_LAMP + i].value = theTriadex.bitValue(i);
  229. }
  230. struct rectangularLEDWidget : ModuleLightWidget {
  231. rectangularLEDWidget() {
  232. bgColor = nvgRGB(0x10, 0x10, 0x10);
  233. borderColor = nvgRGBA(0x20, 0x20, 0x20, 0xc0);
  234. }
  235. void drawLight(NVGcontext *) override;
  236. };
  237. /* the (now) easy part, draw the LED as specified */
  238. void rectangularLEDWidget::drawLight(NVGcontext *vg) {
  239. nvgBeginPath(vg);
  240. nvgRect(vg, 0.0, 0.0, box.size.x, box.size.y);
  241. nvgFillColor(vg, bgColor);
  242. nvgFill(vg);
  243. nvgFillColor(vg, color);
  244. nvgFill(vg);
  245. nvgBeginPath(vg);
  246. nvgRect(vg, 0.25, 0.25, box.size.x - 0.5, box.size.y - 0.5);
  247. nvgStrokeColor(vg, borderColor);
  248. nvgStrokeWidth(vg, 0.5);
  249. nvgStroke(vg);
  250. }
  251. template <typename BASE>
  252. struct rectangularTriadexLight : BASE {
  253. rectangularTriadexLight() {
  254. this->box.size = mm2px(Vec(2.367, 1.463)); /* golden! */
  255. }
  256. };
  257. struct redRectangularLED : rectangularLEDWidget {
  258. redRectangularLED() {
  259. addBaseColor(nvgRGB(0xff, 0x0, 0x0));
  260. }
  261. };
  262. struct yellowRectangularLED : rectangularLEDWidget {
  263. yellowRectangularLED() {
  264. addBaseColor(nvgRGB(0xff, 0xff, 0x0));
  265. }
  266. };
  267. struct greenRectangularLED : rectangularLEDWidget {
  268. greenRectangularLED() {
  269. addBaseColor(nvgRGB(0x0, 0xff, 0x0));
  270. }
  271. };
  272. struct blueRectangularLED : rectangularLEDWidget {
  273. blueRectangularLED() {
  274. addBaseColor(nvgRGB(0x40, 0x40, 0xff));
  275. }
  276. };
  277. struct MyBSlidePot : SVGSlider {
  278. MyBSlidePot() {
  279. snap = true;
  280. maxHandlePos = mm2px(Vec(-0.25, 0.4)); /* was also -0.56 */
  281. // minHandlePos = mm2px(Vec(-0.56, 105.84));
  282. minHandlePos = mm2px(Vec(-0.25, 106.1)); /* just look */
  283. setSVGs(SVG::load(assetPlugin(plugin, "res/MyBSlidePot.svg")),
  284. SVG::load(assetPlugin(plugin, "res/MyBSlidePotHandle.svg")));
  285. }
  286. };
  287. struct HexCapScrew : SVGScrew {
  288. HexCapScrew() {
  289. sw->setSVG(SVG::load(assetPlugin(plugin, "res/HexCapScrew.svg")));
  290. box.size = sw->box.size;
  291. }
  292. };
  293. struct AmuseWidget : ModuleWidget {
  294. AmuseWidget(Amuse *module) : ModuleWidget(module) {
  295. setPanel(SVG::load(assetPlugin(plugin, "res/Amuse.svg")));
  296. /* screw this! */
  297. addChild(Widget::create<HexCapScrew>(Vec(0, 0)));
  298. addChild(Widget::create<HexCapScrew>(Vec(box.size.x - RACK_GRID_WIDTH, 0)));
  299. addChild(Widget::create<HexCapScrew>(Vec(0, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  300. addChild(Widget::create<HexCapScrew>(Vec(box.size.x - RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  301. /* clock section */
  302. addParam(ParamWidget::create<RoundBlackKnob>(mm2px(Vec(1.934, 8.521)), module, Amuse::RATE_PARAM, -2.0f, 3.0f, 0.5f));
  303. addInput(Port::create<PJ301MPort>(mm2px(Vec(2.756, 23.696)), Port::INPUT, module, Amuse::CLOCK_CV_INPUT));
  304. addInput(Port::create<PJ301MPort>(mm2px(Vec(17.47, 23.696)), Port::INPUT, module, Amuse::EXT_CLOCK_INPUT));
  305. /* output */
  306. addOutput(Port::create<PJ301MPort>(mm2px(Vec(2.756, 89.925 - 9.0)), Port::OUTPUT, module, Amuse::NOTE_OUTPUT));
  307. addOutput(Port::create<PJ301MPort>(mm2px(Vec(2.756, 102.103 - 4.5)), Port::OUTPUT, module, Amuse::TRIGGER_OUTPUT));
  308. addOutput(Port::create<PJ301MPort>(mm2px(Vec(2.756, 114.279 - 0.0)), Port::OUTPUT, module, Amuse::CLOCK_OUTPUT));
  309. /* 40 lamps for Triadex state */
  310. float lampX = 96.71 - 27.0;
  311. float lampY = 117.68 + 0.25;
  312. float bulbSize = 2.7053;
  313. for (int i = 0; i < 31; i++)
  314. addChild(ModuleLightWidget::create<rectangularTriadexLight<blueRectangularLED>>(mm2px(Vec(lampX, lampY - bulbSize * i)), module, Amuse::TRIADEX_LAMP + i));
  315. for (int i = 31; i < 33; i++)
  316. addChild(ModuleLightWidget::create<rectangularTriadexLight<yellowRectangularLED>>(mm2px(Vec(lampX, lampY - bulbSize * i)), module, Amuse::TRIADEX_LAMP + i));
  317. for (int i = 33; i < 38; i++)
  318. addChild(ModuleLightWidget::create<rectangularTriadexLight<greenRectangularLED>>(mm2px(Vec(lampX, lampY - bulbSize * i)), module, Amuse::TRIADEX_LAMP + i));
  319. for (int i = 38; i < 40; i++)
  320. addChild(ModuleLightWidget::create<rectangularTriadexLight<redRectangularLED>>(mm2px(Vec(lampX, lampY - bulbSize * i)), module, Amuse::TRIADEX_LAMP + i));
  321. /* 8 40-step slide faders for interval and theme selection */
  322. float sliderX = 110.0 - 17.246 - 27.0; float sliderSpace = 4.06; float tween = 4.06;
  323. for (int i = 0; i < 4; i++)
  324. addParam(ParamWidget::create<MyBSlidePot>(mm2px(Vec(sliderX - sliderSpace * i, 11.84)), module, Amuse::SLIDEPOT_PARAM + i, 0.0, 39.0, 39.0));
  325. for (int i = 4; i < 8; i++)
  326. addParam(ParamWidget::create<MyBSlidePot>(mm2px(Vec(sliderX - sliderSpace * i - tween, 11.84)), module, Amuse::SLIDEPOT_PARAM + i, 0.0, 39.0, 39.0));
  327. // addParam(ParamWidget::create<my2Switch>(mm2px(Vec(3.231, 48.263)), module, Amuse::RUN_HOLD_PARAM, 0.0, 1.0, 0.0));
  328. // addInput(Port::create<PJ301MPort>(mm2px(Vec(2.756, 59.888)), Port::INPUT, module, Amuse::RUN_HOLD_INPUT));
  329. addParam(ParamWidget::create<LEDBezel>(mm2px(Vec(3.184, 48.69)), module, Amuse::RUN_HOLD_PARAM, 0.0, 1.0, 0.0));
  330. addChild(ModuleLightWidget::create<bigLight<RedLight>>(mm2px(Vec(3.934, 49.44)), module, Amuse::RUN_HOLD_LAMP));
  331. addInput(Port::create<PJ301MPort>(mm2px(Vec(2.756, 59.888)), Port::INPUT, module, Amuse::RUN_HOLD_INPUT));
  332. addParam(ParamWidget::create<LEDBezel>(mm2px(Vec(17.898, 48.69)), module, Amuse::RESET_BUTTON_PARAM, 0.0, 1.0, 0.0));
  333. addChild(ModuleLightWidget::create<bigLight<RedLight>>(mm2px(Vec(18.648, 49.44)), module, Amuse::RESET_LAMP));
  334. addInput(Port::create<PJ301MPort>(mm2px(Vec(17.47, 59.888)), Port::INPUT, module, Amuse::RESET_INPUT));
  335. addParam(ParamWidget::create<LEDBezel>(mm2px(Vec(17.898, 9.772)), module, Amuse::STEP_PARAM, 0.0, 1.0, 0.0));
  336. addChild(ModuleLightWidget::create<bigLight<RedLight>>(mm2px(Vec(18.648, 10.522)), module, Amuse::STEP_LAMP));
  337. addParam(ParamWidget::create<myOther2Switch>(mm2px(Vec(19.796, 91.372 - 9.0)), module, Amuse::DELTA_ALL_PARAM, 0.0, 1.0, 0.0));
  338. addParam(ParamWidget::create<my3Switch>(mm2px(Vec(19.793, 102.374 - 4.5)), module, Amuse::TRIG_EDGES_PARAM, 0.0, 2.0, 1.0));
  339. addParam(ParamWidget::create<myOther2Switch>(mm2px(Vec(19.796, 115.727 - 0.0)), module, Amuse::REST_NORMAL_PARAM, 0.0, 1.0, 0.0));
  340. }
  341. };
  342. } // namespace rack_plugin_alto777_LFSR
  343. using namespace rack_plugin_alto777_LFSR;
  344. RACK_PLUGIN_MODEL_INIT(alto777_LFSR, Amuse) {
  345. Model *modelAmuse = Model::create<Amuse, AmuseWidget>("alto777_LFSR", "Amuse", "Amuse", SEQUENCER_TAG);
  346. return modelAmuse;
  347. }