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.

793 lines
28KB

  1. //
  2. // UGraph.cpp
  3. // Author: Dale Johnson
  4. // Contact: valley.audio.soft@gmail.com
  5. // Date: 5/12/2017
  6. //
  7. // UGraph, a port of "Mutable Instruments Grids" for VCV Rack
  8. // Original author: Olivier Gillet (ol.gillet@gmail.com)
  9. // https://github.com/pichenettes/eurorack/tree/master/grids
  10. // Copyright 2012 Olivier Gillet.
  11. //
  12. // This program is free software: you can redistribute it and/or modify
  13. // it under the terms of the GNU General Public License as published by
  14. // the Free Software Foundation, either version 3 of the License, or
  15. // (at your option) any later version.
  16. // This program is distributed in the hope that it will be useful,
  17. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. // GNU General Public License for more details.
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. #include "../Valley.hpp"
  23. #include "../ValleyComponents.hpp"
  24. #include "dsp/digital.hpp"
  25. #include "../Common/Metronome.hpp"
  26. #include "../Common/Oneshot.hpp"
  27. #include "../Topograph/TopographPatternGenerator.hpp"
  28. #include <iomanip> // setprecision
  29. #include <sstream> // stringstream
  30. namespace rack_plugin_Valley {
  31. struct UGraph : Module {
  32. enum ParamIds {
  33. RESET_BUTTON_PARAM,
  34. RUN_BUTTON_PARAM,
  35. TEMPO_PARAM,
  36. MAPX_PARAM,
  37. MAPY_PARAM,
  38. CHAOS_PARAM,
  39. BD_DENS_PARAM,
  40. SN_DENS_PARAM,
  41. HH_DENS_PARAM,
  42. SWING_PARAM,
  43. NUM_PARAMS
  44. };
  45. enum InputIds {
  46. CLOCK_INPUT,
  47. RESET_INPUT,
  48. MAPX_CV,
  49. MAPY_CV,
  50. CHAOS_CV,
  51. BD_FILL_CV,
  52. SN_FILL_CV,
  53. HH_FILL_CV,
  54. SWING_CV,
  55. RUN_INPUT,
  56. NUM_INPUTS
  57. };
  58. enum OutputIds {
  59. BD_OUTPUT,
  60. SN_OUTPUT,
  61. HH_OUTPUT,
  62. BD_ACC_OUTPUT,
  63. SN_ACC_OUTPUT,
  64. HH_ACC_OUTPUT,
  65. NUM_OUTPUTS
  66. };
  67. enum LightIds {
  68. RUNNING_LIGHT,
  69. RESET_LIGHT,
  70. BD_LIGHT,
  71. SN_LIGHT,
  72. HH_LIGHT,
  73. NUM_LIGHTS
  74. };
  75. Metronome metro;
  76. PatternGenerator grids;
  77. uint8_t numTicks;
  78. SchmittTrigger clockTrig;
  79. SchmittTrigger resetTrig;
  80. SchmittTrigger resetButtonTrig;
  81. SchmittTrigger runButtonTrig;
  82. SchmittTrigger runInputTrig;
  83. bool initExtReset = true;
  84. int running = 0;
  85. bool extClock = false;
  86. bool advStep = false;
  87. long seqStep = 0;
  88. float swing = 0.5;
  89. float swingHighTempo = 0.0;
  90. float swingLowTempo = 0.0;
  91. long elapsedTicks = 0;
  92. float tempoParam = 0.0;
  93. std::shared_ptr<float> tempo = std::make_shared<float>(120.0);
  94. std::shared_ptr<float> mapX = std::make_shared<float>(0.0);
  95. std::shared_ptr<float> mapY = std::make_shared<float>(0.0);
  96. std::shared_ptr<float> chaos = std::make_shared<float>(0.0);
  97. float BDFill = 0.0;
  98. float SNFill = 0.0;
  99. float HHFill = 0.0;
  100. uint8_t state = 0;
  101. // LED Triggers
  102. Oneshot drumLED[3];
  103. const LightIds drumLEDIds[3] = {BD_LIGHT, SN_LIGHT, HH_LIGHT};
  104. Oneshot BDLed;
  105. Oneshot SNLed;
  106. Oneshot HHLed;
  107. Oneshot resetLed;
  108. Oneshot runningLed;
  109. // Drum Triggers
  110. Oneshot drumTriggers[6];
  111. bool gateState[6];
  112. const OutputIds outIDs[6] = {BD_OUTPUT, SN_OUTPUT, HH_OUTPUT,
  113. BD_ACC_OUTPUT, SN_ACC_OUTPUT, HH_ACC_OUTPUT};
  114. enum SequencerMode {
  115. HENRI,
  116. OLIVIER,
  117. EUCLIDEAN
  118. };
  119. SequencerMode sequencerMode = OLIVIER;
  120. int inEuclideanMode = 0;
  121. unsigned long sequencerModeChoice = 0;
  122. unsigned long prevClockResChoice = 0;
  123. unsigned long clockResChoice = 0;
  124. enum TriggerOutputMode {
  125. PULSE,
  126. GATE
  127. };
  128. TriggerOutputMode triggerOutputMode = PULSE;
  129. enum AccOutputMode {
  130. INDIVIDUAL_ACCENTS,
  131. ACC_CLK_RST
  132. };
  133. AccOutputMode accOutputMode = INDIVIDUAL_ACCENTS;
  134. enum ExtClockResolution {
  135. EXTCLOCK_RES_4_PPQN,
  136. EXTCLOCK_RES_8_PPQN,
  137. EXTCLOCK_RES_24_PPQN,
  138. };
  139. ExtClockResolution extClockResolution = EXTCLOCK_RES_24_PPQN;
  140. enum ChaosKnobMode {
  141. CHAOS,
  142. SWING
  143. };
  144. ChaosKnobMode chaosKnobMode = CHAOS;
  145. enum RunMode {
  146. TOGGLE,
  147. MOMENTARY
  148. };
  149. RunMode runMode = TOGGLE;
  150. int panelStyle;
  151. std::string clockBPM;
  152. std::string mapXText = "Map X";
  153. std::string mapYText = "Map Y";
  154. std::string chaosText = "Chaos";
  155. int textVisible = 1;
  156. UGraph() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  157. metro = Metronome(120, engineGetSampleRate(), 24.0, 0.0);
  158. numTicks = ticks_granularity[2];
  159. srand(time(NULL));
  160. BDLed = Oneshot(0.1, engineGetSampleRate());
  161. SNLed = Oneshot(0.1, engineGetSampleRate());
  162. HHLed = Oneshot(0.1, engineGetSampleRate());
  163. resetLed = Oneshot(0.1, engineGetSampleRate());
  164. for(int i = 0; i < 6; ++i) {
  165. drumTriggers[i] = Oneshot(0.001, engineGetSampleRate());
  166. gateState[i] = false;
  167. }
  168. for(int i = 0; i < 3; ++i) {
  169. drumLED[i] = Oneshot(0.1, engineGetSampleRate());
  170. }
  171. panelStyle = 0;
  172. }
  173. json_t *toJson() override {
  174. json_t *rootJ = json_object();
  175. json_object_set_new(rootJ, "sequencerMode", json_integer(sequencerModeChoice));
  176. json_object_set_new(rootJ, "triggerOutputMode", json_integer(triggerOutputMode));
  177. json_object_set_new(rootJ, "accOutputMode", json_integer(accOutputMode));
  178. json_object_set_new(rootJ, "extClockResolution", json_integer(clockResChoice));
  179. json_object_set_new(rootJ, "chaosKnobMode", json_integer(chaosKnobMode));
  180. json_object_set_new(rootJ, "runMode", json_integer(runMode));
  181. json_object_set_new(rootJ, "panelStyle", json_integer(panelStyle));
  182. json_object_set_new(rootJ, "running", json_integer(running));
  183. return rootJ;
  184. }
  185. void fromJson(json_t *rootJ) override {
  186. json_t *sequencerModeJ = json_object_get(rootJ, "sequencerMode");
  187. if (sequencerModeJ) {
  188. sequencerModeChoice = json_integer_value(sequencerModeJ);
  189. }
  190. json_t *triggerOutputModeJ = json_object_get(rootJ, "triggerOutputMode");
  191. if (triggerOutputModeJ) {
  192. triggerOutputMode = (UGraph::TriggerOutputMode) json_integer_value(triggerOutputModeJ);
  193. }
  194. json_t *accOutputModeJ = json_object_get(rootJ, "accOutputMode");
  195. if (accOutputModeJ) {
  196. accOutputMode = (UGraph::AccOutputMode) json_integer_value(accOutputModeJ);
  197. switch(accOutputMode) {
  198. case INDIVIDUAL_ACCENTS:
  199. grids.setAccentAltMode(false);
  200. break;
  201. case ACC_CLK_RST:
  202. grids.setAccentAltMode(true);
  203. }
  204. }
  205. json_t *extClockResolutionJ = json_object_get(rootJ, "extClockResolution");
  206. if (extClockResolutionJ) {
  207. clockResChoice = json_integer_value(extClockResolutionJ);
  208. grids.reset();
  209. }
  210. json_t *chaosKnobModeJ = json_object_get(rootJ, "chaosKnobMode");
  211. if (chaosKnobModeJ) {
  212. chaosKnobMode = (UGraph::ChaosKnobMode) json_integer_value(chaosKnobModeJ);
  213. }
  214. json_t *runModeJ = json_object_get(rootJ, "runMode");
  215. if (runModeJ) {
  216. runMode = (UGraph::RunMode) json_integer_value(runModeJ);
  217. }
  218. json_t *panelStyleJ = json_object_get(rootJ, "panelStyle");
  219. if (panelStyleJ) {
  220. panelStyle = (int)json_integer_value(panelStyleJ);
  221. }
  222. json_t *runningJ = json_object_get(rootJ, "running");
  223. if (runningJ) {
  224. running = (int)json_integer_value(runningJ);
  225. }
  226. }
  227. void step() override;
  228. void onSampleRateChange() override;
  229. void updateUI();
  230. void updateOutputs();
  231. };
  232. void UGraph::step() {
  233. if(runMode == TOGGLE) {
  234. if (runButtonTrig.process(params[RUN_BUTTON_PARAM].value) ||
  235. runInputTrig.process(inputs[RUN_INPUT].value)) {
  236. if(runMode == TOGGLE){
  237. running = !running;
  238. }
  239. }
  240. }
  241. else {
  242. running = params[RUN_BUTTON_PARAM].value + inputs[RUN_INPUT].value;
  243. if(running == 0) {
  244. metro.reset();
  245. }
  246. }
  247. lights[RUNNING_LIGHT].value = running ? 1.0 : 0.0;
  248. if(resetButtonTrig.process(params[RESET_BUTTON_PARAM].value) ||
  249. resetTrig.process(inputs[RESET_INPUT].value)) {
  250. grids.reset();
  251. metro.reset();
  252. resetLed.trigger();
  253. seqStep = 0;
  254. elapsedTicks = 0;
  255. }
  256. switch(sequencerModeChoice) {
  257. case 0:
  258. grids.setPatternMode(PATTERN_OLIVIER);
  259. inEuclideanMode = 0;
  260. break;
  261. case 1:
  262. grids.setPatternMode(PATTERN_HENRI);
  263. inEuclideanMode = 0;
  264. break;
  265. case 2:
  266. grids.setPatternMode(PATTERN_EUCLIDEAN);
  267. inEuclideanMode = 1;
  268. break;
  269. }
  270. // Clock, tempo and swing
  271. tempoParam = params[TEMPO_PARAM].value;
  272. *tempo = rescale(tempoParam, 0.01f, 1.f, 40.f, 240.f);
  273. swing = clamp(params[SWING_PARAM].value + inputs[SWING_CV].value / 10.f, 0.f, 0.9f);
  274. swingHighTempo = *tempo / (1 - swing);
  275. swingLowTempo = *tempo / (1 + swing);
  276. if(elapsedTicks < 6) {
  277. metro.setTempo(swingLowTempo);
  278. }
  279. else {
  280. metro.setTempo(swingHighTempo);
  281. }
  282. if(clockResChoice != prevClockResChoice) {
  283. prevClockResChoice = clockResChoice;
  284. grids.reset();
  285. }
  286. // External clock select
  287. if(tempoParam < 0.01) {
  288. clockBPM = "Ext.";
  289. if(initExtReset) {
  290. grids.reset();
  291. initExtReset = false;
  292. }
  293. numTicks = ticks_granularity[clockResChoice];
  294. extClock = true;
  295. }
  296. else {
  297. initExtReset = true;
  298. numTicks = ticks_granularity[2];
  299. extClock = false;
  300. metro.process();
  301. }
  302. *mapX = params[MAPX_PARAM].value + (inputs[MAPX_CV].value / 10.f);
  303. *mapX = clamp(*mapX, 0.f, 1.f);
  304. *mapY = params[MAPY_PARAM].value + (inputs[MAPY_CV].value / 10.f);
  305. *mapY = clamp(*mapY, 0.f, 1.f);
  306. BDFill = params[BD_DENS_PARAM].value + (inputs[BD_FILL_CV].value / 10.f);
  307. BDFill = clamp(BDFill, 0.f, 1.f);
  308. SNFill = params[SN_DENS_PARAM].value + (inputs[SN_FILL_CV].value / 10.f);
  309. SNFill = clamp(SNFill, 0.f, 1.f);
  310. HHFill = params[HH_DENS_PARAM].value + (inputs[HH_FILL_CV].value / 10.f);
  311. HHFill = clamp(HHFill, 0.f, 1.f);
  312. *chaos = params[CHAOS_PARAM].value + (inputs[CHAOS_CV].value / 10.f);
  313. *chaos = clamp(*chaos, 0.f, 1.f);
  314. if(running) {
  315. if(extClock) {
  316. if(clockTrig.process(inputs[CLOCK_INPUT].value)) {
  317. advStep = true;
  318. }
  319. }
  320. else if(metro.hasTicked()){
  321. advStep = true;
  322. elapsedTicks++;
  323. elapsedTicks %= 12;
  324. }
  325. else {
  326. advStep = false;
  327. }
  328. grids.setMapX((uint8_t)(*mapX * 255.0));
  329. grids.setMapY((uint8_t)(*mapY * 255.0));
  330. grids.setBDDensity((uint8_t)(BDFill * 255.0));
  331. grids.setSDDensity((uint8_t)(SNFill * 255.0));
  332. grids.setHHDensity((uint8_t)(HHFill * 255.0));
  333. grids.setRandomness((uint8_t)(*chaos * 255.0));
  334. grids.setEuclideanLength(0, (uint8_t)(*mapX * 255.0));
  335. grids.setEuclideanLength(1, (uint8_t)(*mapY * 255.0));
  336. grids.setEuclideanLength(2, (uint8_t)(*chaos * 255.0));
  337. }
  338. if(advStep) {
  339. grids.tick(numTicks);
  340. for(int i = 0; i < 6; ++i) {
  341. if(grids.getDrumState(i)) {
  342. drumTriggers[i].trigger();
  343. gateState[i] = true;
  344. if(i < 3) {
  345. drumLED[i].trigger();
  346. }
  347. }
  348. }
  349. seqStep++;
  350. if(seqStep >= 32) {
  351. seqStep = 0;
  352. }
  353. advStep = false;
  354. }
  355. updateOutputs();
  356. updateUI();
  357. }
  358. void UGraph::updateUI() {
  359. resetLed.process();
  360. for(int i = 0; i < 3; ++i) {
  361. drumLED[i].process();
  362. if(drumLED[i].getState() == 1) {
  363. lights[drumLEDIds[i]].value = 1.0;
  364. }
  365. else {
  366. lights[drumLEDIds[i]].value = 0.0;
  367. }
  368. }
  369. if(resetLed.getState() == 1) {
  370. lights[RESET_LIGHT].value = 1.0;
  371. }
  372. else {
  373. lights[RESET_LIGHT].value = 0.0;
  374. }
  375. }
  376. void UGraph::updateOutputs() {
  377. if(triggerOutputMode == PULSE) {
  378. for(int i = 0; i < 6; ++i) {
  379. drumTriggers[i].process();
  380. if(drumTriggers[i].getState()) {
  381. outputs[outIDs[i]].value = 10;
  382. }
  383. else {
  384. outputs[outIDs[i]].value = 0;
  385. }
  386. }
  387. }
  388. else if(extClock && triggerOutputMode == GATE) {
  389. for(int i = 0; i < 6; ++i) {
  390. if(inputs[CLOCK_INPUT].value > 0 && gateState[i]) {
  391. gateState[i] = false;
  392. outputs[outIDs[i]].value = 10;
  393. }
  394. if(inputs[CLOCK_INPUT].value <= 0) {
  395. outputs[outIDs[i]].value = 0;
  396. }
  397. }
  398. }
  399. else {
  400. for(int i = 0; i < 6; ++i) {
  401. if(metro.getElapsedTickTime() < 0.5 && gateState[i]) {
  402. outputs[outIDs[i]].value = 10;
  403. }
  404. else {
  405. outputs[outIDs[i]].value = 0;
  406. gateState[i] = false;
  407. }
  408. }
  409. }
  410. }
  411. void UGraph::onSampleRateChange() {
  412. metro.setSampleRate(engineGetSampleRate());
  413. for(int i = 0; i < 3; ++i) {
  414. drumLED[i].setSampleRate(engineGetSampleRate());
  415. }
  416. resetLed.setSampleRate(engineGetSampleRate());
  417. for(int i = 0; i < 6; ++i) {
  418. drumTriggers[i].setSampleRate(engineGetSampleRate());
  419. }
  420. }
  421. // The widget
  422. struct PanelBorder : TransparentWidget {
  423. void draw(NVGcontext *vg) override {
  424. NVGcolor borderColor = nvgRGBAf(0.5, 0.5, 0.5, 0.5);
  425. nvgBeginPath(vg);
  426. nvgRect(vg, 0.5, 0.5, box.size.x - 1.0, box.size.y - 1.0);
  427. nvgStrokeColor(vg, borderColor);
  428. nvgStrokeWidth(vg, 1.0);
  429. nvgStroke(vg);
  430. }
  431. };
  432. struct UGraphDynamicText : TransparentWidget {
  433. std::string oldText;
  434. std::string* pText;
  435. std::shared_ptr<Font> font;
  436. int size;
  437. NVGcolor drawColour;
  438. int* visibility;
  439. DynamicViewMode viewMode;
  440. enum Colour {
  441. COLOUR_WHITE,
  442. COLOUR_BLACK
  443. };
  444. int* colourHandle;
  445. UGraphDynamicText() {
  446. font = Font::load(assetPlugin(plugin, "res/din1451alt.ttf"));
  447. size = 16;
  448. visibility = nullptr;
  449. pText = nullptr;
  450. viewMode = ACTIVE_HIGH_VIEW;
  451. }
  452. void draw(NVGcontext* vg) {
  453. nvgFontSize(vg, size);
  454. nvgFontFaceId(vg, font->handle);
  455. nvgTextLetterSpacing(vg, 0.f);
  456. Vec textPos = Vec(0.f, 0.f);
  457. if(colourHandle != nullptr) {
  458. switch(*colourHandle) {
  459. case COLOUR_BLACK : drawColour = nvgRGB(0x00,0x00,0x00); break;
  460. case COLOUR_WHITE : drawColour = nvgRGB(0xFF,0xFF,0xFF); break;
  461. default : drawColour = nvgRGB(0x00,0x00,0x00);
  462. }
  463. }
  464. else {
  465. drawColour = nvgRGB(0x00,0x00,0x00);
  466. }
  467. nvgFillColor(vg, drawColour);
  468. nvgTextAlign(vg, NVG_ALIGN_CENTER | NVG_ALIGN_TOP);
  469. if(pText != nullptr) {
  470. nvgText(vg, textPos.x, textPos.y, pText->c_str(), NULL);
  471. }
  472. }
  473. void step() {
  474. if(visibility != nullptr) {
  475. if(*visibility) {
  476. visible = true;
  477. }
  478. else {
  479. visible = false;
  480. }
  481. if(viewMode == ACTIVE_LOW_VIEW) {
  482. visible = !visible;
  483. }
  484. }
  485. else {
  486. visible = true;
  487. }
  488. }
  489. };
  490. UGraphDynamicText* createUGraphDynamicText(const Vec& pos, int size, int* colourHandle, std::string* pText,
  491. int* visibilityHandle, DynamicViewMode viewMode) {
  492. UGraphDynamicText* dynText = new UGraphDynamicText();
  493. dynText->size = size;
  494. dynText->colourHandle = colourHandle;
  495. dynText->pText = pText;
  496. dynText->box.pos = pos;
  497. dynText->box.size = Vec(82,14);
  498. dynText->visibility = visibilityHandle;
  499. dynText->viewMode = viewMode;
  500. return dynText;
  501. }
  502. ////////////////////////////////////////////////////////////////////////////////////////////////////
  503. /////////////////////////////////////////// Context Menu ///////////////////////////////////////////
  504. ////////////////////////////////////////////////////////////////////////////////////////////////////
  505. struct UGraphWidget : ModuleWidget {
  506. const std::string seqModeItemText[3] = {"Olivier", "Henri", "Euclid"};
  507. const std::string clockResText[3] = {"4 PPQN", "8 PPQN", "24 PPQN"};
  508. UGraphWidget(UGraph *module);
  509. void appendContextMenu(Menu* menu) override;
  510. };
  511. UGraphWidget::UGraphWidget(UGraph *module) : ModuleWidget(module){
  512. {
  513. DynamicPanelWidget *panel = new DynamicPanelWidget();
  514. panel->addPanel(SVG::load(assetPlugin(plugin, "res/UGraphPanel.svg")));
  515. panel->addPanel(SVG::load(assetPlugin(plugin, "res/UGraphPanelLight.svg")));
  516. box.size = panel->box.size;
  517. panel->mode = &module->panelStyle;
  518. addChild(panel);
  519. }
  520. addChild(Widget::create<ScrewBlack>(Vec(RACK_GRID_WIDTH, 0)));
  521. addChild(Widget::create<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  522. addChild(Widget::create<ScrewBlack>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  523. addChild(Widget::create<ScrewBlack>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  524. auto floatToTempoText = [](float a){
  525. std::stringstream stream;
  526. stream << std::fixed << std::setprecision(1) << a;
  527. if(a >= 40.0) {
  528. return stream.str();
  529. }
  530. std::string out = "Ext.";
  531. return out;
  532. };
  533. auto floatToEuclideanText = [](float a){
  534. return std::to_string(((uint8_t)(a * 255.0) >> 3) + 1);
  535. };
  536. // Tempo text
  537. {
  538. std::shared_ptr<float> i = module->tempo;
  539. DynamicValueText<float>* vText = new DynamicValueText<float>(i, floatToTempoText);
  540. vText->box.pos = Vec(53, 66.75);
  541. vText->size = 14;
  542. vText->viewMode = ACTIVE_HIGH_VIEW;
  543. vText->colorHandle = &module->panelStyle;
  544. addChild(vText);
  545. }
  546. // Map X Text
  547. {
  548. addChild(createDynamicText(Vec(53, 163), 14, "Map X", &module->inEuclideanMode,
  549. &module->panelStyle, ACTIVE_LOW_VIEW));
  550. std::shared_ptr<float> i = module->mapX;
  551. DynamicValueText<float>* vText = new DynamicValueText<float>(i, floatToEuclideanText);
  552. vText->box.pos = Vec(53, 163);
  553. vText->size = 14;
  554. vText->viewMode = ACTIVE_HIGH_VIEW;
  555. vText->visibility = &module->inEuclideanMode;
  556. vText->colorHandle = &module->panelStyle;
  557. addChild(vText);
  558. }
  559. // Map Y Text
  560. {
  561. addChild(createDynamicText(Vec(89, 163), 14, "Map Y", &module->inEuclideanMode,
  562. &module->panelStyle, ACTIVE_LOW_VIEW));
  563. std::shared_ptr<float> i = module->mapY;
  564. DynamicValueText<float>* vText = new DynamicValueText<float>(i, floatToEuclideanText);
  565. vText->box.pos = Vec(89, 163);
  566. vText->size = 14;
  567. vText->viewMode = ACTIVE_HIGH_VIEW;
  568. vText->visibility = &module->inEuclideanMode;
  569. vText->colorHandle = &module->panelStyle;
  570. addChild(vText);
  571. }
  572. // Chaos Text
  573. {
  574. addChild(createDynamicText(Vec(125, 163), 14, "Chaos", &module->inEuclideanMode,
  575. &module->panelStyle, ACTIVE_LOW_VIEW));
  576. std::shared_ptr<float> i = module->chaos;
  577. DynamicValueText<float>* vText = new DynamicValueText<float>(i, floatToEuclideanText);
  578. vText->box.pos = Vec(125, 163);
  579. vText->size = 14;
  580. vText->viewMode = ACTIVE_HIGH_VIEW;
  581. vText->visibility = &module->inEuclideanMode;
  582. vText->colorHandle = &module->panelStyle;
  583. addChild(vText);
  584. }
  585. addParam(ParamWidget::create<RoganMedBlue>(Vec(36.5, 30.15), module, UGraph::TEMPO_PARAM, 0.0, 1.0, 0.406));
  586. addParam(ParamWidget::create<RoganSmallWhite>(Vec(43.5, 137), module, UGraph::MAPX_PARAM, 0.0, 1.0, 0.0));
  587. addParam(ParamWidget::create<RoganSmallWhite>(Vec(79.5, 137), module, UGraph::MAPY_PARAM, 0.0, 1.0, 0.0));
  588. addParam(ParamWidget::create<RoganSmallWhite>(Vec(115.5, 137), module, UGraph::CHAOS_PARAM, 0.0, 1.0, 0.0));
  589. addParam(ParamWidget::create<RoganSmallBrightRed>(Vec(43.5, 217.65), module, UGraph::BD_DENS_PARAM, 0.0, 1.0, 0.5));
  590. addParam(ParamWidget::create<RoganSmallOrange>(Vec(79.5, 217.65), module, UGraph::SN_DENS_PARAM, 0.0, 1.0, 0.5));
  591. addParam(ParamWidget::create<RoganSmallYellow>(Vec(115.5, 217.65), module, UGraph::HH_DENS_PARAM, 0.0, 1.0, 0.5));
  592. addParam(ParamWidget::create<RoganMedWhite>(Vec(108.5, 30.15), module, UGraph::SWING_PARAM, 0.0, 0.9, 0.0));
  593. addInput(Port::create<PJ301MDarkSmall>(Vec(8.0, 35.5), Port::INPUT, module, UGraph::CLOCK_INPUT));
  594. addInput(Port::create<PJ301MDarkSmall>(Vec(8.0, 214), Port::INPUT, module, UGraph::RESET_INPUT));
  595. addInput(Port::create<PJ301MDarkSmall>(Vec(42.5, 186), Port::INPUT, module, UGraph::MAPX_CV));
  596. addInput(Port::create<PJ301MDarkSmall>(Vec(78.5, 186), Port::INPUT, module, UGraph::MAPY_CV));
  597. addInput(Port::create<PJ301MDarkSmall>(Vec(114.5, 186), Port::INPUT, module, UGraph::CHAOS_CV));
  598. addInput(Port::create<PJ301MDarkSmall>(Vec(42.5, 261.5), Port::INPUT, module, UGraph::BD_FILL_CV));
  599. addInput(Port::create<PJ301MDarkSmall>(Vec(78.5, 261.5), Port::INPUT, module, UGraph::SN_FILL_CV));
  600. addInput(Port::create<PJ301MDarkSmall>(Vec(114.5, 261.5), Port::INPUT, module, UGraph::HH_FILL_CV));
  601. addInput(Port::create<PJ301MDarkSmall>(Vec(78.5, 35.5), Port::INPUT, module, UGraph::SWING_CV));
  602. addInput(Port::create<PJ301MDarkSmall>(Vec(8.0, 133.35), Port::INPUT, module, UGraph::RUN_INPUT));
  603. addOutput(Port::create<PJ301MDarkSmallOut>(Vec(42.5, 299.736), Port::OUTPUT, module, UGraph::BD_OUTPUT));
  604. addOutput(Port::create<PJ301MDarkSmallOut>(Vec(78.5, 299.736), Port::OUTPUT, module, UGraph::SN_OUTPUT));
  605. addOutput(Port::create<PJ301MDarkSmallOut>(Vec(114.5, 299.736), Port::OUTPUT, module, UGraph::HH_OUTPUT));
  606. addOutput(Port::create<PJ301MDarkSmallOut>(Vec(42.5, 327.736), Port::OUTPUT, module, UGraph::BD_ACC_OUTPUT));
  607. addOutput(Port::create<PJ301MDarkSmallOut>(Vec(78.5, 327.736), Port::OUTPUT, module, UGraph::SN_ACC_OUTPUT));
  608. addOutput(Port::create<PJ301MDarkSmallOut>(Vec(114.5, 327.736), Port::OUTPUT, module, UGraph::HH_ACC_OUTPUT));
  609. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(50.1, 247), module, UGraph::BD_LIGHT));
  610. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(86.1, 247), module, UGraph::SN_LIGHT));
  611. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(122.1, 247), module, UGraph::HH_LIGHT));
  612. addParam(ParamWidget::create<LightLEDButton>(Vec(12.1, 189.6), module, UGraph::RESET_BUTTON_PARAM, 0.0, 1.0, 0.0));
  613. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(14.5, 192), module, UGraph::RESET_LIGHT));
  614. addParam(ParamWidget::create<LightLEDButton>(Vec(12.1, 107), module, UGraph::RUN_BUTTON_PARAM, 0.0, 1.0, 0.0));
  615. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(14.5, 109.5), module, UGraph::RUNNING_LIGHT));
  616. std::vector<std::string> seqModeItems(seqModeItemText, seqModeItemText + 3);
  617. addChild(createDynamicChoice(Vec(90, 88), 55.f, seqModeItems, &module->sequencerModeChoice, nullptr, ACTIVE_LOW_VIEW));
  618. std::vector<std::string> clockResItems(clockResText, clockResText + 3);
  619. addChild(createDynamicChoice(Vec(90, 111), 55.f, clockResItems, &module->clockResChoice, nullptr, ACTIVE_LOW_VIEW));
  620. }
  621. struct UGraphPanelStyleItem : MenuItem {
  622. UGraph* module;
  623. int panelStyle;
  624. void onAction(EventAction &e) override {
  625. module->panelStyle = panelStyle;
  626. }
  627. void step() override {
  628. rightText = (module->panelStyle == panelStyle) ? "✔" : "";
  629. MenuItem::step();
  630. }
  631. };
  632. struct UGraphTriggerOutputModeItem : MenuItem {
  633. UGraph* module;
  634. UGraph::TriggerOutputMode triggerOutputMode;
  635. void onAction(EventAction &e) override {
  636. module->triggerOutputMode = triggerOutputMode;
  637. }
  638. void step() override {
  639. rightText = (module->triggerOutputMode == triggerOutputMode) ? "✔" : "";
  640. MenuItem::step();
  641. }
  642. };
  643. struct UGraphAccOutputModeItem : MenuItem {
  644. UGraph* module;
  645. UGraph::AccOutputMode accOutputMode;
  646. void onAction(EventAction &e) override {
  647. module->accOutputMode = accOutputMode;
  648. switch(accOutputMode) {
  649. case UGraph::INDIVIDUAL_ACCENTS:
  650. module->grids.setAccentAltMode(false);
  651. break;
  652. case UGraph::ACC_CLK_RST:
  653. module->grids.setAccentAltMode(true);
  654. }
  655. }
  656. void step() override {
  657. rightText = (module->accOutputMode == accOutputMode) ? "✔" : "";
  658. MenuItem::step();
  659. }
  660. };
  661. struct UGraphRunModeItem : MenuItem {
  662. UGraph* module;
  663. UGraph::RunMode runMode;
  664. void onAction(EventAction &e) override {
  665. module->runMode = runMode;
  666. }
  667. void step() override {
  668. rightText = (module->runMode == runMode) ? "✔" : "";
  669. MenuItem::step();
  670. }
  671. };
  672. void UGraphWidget::appendContextMenu(Menu *menu) {
  673. UGraph *module = dynamic_cast<UGraph*>(this->module);
  674. assert(module);
  675. // Panel style
  676. menu->addChild(construct<MenuLabel>());
  677. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Panel style"));
  678. menu->addChild(construct<UGraphPanelStyleItem>(&MenuItem::text, "Dark", &UGraphPanelStyleItem::module,
  679. module, &UGraphPanelStyleItem::panelStyle, 0));
  680. menu->addChild(construct<UGraphPanelStyleItem>(&MenuItem::text, "Light", &UGraphPanelStyleItem::module,
  681. module, &UGraphPanelStyleItem::panelStyle, 1));
  682. // Trigger Output Modes
  683. menu->addChild(construct<MenuLabel>());
  684. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Trigger Output Mode"));
  685. menu->addChild(construct<UGraphTriggerOutputModeItem>(&MenuItem::text, "1ms Pulse", &UGraphTriggerOutputModeItem::module,
  686. module, &UGraphTriggerOutputModeItem::triggerOutputMode, UGraph::PULSE));
  687. menu->addChild(construct<UGraphTriggerOutputModeItem>(&MenuItem::text, "Gate", &UGraphTriggerOutputModeItem::module,
  688. module, &UGraphTriggerOutputModeItem::triggerOutputMode, UGraph::GATE));
  689. // Acc Output Modes
  690. menu->addChild(construct<MenuLabel>());
  691. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Accent Output Mode"));
  692. menu->addChild(construct<UGraphAccOutputModeItem>(&MenuItem::text, "Individual accents", &UGraphAccOutputModeItem::module,
  693. module, &UGraphAccOutputModeItem::accOutputMode, UGraph::INDIVIDUAL_ACCENTS));
  694. menu->addChild(construct<UGraphAccOutputModeItem>(&MenuItem::text, "Accent / Clock / Reset", &UGraphAccOutputModeItem::module,
  695. module, &UGraphAccOutputModeItem::accOutputMode, UGraph::ACC_CLK_RST));
  696. // Run Mode
  697. menu->addChild(construct<MenuLabel>());
  698. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Run Mode"));
  699. menu->addChild(construct<UGraphRunModeItem>(&MenuItem::text, "Toggle", &UGraphRunModeItem::module,
  700. module, &UGraphRunModeItem::runMode, UGraph::RunMode::TOGGLE));
  701. menu->addChild(construct<UGraphRunModeItem>(&MenuItem::text, "Momentary", &UGraphRunModeItem::module,
  702. module, &UGraphRunModeItem::runMode, UGraph::RunMode::MOMENTARY));
  703. }
  704. } // namespace rack_plugin_Valley
  705. using namespace rack_plugin_Valley;
  706. RACK_PLUGIN_MODEL_INIT(Valley, UGraph) {
  707. Model *modelUGraph = Model::create<UGraph, UGraphWidget>(TOSTRING(SLUG), "uGraph", "uGraph", SEQUENCER_TAG);
  708. return modelUGraph;
  709. }