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.

625 lines
23KB

  1. #include <string.h>
  2. #include "FrozenWasteland.hpp"
  3. #include "dsp/digital.hpp"
  4. #define NUM_RULERS 10
  5. #define MAX_DIVISIONS 6
  6. #define TRACK_COUNT 4
  7. #define MAX_STEPS 18
  8. #define sMIN(a,b) (((a)>(b))?(b):(a))
  9. #define sMAX(a,b) (((a)>(b))?(a):(b))
  10. namespace rack_plugin_FrozenWasteland {
  11. struct QuadEuclideanRhythm : Module {
  12. enum ParamIds {
  13. STEPS_1_PARAM,
  14. DIVISIONS_1_PARAM,
  15. OFFSET_1_PARAM,
  16. PAD_1_PARAM,
  17. ACCENTS_1_PARAM,
  18. ACCENT_ROTATE_1_PARAM,
  19. STEPS_2_PARAM,
  20. DIVISIONS_2_PARAM,
  21. OFFSET_2_PARAM,
  22. PAD_2_PARAM,
  23. ACCENTS_2_PARAM,
  24. ACCENT_ROTATE_2_PARAM,
  25. STEPS_3_PARAM,
  26. DIVISIONS_3_PARAM,
  27. OFFSET_3_PARAM,
  28. PAD_3_PARAM,
  29. ACCENTS_3_PARAM,
  30. ACCENT_ROTATE_3_PARAM,
  31. STEPS_4_PARAM,
  32. DIVISIONS_4_PARAM,
  33. OFFSET_4_PARAM,
  34. PAD_4_PARAM,
  35. ACCENTS_4_PARAM,
  36. ACCENT_ROTATE_4_PARAM,
  37. CHAIN_MODE_PARAM,
  38. CONSTANT_TIME_MODE_PARAM,
  39. NUM_PARAMS
  40. };
  41. enum InputIds {
  42. STEPS_1_INPUT,
  43. DIVISIONS_1_INPUT,
  44. OFFSET_1_INPUT,
  45. PAD_1_INPUT,
  46. ACCENTS_1_INPUT,
  47. ACCENT_ROTATE_1_INPUT,
  48. START_1_INPUT,
  49. STEPS_2_INPUT,
  50. DIVISIONS_2_INPUT,
  51. OFFSET_2_INPUT,
  52. PAD_2_INPUT,
  53. ACCENTS_2_INPUT,
  54. ACCENT_ROTATE_2_INPUT,
  55. START_2_INPUT,
  56. STEPS_3_INPUT,
  57. DIVISIONS_3_INPUT,
  58. OFFSET_3_INPUT,
  59. PAD_3_INPUT,
  60. ACCENTS_3_INPUT,
  61. ACCENT_ROTATE_3_INPUT,
  62. START_3_INPUT,
  63. STEPS_4_INPUT,
  64. DIVISIONS_4_INPUT,
  65. OFFSET_4_INPUT,
  66. PAD_4_INPUT,
  67. ACCENTS_4_INPUT,
  68. ACCENT_ROTATE_4_INPUT,
  69. START_4_INPUT,
  70. CLOCK_INPUT,
  71. RESET_INPUT,
  72. MUTE_INPUT,
  73. NUM_INPUTS
  74. };
  75. enum OutputIds {
  76. OUTPUT_1,
  77. ACCENT_OUTPUT_1,
  78. EOC_OUTPUT_1,
  79. OUTPUT_2,
  80. ACCENT_OUTPUT_2,
  81. EOC_OUTPUT_2,
  82. OUTPUT_3,
  83. ACCENT_OUTPUT_3,
  84. EOC_OUTPUT_3,
  85. OUTPUT_4,
  86. ACCENT_OUTPUT_4,
  87. EOC_OUTPUT_4,
  88. NUM_OUTPUTS
  89. };
  90. enum LightIds {
  91. CHAIN_MODE_NONE_LIGHT,
  92. CHAIN_MODE_BOSS_LIGHT,
  93. CHAIN_MODE_EMPLOYEE_LIGHT,
  94. MUTED_LIGHT,
  95. CONSTANT_TIME_LIGHT,
  96. NUM_LIGHTS
  97. };
  98. enum ChainModes {
  99. CHAIN_MODE_NONE,
  100. CHAIN_MODE_BOSS,
  101. CHAIN_MODE_EMPLOYEE
  102. };
  103. bool beatMatrix[TRACK_COUNT][MAX_STEPS];
  104. bool accentMatrix[TRACK_COUNT][MAX_STEPS];
  105. int beatIndex[TRACK_COUNT];
  106. int stepsCount[TRACK_COUNT];
  107. float stepDuration[TRACK_COUNT];
  108. float lastStepTime[TRACK_COUNT];
  109. float maxStepCount;
  110. bool running[TRACK_COUNT];
  111. int chainMode;
  112. bool initialized = false;
  113. bool muted = false;
  114. bool constantTime = false;
  115. float time = 0.0;
  116. float duration = 0.0;
  117. bool secondClockReceived = false;
  118. SchmittTrigger clockTrigger,resetTrigger,chainModeTrigger,constantTimeTrigger,muteTrigger,startTrigger[TRACK_COUNT];
  119. PulseGenerator beatPulse[TRACK_COUNT],accentPulse[TRACK_COUNT],eocPulse[TRACK_COUNT];
  120. QuadEuclideanRhythm() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS,NUM_LIGHTS) {
  121. for(unsigned i = 0; i < TRACK_COUNT; i++) {
  122. beatIndex[i] = 0;
  123. stepsCount[i] = MAX_STEPS;
  124. lastStepTime[i] = 0.0;
  125. stepDuration[i] = 0.0;
  126. running[i] = true;
  127. for(unsigned j = 0; j < MAX_STEPS; j++) {
  128. beatMatrix[i][j] = false;
  129. accentMatrix[i][j] = false;
  130. }
  131. }
  132. }
  133. void step() override;
  134. json_t *toJson() override {
  135. json_t *rootJ = json_object();
  136. json_object_set_new(rootJ, "constantTime", json_integer((bool) constantTime));
  137. json_object_set_new(rootJ, "chainMode", json_integer((int) chainMode));
  138. json_object_set_new(rootJ, "muted", json_integer((bool) muted));
  139. return rootJ;
  140. }
  141. void fromJson(json_t *rootJ) override {
  142. json_t *ctJ = json_object_get(rootJ, "constantTime");
  143. if (ctJ)
  144. constantTime = json_integer_value(ctJ);
  145. json_t *cmJ = json_object_get(rootJ, "chainMode");
  146. if (cmJ)
  147. chainMode = json_integer_value(cmJ);
  148. json_t *mutedJ = json_object_get(rootJ, "muted");
  149. if (mutedJ)
  150. muted = json_integer_value(mutedJ);
  151. }
  152. void setRunningState() {
  153. for(int trackNumber=0;trackNumber<4;trackNumber++)
  154. {
  155. if(chainMode == CHAIN_MODE_EMPLOYEE && inputs[(trackNumber * 7) + 6].active) { //START Input needs to be active
  156. running[trackNumber] = false;
  157. }
  158. else {
  159. running[trackNumber] = true;
  160. }
  161. }
  162. }
  163. void advanceBeat(int trackNumber) {
  164. beatIndex[trackNumber]++;
  165. lastStepTime[trackNumber] = 0.0;
  166. //End of Cycle
  167. if(beatIndex[trackNumber] >= stepsCount[trackNumber]) {
  168. beatIndex[trackNumber] = 0;
  169. eocPulse[trackNumber].trigger(1e-3);
  170. //If in a chain mode, stop running until start trigger received
  171. if(chainMode != CHAIN_MODE_NONE && inputs[(trackNumber * 7) + 6].active) { //START Input needs to be active
  172. running[trackNumber] = false;
  173. }
  174. }
  175. }
  176. // For more advanced Module features, read Rack's engine.hpp header file
  177. // - onSampleRateChange: event triggered by a change of sample rate
  178. // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
  179. };
  180. void QuadEuclideanRhythm::step() {
  181. int levelArray[16];
  182. int accentLevelArray[16];
  183. int beatLocation[16];
  184. //Set startup state
  185. if(!initialized) {
  186. setRunningState();
  187. initialized = true;
  188. }
  189. // Modes
  190. if (constantTimeTrigger.process(params[CONSTANT_TIME_MODE_PARAM].value)) {
  191. constantTime = !constantTime;
  192. for(int trackNumber=0;trackNumber<4;trackNumber++) {
  193. beatIndex[trackNumber] = 0;
  194. }
  195. setRunningState();
  196. }
  197. lights[CONSTANT_TIME_LIGHT].value = constantTime;
  198. if (chainModeTrigger.process(params[CHAIN_MODE_PARAM].value)) {
  199. chainMode = (chainMode + 1) % 3;
  200. setRunningState();
  201. }
  202. lights[CHAIN_MODE_NONE_LIGHT].value = chainMode == CHAIN_MODE_NONE ? 1.0 : 0.0;
  203. lights[CHAIN_MODE_BOSS_LIGHT].value = chainMode == CHAIN_MODE_BOSS ? 1.0 : 0.0;
  204. lights[CHAIN_MODE_EMPLOYEE_LIGHT].value = chainMode == CHAIN_MODE_EMPLOYEE ? 1.0 : 0.0;
  205. lights[MUTED_LIGHT].value = muted ? 1.0 : 0.0;
  206. maxStepCount = 0;
  207. for(int trackNumber=0;trackNumber<4;trackNumber++) {
  208. //clear out the matrix and levels
  209. for(int j=0;j<16;j++)
  210. {
  211. beatMatrix[trackNumber][j] = false;
  212. accentMatrix[trackNumber][j] = false;
  213. levelArray[j] = 0;
  214. accentLevelArray[j] = 0;
  215. beatLocation[j] = 0;
  216. }
  217. float stepsCountf = params[trackNumber * 6].value;
  218. if(inputs[trackNumber * 7].active) {
  219. stepsCountf += inputs[trackNumber * 7].value;
  220. }
  221. stepsCountf = clamp(stepsCountf,0.0f,16.0f);
  222. float divisionf = params[(trackNumber * 6) + 1].value;
  223. if(inputs[(trackNumber * 7) + 1].active) {
  224. divisionf += inputs[(trackNumber * 7) + 1].value;
  225. }
  226. divisionf = clamp(divisionf,0.0f,stepsCountf);
  227. float offsetf = params[(trackNumber * 6) + 2].value;
  228. if(inputs[(trackNumber * 7) + 2].active) {
  229. offsetf += inputs[(trackNumber * 7) + 2].value;
  230. }
  231. offsetf = clamp(offsetf,0.0f,15.0f);
  232. float padf = params[trackNumber * 6 + 3].value;
  233. if(inputs[trackNumber * 7 + 3].active) {
  234. padf += inputs[trackNumber * 7 + 3].value;
  235. }
  236. padf = clamp(padf,0.0f,stepsCountf - divisionf);
  237. //Use this to reduce range of accent params/inputs so the range of motion of knob/modulation is more useful.
  238. float divisionScale = 1;
  239. if(stepsCountf > 0) {
  240. divisionScale = divisionf / stepsCountf;
  241. }
  242. float accentDivisionf = params[(trackNumber * 6) + 4].value * divisionScale;
  243. if(inputs[(trackNumber * 7) + 4].active) {
  244. accentDivisionf += inputs[(trackNumber * 7) + 4].value * divisionScale;
  245. }
  246. accentDivisionf = clamp(accentDivisionf,0.0f,divisionf);
  247. float accentRotationf = params[(trackNumber * 6) + 5].value * divisionScale;
  248. if(inputs[(trackNumber * 7) + 5].active) {
  249. accentRotationf += inputs[(trackNumber * 7) + 5].value * divisionScale;
  250. }
  251. if(divisionf > 0) {
  252. accentRotationf = clamp(accentRotationf,0.0f,divisionf-1);
  253. } else {
  254. accentRotationf = 0;
  255. }
  256. if(stepsCountf > maxStepCount)
  257. maxStepCount = stepsCountf;
  258. stepsCount[trackNumber] = int(stepsCountf);
  259. int division = int(divisionf);
  260. int offset = int(offsetf);
  261. int pad = int(padf);
  262. int accentDivision = int(accentDivisionf);
  263. int accentRotation = int(accentRotationf);
  264. if(stepsCount[trackNumber] > 0) {
  265. //Calculate Beats
  266. int level = 0;
  267. int restsLeft = sMAX(0,stepsCount[trackNumber]-division-pad); // just make sure no negatives
  268. do {
  269. levelArray[level] = sMIN(restsLeft,division);
  270. restsLeft = restsLeft - division;
  271. level += 1;
  272. } while (restsLeft > 0 && level < 16);
  273. int tempIndex =pad;
  274. int beatIndex = 0;
  275. for (int j = 0; j < division; j++) {
  276. beatMatrix[trackNumber][(tempIndex + offset) % stepsCount[trackNumber]] = true;
  277. beatLocation[beatIndex] = (tempIndex + offset) % stepsCount[trackNumber];
  278. tempIndex++;
  279. beatIndex++;
  280. for (int k = 0; k < 16; k++) {
  281. if (levelArray[k] > j) {
  282. tempIndex++;
  283. }
  284. }
  285. }
  286. //Calculate Accents
  287. level = 0;
  288. restsLeft = sMAX(0,division-accentDivision); // just make sure no negatives
  289. do {
  290. accentLevelArray[level] = sMIN(restsLeft,accentDivision);
  291. restsLeft = restsLeft - accentDivision;
  292. level += 1;
  293. } while (restsLeft > 0 && level < 16);
  294. tempIndex =0;
  295. for (int j = 0; j < accentDivision; j++) {
  296. accentMatrix[trackNumber][beatLocation[(tempIndex + accentRotation) % division]] = true;
  297. tempIndex++;
  298. for (int k = 0; k < 16; k++) {
  299. if (accentLevelArray[k] > j) {
  300. tempIndex++;
  301. }
  302. }
  303. }
  304. }
  305. }
  306. if(inputs[RESET_INPUT].active) {
  307. if(resetTrigger.process(inputs[RESET_INPUT].value)) {
  308. for(int trackNumber=0;trackNumber<4;trackNumber++)
  309. {
  310. beatIndex[trackNumber] = 0;
  311. }
  312. setRunningState();
  313. }
  314. }
  315. if(inputs[MUTE_INPUT].active) {
  316. if(muteTrigger.process(inputs[MUTE_INPUT].value)) {
  317. muted = !muted;
  318. }
  319. }
  320. //See if need to start up
  321. for(int trackNumber=0;trackNumber < 4;trackNumber++) {
  322. if(chainMode != CHAIN_MODE_NONE && inputs[(trackNumber * 7) + 6].active && !running[trackNumber]) {
  323. if(startTrigger[trackNumber].process(inputs[(trackNumber * 7) + 6].value)) {
  324. running[trackNumber] = true;
  325. }
  326. }
  327. }
  328. //Advance beat on trigger if not in constant time mode
  329. float timeAdvance =1.0 / engineGetSampleRate();
  330. time += timeAdvance;
  331. if(inputs[CLOCK_INPUT].active) {
  332. if(clockTrigger.process(inputs[CLOCK_INPUT].value)) {
  333. if(secondClockReceived) {
  334. duration = time;
  335. }
  336. time = 0;
  337. secondClockReceived = true;
  338. for(int trackNumber=0;trackNumber < TRACK_COUNT;trackNumber++)
  339. {
  340. if(running[trackNumber]) {
  341. if(!constantTime) {
  342. advanceBeat(trackNumber);
  343. }
  344. }
  345. }
  346. }
  347. bool resyncAll = false;
  348. //For constant time, don't rely on clock trigger to advance beat, use time
  349. for(int trackNumber=0;trackNumber < TRACK_COUNT;trackNumber++) {
  350. if(stepsCount[trackNumber] > 0)
  351. stepDuration[trackNumber] = duration * maxStepCount / (float)stepsCount[trackNumber];
  352. if(running[trackNumber]) {
  353. lastStepTime[trackNumber] +=timeAdvance;
  354. if(constantTime && stepDuration[trackNumber] > 0.0 && lastStepTime[trackNumber] >= stepDuration[trackNumber]) {
  355. advanceBeat(trackNumber);
  356. if(stepsCount[trackNumber] >= (int)maxStepCount && beatIndex[trackNumber] == 0) {
  357. resyncAll = true;
  358. }
  359. }
  360. }
  361. }
  362. if(resyncAll) {
  363. for(int trackNumber=0;trackNumber < TRACK_COUNT;trackNumber++) {
  364. lastStepTime[trackNumber] = 0;
  365. beatIndex[trackNumber] = 0;
  366. }
  367. }
  368. }
  369. // Set output to current state
  370. for(int trackNumber=0;trackNumber<TRACK_COUNT;trackNumber++) {
  371. float outputValue = (lastStepTime[trackNumber] < stepDuration[trackNumber] / 2) ? 10.0 : 0.0;
  372. //Send out beat
  373. if(beatMatrix[trackNumber][beatIndex[trackNumber]] == true && running[trackNumber] && !muted) {
  374. outputs[trackNumber * 3].value = outputValue;
  375. } else {
  376. outputs[trackNumber * 3].value = 0.0;
  377. }
  378. //send out accent
  379. if(accentMatrix[trackNumber][beatIndex[trackNumber]] == true && running[trackNumber] && !muted) {
  380. outputs[trackNumber * 3 + 1].value = outputValue;
  381. } else {
  382. outputs[trackNumber * 3 + 1].value = 0.0;
  383. }
  384. //Send out End of Cycle
  385. outputs[(trackNumber * 3) + 2].value = eocPulse[trackNumber].process(1.0 / engineGetSampleRate()) ? 10.0 : 0;
  386. }
  387. }
  388. struct QERBeatDisplay : TransparentWidget {
  389. QuadEuclideanRhythm *module;
  390. int frame = 0;
  391. std::shared_ptr<Font> font;
  392. QERBeatDisplay() {
  393. font = Font::load(assetPlugin(plugin, "res/fonts/Sudo.ttf"));
  394. }
  395. void drawBox(NVGcontext *vg, float stepNumber, float trackNumber,bool isBeat,bool isAccent,bool isCurrent) {
  396. //nvgSave(vg);
  397. //Rect b = Rect(Vec(0, 0), box.size);
  398. //nvgScissor(vg, b.pos.x, b.pos.y, b.size.x, b.size.y);
  399. nvgBeginPath(vg);
  400. float boxX = stepNumber * 22.5;
  401. float boxY = trackNumber * 22.5;
  402. float opacity = 0x80; // Testing using opacity for accents
  403. if(isAccent) {
  404. opacity = 0xff;
  405. }
  406. NVGcolor strokeColor = nvgRGBA(0xef, 0xe0, 0, 0xff);
  407. NVGcolor fillColor = nvgRGBA(0xef,0xe0,0,opacity);
  408. if(isCurrent)
  409. {
  410. strokeColor = nvgRGBA(0x2f, 0xf0, 0, 0xff);
  411. fillColor = nvgRGBA(0x2f,0xf0,0,opacity);
  412. }
  413. nvgStrokeColor(vg, strokeColor);
  414. nvgStrokeWidth(vg, 1.0);
  415. nvgRect(vg,boxX,boxY,21,21.0);
  416. if(isBeat) {
  417. nvgFillColor(vg, fillColor);
  418. nvgFill(vg);
  419. }
  420. nvgStroke(vg);
  421. }
  422. void draw(NVGcontext *vg) override {
  423. for(int trackNumber = 0;trackNumber < 4;trackNumber++) {
  424. for(int stepNumber = 0;stepNumber < module->stepsCount[trackNumber];stepNumber++) {
  425. bool isBeat = module->beatMatrix[trackNumber][stepNumber];
  426. bool isAccent = module->accentMatrix[trackNumber][stepNumber];
  427. bool isCurrent = module->beatIndex[trackNumber] == stepNumber && module->running[trackNumber];
  428. drawBox(vg, float(stepNumber), float(trackNumber),isBeat,isAccent,isCurrent);
  429. }
  430. }
  431. }
  432. };
  433. struct QuadEuclideanRhythmWidget : ModuleWidget {
  434. QuadEuclideanRhythmWidget(QuadEuclideanRhythm *module);
  435. };
  436. QuadEuclideanRhythmWidget::QuadEuclideanRhythmWidget(QuadEuclideanRhythm *module) : ModuleWidget(module) {
  437. box.size = Vec(15*26, RACK_GRID_HEIGHT);
  438. {
  439. SVGPanel *panel = new SVGPanel();
  440. panel->box.size = box.size;
  441. panel->setBackground(SVG::load(assetPlugin(plugin, "res/QuadEuclideanRhythm.svg")));
  442. addChild(panel);
  443. }
  444. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, 0)));
  445. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0)));
  446. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  447. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  448. {
  449. QERBeatDisplay *display = new QERBeatDisplay();
  450. display->module = module;
  451. display->box.pos = Vec(16, 34);
  452. display->box.size = Vec(box.size.x-30, 135);
  453. addChild(display);
  454. }
  455. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(22, 138), module, QuadEuclideanRhythm::STEPS_1_PARAM, 0.0, 16.2, 16.0));
  456. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(61, 138), module, QuadEuclideanRhythm::DIVISIONS_1_PARAM, 1.0, 16.2, 2.0));
  457. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(100, 138), module, QuadEuclideanRhythm::OFFSET_1_PARAM, 0.0, 15.2, 0.0));
  458. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(139, 138), module, QuadEuclideanRhythm::PAD_1_PARAM, 0.0, 15.2, 0.0));
  459. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(178, 138), module, QuadEuclideanRhythm::ACCENTS_1_PARAM, 0.0, 15.2, 0.0));
  460. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(217, 138), module, QuadEuclideanRhythm::ACCENT_ROTATE_1_PARAM, 0.0, 15.2, 0.0));
  461. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(22, 195), module, QuadEuclideanRhythm::STEPS_2_PARAM, 0.0, 16.0, 16.0));
  462. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(61, 195), module, QuadEuclideanRhythm::DIVISIONS_2_PARAM, 1.0, 16.2, 2.0));
  463. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(100, 195), module, QuadEuclideanRhythm::OFFSET_2_PARAM, 0.0, 15.2, 0.0));
  464. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(139, 195), module, QuadEuclideanRhythm::PAD_2_PARAM, 0.0, 15.2, 0.0));
  465. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(178, 195), module, QuadEuclideanRhythm::ACCENTS_2_PARAM, 0.0, 15.2, 0.0));
  466. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(217, 195), module, QuadEuclideanRhythm::ACCENT_ROTATE_2_PARAM, 0.0, 15.2, 0.0));
  467. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(22, 252), module, QuadEuclideanRhythm::STEPS_3_PARAM, 0.0, 16.2, 16.0));
  468. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(61, 252), module, QuadEuclideanRhythm::DIVISIONS_3_PARAM, 1.0, 16.2, 2.0));
  469. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(100, 252), module, QuadEuclideanRhythm::OFFSET_3_PARAM, 0.0, 15.2, 0.0));
  470. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(139, 252), module, QuadEuclideanRhythm::PAD_3_PARAM, 0.0, 15.2, 0.0));
  471. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(178, 252), module, QuadEuclideanRhythm::ACCENTS_3_PARAM, 0.0, 15.2, 0.0));
  472. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(217, 252), module, QuadEuclideanRhythm::ACCENT_ROTATE_3_PARAM, 0.0, 15.2, 0.0));
  473. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(22, 309), module, QuadEuclideanRhythm::STEPS_4_PARAM, 0.0, 16.2, 16.0));
  474. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(61, 309), module, QuadEuclideanRhythm::DIVISIONS_4_PARAM, 1.0, 16.2, 2.0));
  475. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(100, 309), module, QuadEuclideanRhythm::OFFSET_4_PARAM, 0.0, 15.2, 0.0));
  476. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(139, 309), module, QuadEuclideanRhythm::PAD_4_PARAM, 0.0, 15.2, 0.0));
  477. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(178, 309), module, QuadEuclideanRhythm::ACCENTS_4_PARAM, 0.0, 15.2, 0.0));
  478. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(217, 309), module, QuadEuclideanRhythm::ACCENT_ROTATE_4_PARAM, 0.0, 15.2, 0.0));
  479. addParam(ParamWidget::create<CKD6>(Vec(250, 285), module, QuadEuclideanRhythm::CHAIN_MODE_PARAM, 0.0, 1.0, 0.0));
  480. addParam(ParamWidget::create<CKD6>(Vec(335, 285), module, QuadEuclideanRhythm::CONSTANT_TIME_MODE_PARAM, 0.0, 1.0, 0.0));
  481. addInput(Port::create<PJ301MPort>(Vec(23, 163), Port::INPUT, module, QuadEuclideanRhythm::STEPS_1_INPUT));
  482. addInput(Port::create<PJ301MPort>(Vec(62, 163), Port::INPUT, module, QuadEuclideanRhythm::DIVISIONS_1_INPUT));
  483. addInput(Port::create<PJ301MPort>(Vec(101, 163), Port::INPUT, module, QuadEuclideanRhythm::OFFSET_1_INPUT));
  484. addInput(Port::create<PJ301MPort>(Vec(140, 163), Port::INPUT, module, QuadEuclideanRhythm::PAD_1_INPUT));
  485. addInput(Port::create<PJ301MPort>(Vec(179, 163), Port::INPUT, module, QuadEuclideanRhythm::ACCENTS_1_INPUT));
  486. addInput(Port::create<PJ301MPort>(Vec(218, 163), Port::INPUT, module, QuadEuclideanRhythm::ACCENT_ROTATE_1_INPUT));
  487. addInput(Port::create<PJ301MPort>(Vec(23, 220), Port::INPUT, module, QuadEuclideanRhythm::STEPS_2_INPUT));
  488. addInput(Port::create<PJ301MPort>(Vec(62, 220), Port::INPUT, module, QuadEuclideanRhythm::DIVISIONS_2_INPUT));
  489. addInput(Port::create<PJ301MPort>(Vec(101, 220), Port::INPUT, module, QuadEuclideanRhythm::OFFSET_2_INPUT));
  490. addInput(Port::create<PJ301MPort>(Vec(140, 220), Port::INPUT, module, QuadEuclideanRhythm::PAD_2_INPUT));
  491. addInput(Port::create<PJ301MPort>(Vec(179, 220), Port::INPUT, module, QuadEuclideanRhythm::ACCENTS_2_INPUT));
  492. addInput(Port::create<PJ301MPort>(Vec(218, 220), Port::INPUT, module, QuadEuclideanRhythm::ACCENT_ROTATE_2_INPUT));
  493. addInput(Port::create<PJ301MPort>(Vec(23, 277), Port::INPUT, module, QuadEuclideanRhythm::STEPS_3_INPUT));
  494. addInput(Port::create<PJ301MPort>(Vec(62, 277), Port::INPUT, module, QuadEuclideanRhythm::DIVISIONS_3_INPUT));
  495. addInput(Port::create<PJ301MPort>(Vec(101, 277), Port::INPUT, module, QuadEuclideanRhythm::OFFSET_3_INPUT));
  496. addInput(Port::create<PJ301MPort>(Vec(140, 277), Port::INPUT, module, QuadEuclideanRhythm::PAD_3_INPUT));
  497. addInput(Port::create<PJ301MPort>(Vec(179, 277), Port::INPUT, module, QuadEuclideanRhythm::ACCENTS_3_INPUT));
  498. addInput(Port::create<PJ301MPort>(Vec(218, 277), Port::INPUT, module, QuadEuclideanRhythm::ACCENT_ROTATE_3_INPUT));
  499. addInput(Port::create<PJ301MPort>(Vec(23, 334), Port::INPUT, module, QuadEuclideanRhythm::STEPS_4_INPUT));
  500. addInput(Port::create<PJ301MPort>(Vec(62, 334), Port::INPUT, module, QuadEuclideanRhythm::DIVISIONS_4_INPUT));
  501. addInput(Port::create<PJ301MPort>(Vec(101, 334), Port::INPUT, module, QuadEuclideanRhythm::OFFSET_4_INPUT));
  502. addInput(Port::create<PJ301MPort>(Vec(140, 334), Port::INPUT, module, QuadEuclideanRhythm::PAD_4_INPUT));
  503. addInput(Port::create<PJ301MPort>(Vec(179, 334), Port::INPUT, module, QuadEuclideanRhythm::ACCENTS_4_INPUT));
  504. addInput(Port::create<PJ301MPort>(Vec(218, 334), Port::INPUT, module, QuadEuclideanRhythm::ACCENT_ROTATE_4_INPUT));
  505. addInput(Port::create<PJ301MPort>(Vec(262, 343), Port::INPUT, module, QuadEuclideanRhythm::CLOCK_INPUT));
  506. addInput(Port::create<PJ301MPort>(Vec(302, 343), Port::INPUT, module, QuadEuclideanRhythm::RESET_INPUT));
  507. addInput(Port::create<PJ301MPort>(Vec(335, 343), Port::INPUT, module, QuadEuclideanRhythm::MUTE_INPUT));
  508. addInput(Port::create<PJ301MPort>(Vec(322, 145), Port::INPUT, module, QuadEuclideanRhythm::START_1_INPUT));
  509. addInput(Port::create<PJ301MPort>(Vec(322, 175), Port::INPUT, module, QuadEuclideanRhythm::START_2_INPUT));
  510. addInput(Port::create<PJ301MPort>(Vec(322, 205), Port::INPUT, module, QuadEuclideanRhythm::START_3_INPUT));
  511. addInput(Port::create<PJ301MPort>(Vec(322, 235), Port::INPUT, module, QuadEuclideanRhythm::START_4_INPUT));
  512. addOutput(Port::create<PJ301MPort>(Vec(255, 145), Port::OUTPUT, module, QuadEuclideanRhythm::OUTPUT_1));
  513. addOutput(Port::create<PJ301MPort>(Vec(286, 145), Port::OUTPUT, module, QuadEuclideanRhythm::ACCENT_OUTPUT_1));
  514. addOutput(Port::create<PJ301MPort>(Vec(354, 145), Port::OUTPUT, module, QuadEuclideanRhythm::EOC_OUTPUT_1));
  515. addOutput(Port::create<PJ301MPort>(Vec(256, 175), Port::OUTPUT, module, QuadEuclideanRhythm::OUTPUT_2));
  516. addOutput(Port::create<PJ301MPort>(Vec(286, 175), Port::OUTPUT, module, QuadEuclideanRhythm::ACCENT_OUTPUT_2));
  517. addOutput(Port::create<PJ301MPort>(Vec(354, 175), Port::OUTPUT, module, QuadEuclideanRhythm::EOC_OUTPUT_2));
  518. addOutput(Port::create<PJ301MPort>(Vec(256, 205), Port::OUTPUT, module, QuadEuclideanRhythm::OUTPUT_3));
  519. addOutput(Port::create<PJ301MPort>(Vec(286, 205), Port::OUTPUT, module, QuadEuclideanRhythm::ACCENT_OUTPUT_3));
  520. addOutput(Port::create<PJ301MPort>(Vec(354, 205), Port::OUTPUT, module, QuadEuclideanRhythm::EOC_OUTPUT_3));
  521. addOutput(Port::create<PJ301MPort>(Vec(256, 235), Port::OUTPUT, module, QuadEuclideanRhythm::OUTPUT_4));
  522. addOutput(Port::create<PJ301MPort>(Vec(286, 235), Port::OUTPUT, module, QuadEuclideanRhythm::ACCENT_OUTPUT_4));
  523. addOutput(Port::create<PJ301MPort>(Vec(354, 235), Port::OUTPUT, module, QuadEuclideanRhythm::EOC_OUTPUT_4));
  524. addChild(ModuleLightWidget::create<SmallLight<BlueLight>>(Vec(282, 285), module, QuadEuclideanRhythm::CHAIN_MODE_NONE_LIGHT));
  525. addChild(ModuleLightWidget::create<SmallLight<GreenLight>>(Vec(282, 300), module, QuadEuclideanRhythm::CHAIN_MODE_BOSS_LIGHT));
  526. addChild(ModuleLightWidget::create<SmallLight<RedLight>>(Vec(282, 315), module, QuadEuclideanRhythm::CHAIN_MODE_EMPLOYEE_LIGHT));
  527. addChild(ModuleLightWidget::create<LargeLight<RedLight>>(Vec(363, 347), module, QuadEuclideanRhythm::MUTED_LIGHT));
  528. addChild(ModuleLightWidget::create<SmallLight<BlueLight>>(Vec(370, 295), module, QuadEuclideanRhythm::CONSTANT_TIME_LIGHT));
  529. }
  530. } // namespace rack_plugin_FrozenWasteland
  531. using namespace rack_plugin_FrozenWasteland;
  532. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, QuadEuclideanRhythm) {
  533. Model *modelQuadEuclideanRhythm = Model::create<QuadEuclideanRhythm, QuadEuclideanRhythmWidget>("Frozen Wasteland", "QuadEuclideanRhythm", "Quad Euclidean Rhythm", SEQUENCER_TAG);
  534. return modelQuadEuclideanRhythm;
  535. }