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.

839 lines
39KB

  1. //***********************************************************************************************
  2. //Thermodynamic Evolving Sequencer module for VCV Rack by Pierre Collard and Marc Boulé
  3. //
  4. //Based on code from the Fundamental plugins by Andrew Belt and graphics
  5. // from the Component Library by Wes Milholen.
  6. //See ./LICENSE.txt for all licenses
  7. //See ./res/fonts/ for font licenses
  8. //
  9. //***********************************************************************************************
  10. #include "Geodesics.hpp"
  11. namespace rack_plugin_Geodesics {
  12. struct Entropia : Module {
  13. enum ParamIds {
  14. RUN_PARAM,
  15. STEPCLOCK_PARAM,// magnetic clock
  16. RESET_PARAM,
  17. RESETONRUN_PARAM,
  18. LENGTH_PARAM,
  19. ENUMS(CV_PARAMS, 16),// first 8 are blue, last 8 are yellow
  20. ENUMS(PROB_PARAMS, 8),// prob knobs
  21. ENUMS(OCT_PARAMS, 2),// energy (range)
  22. ENUMS(QUANTIZE_PARAMS, 2),// plank
  23. STATESWITCH_PARAM,// state switch
  24. SWITCHADD_PARAM,
  25. ENUMS(FIXEDCV_PARAMS, 2),
  26. ENUMS(EXTSIG_PARAMS, 2),
  27. ENUMS(RANDOM_PARAMS, 2),
  28. GPROB_PARAM,
  29. CLKSRC_PARAM,
  30. ENUMS(EXTAUDIO_PARAMS, 2),
  31. NUM_PARAMS
  32. };
  33. enum InputIds {
  34. CERTAIN_CLK_INPUT,
  35. UNCERTAIN_CLK_INPUT,
  36. LENGTH_INPUT,
  37. RUN_INPUT,
  38. RESET_INPUT,
  39. STATESWITCH_INPUT,// state switch
  40. SWITCHADD_INPUT,
  41. ENUMS(OCTCV_INPUTS, 2),
  42. ENUMS(EXTSIG_INPUTS, 2),
  43. ENUMS(QUANTIZE_INPUTS, 2),
  44. GPROB_INPUT,
  45. NUM_INPUTS
  46. };
  47. enum OutputIds {
  48. CV_OUTPUT,// main output
  49. NUM_OUTPUTS
  50. };
  51. enum LightIds {
  52. ENUMS(STEP_LIGHTS, 16),// first 8 are blue, last 8 are yellow
  53. ENUMS(CV_LIGHT, 3),// main output (room for Blue-Yellow-White)
  54. RUN_LIGHT,
  55. STEPCLOCK_LIGHT,
  56. RESET_LIGHT,
  57. RESETONRUN_LIGHT,
  58. ENUMS(LENGTH_LIGHTS, 8),// all off when len = 8, north-west turns on when len = 7, north-west and west on when len = 6, etc
  59. STATESWITCH_LIGHT,
  60. SWITCHADD_LIGHT,
  61. ADD_LIGHT,
  62. ENUMS(QUANTIZE_LIGHTS, 2),
  63. ENUMS(OCT_LIGHTS, 6),// first 3 are blue, last 3 are yellow (symetrical so only 3 instead of 5 declared); 0 is center, 1 is inside mirrors, 2 is outside mirrors
  64. ENUMS(FIXEDCV_LIGHTS, 2),
  65. ENUMS(EXTSIG_LIGHTS, 2),
  66. ENUMS(RANDOM_LIGHTS, 2),
  67. ENUMS(CLKSRC_LIGHTS, 2),// certain, uncertain
  68. ENUMS(EXTAUDIO_LIGHTS, 2),
  69. ENUMS(EXTCV_LIGHTS, 2),
  70. NUM_LIGHTS
  71. };
  72. // Constants
  73. enum SourceIds {SRC_CV, SRC_EXT, SRC_RND};
  74. // Need to save, with reset
  75. int panelTheme = 0;
  76. bool running;
  77. bool resetOnRun;
  78. int length;
  79. int quantize;// a.k.a. plank constant, bit0 = blue, bit1 = yellow
  80. int audio;// bit0 = blue has audio src (else is cv), bit1 = yellow has audio src (else is cv)
  81. int ranges[2];// [0; 2], number of extra octaves to span each side of central octave (which is C4: 0 - 1V)
  82. bool addMode;
  83. int sources[2];// [0; ], first is blue, 2nd yellow; follows SourceIds
  84. int stepIndex;
  85. bool pipeBlue[8];
  86. float randomCVs[2];// used in SRC_RND
  87. int clkSource;// which clock to use (0 = both, 1 = certain only, 2 = uncertain only)
  88. // No need to save
  89. int stepIndexOld;// when equal to stepIndex, crossfade (antipop) is finished, when not equal, crossfade until counter 0, then set to stepIndex
  90. long crossFadeStepsToGo;
  91. long clockIgnoreOnReset;
  92. float resetLight;
  93. float cvLight;
  94. unsigned int lightRefreshCounter = 0;
  95. bool rangeInc[2] = {true, true};// true when 1-3-5 increasing, false when 5-3-1 decreasing
  96. Trigger runningTrigger;
  97. Trigger plankTriggers[2];
  98. Trigger lengthTrigger;
  99. Trigger stateSwitchTrigger;
  100. Trigger switchAddTrigger;
  101. Trigger certainClockTrigger;
  102. Trigger uncertainClockTrigger;
  103. Trigger octTriggers[2];
  104. Trigger stepClockTrigger;
  105. Trigger resetTrigger;
  106. Trigger resetOnRunTrigger;
  107. Trigger fixedSrcTriggers[2];
  108. Trigger rndSrcTriggers[2];
  109. Trigger extSrcTriggers[2];
  110. Trigger extAudioTriggers[2];
  111. Trigger clkSrcTrigger;
  112. float stepClockLight = 0.0f;
  113. float stateSwitchLight = 0.0f;
  114. inline float quantizeCV(float cv) {return roundf(cv * 12.0f) / 12.0f;}
  115. inline void updatePipeBlue(int step) {
  116. float effectiveKnob = params[PROB_PARAMS + step].value + -1.0f * (params[GPROB_PARAM].value + inputs[GPROB_INPUT].value / 5.0f);
  117. pipeBlue[step] = effectiveKnob > randomUniform();
  118. }
  119. inline void updateRandomCVs() {
  120. randomCVs[0] = randomUniform();
  121. randomCVs[1] = randomUniform();
  122. cvLight = 1.0f;// this could be elsewhere since no relevance to randomCVs, but ok here
  123. }
  124. Entropia() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  125. for (int i = 0; i < 8; i++)
  126. params[Entropia::PROB_PARAMS + i].value = 1.0f;// HACK since params not initialized properly yet, remove this in Rack 1.0
  127. onReset();
  128. }
  129. void onReset() override {
  130. running = true;
  131. resetOnRun = false;
  132. length = 8;
  133. quantize = 3;
  134. audio = 0;
  135. addMode = false;
  136. for (int i = 0; i < 2; i++) {
  137. ranges[i] = 1;
  138. sources[i] = SRC_CV;
  139. }
  140. clkSource = 0;
  141. initRun();
  142. clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
  143. }
  144. void onRandomize() override {
  145. initRun();
  146. }
  147. void initRun() {
  148. stepIndex = 0;
  149. stepIndexOld = 0;
  150. crossFadeStepsToGo = 0;
  151. for (int i = 0; i < 8; i++)
  152. updatePipeBlue(i);
  153. updateRandomCVs();
  154. resetLight = 0.0f;
  155. cvLight = 0.0f;
  156. }
  157. json_t *toJson() override {
  158. json_t *rootJ = json_object();
  159. // panelTheme
  160. json_object_set_new(rootJ, "panelTheme", json_integer(panelTheme));
  161. // running
  162. json_object_set_new(rootJ, "running", json_boolean(running));
  163. // resetOnRun
  164. json_object_set_new(rootJ, "resetOnRun", json_boolean(resetOnRun));
  165. // length
  166. json_object_set_new(rootJ, "length", json_integer(length));
  167. // quantize
  168. json_object_set_new(rootJ, "quantize", json_integer(quantize));
  169. // audio
  170. json_object_set_new(rootJ, "audio", json_integer(audio));
  171. // ranges
  172. json_object_set_new(rootJ, "ranges0", json_integer(ranges[0]));
  173. json_object_set_new(rootJ, "ranges1", json_integer(ranges[1]));
  174. // addMode
  175. json_object_set_new(rootJ, "addMode", json_boolean(addMode));
  176. // sources
  177. json_object_set_new(rootJ, "sources0", json_integer(sources[0]));
  178. json_object_set_new(rootJ, "sources1", json_integer(sources[1]));
  179. // stepIndex
  180. json_object_set_new(rootJ, "stepIndex", json_integer(stepIndex));
  181. // pipeBlue (only need to save the one corresponding to stepIndex, since others will get regenerated when moving to those steps)
  182. json_object_set_new(rootJ, "pipeBlue", json_boolean(pipeBlue[stepIndex]));
  183. // randomCVs (only need to save the one corresponding to stepIndex, since others will get regenerated when moving to those steps)
  184. json_object_set_new(rootJ, "randomCVs0", json_real(randomCVs[0]));
  185. json_object_set_new(rootJ, "randomCVs1", json_real(randomCVs[1]));
  186. // clkSource
  187. json_object_set_new(rootJ, "clkSource", json_integer(clkSource));
  188. return rootJ;
  189. }
  190. void fromJson(json_t *rootJ) override {
  191. // panelTheme
  192. json_t *panelThemeJ = json_object_get(rootJ, "panelTheme");
  193. if (panelThemeJ)
  194. panelTheme = json_integer_value(panelThemeJ);
  195. // running
  196. json_t *runningJ = json_object_get(rootJ, "running");
  197. if (runningJ)
  198. running = json_is_true(runningJ);
  199. // resetOnRun
  200. json_t *resetOnRunJ = json_object_get(rootJ, "resetOnRun");
  201. if (resetOnRunJ)
  202. resetOnRun = json_is_true(resetOnRunJ);
  203. // length
  204. json_t *lengthJ = json_object_get(rootJ, "length");
  205. if (lengthJ)
  206. length = json_integer_value(lengthJ);
  207. // quantize
  208. json_t *quantizeJ = json_object_get(rootJ, "quantize");
  209. if (quantizeJ)
  210. quantize = json_integer_value(quantizeJ);
  211. // audio
  212. json_t *audioJ = json_object_get(rootJ, "audio");
  213. if (audioJ)
  214. audio = json_integer_value(audioJ);
  215. // ranges
  216. json_t *ranges0J = json_object_get(rootJ, "ranges0");
  217. if (ranges0J)
  218. ranges[0] = json_integer_value(ranges0J);
  219. json_t *ranges1J = json_object_get(rootJ, "ranges1");
  220. if (ranges1J)
  221. ranges[1] = json_integer_value(ranges1J);
  222. // addMode
  223. json_t *addModeJ = json_object_get(rootJ, "addMode");
  224. if (addModeJ)
  225. addMode = json_is_true(addModeJ);
  226. // sources
  227. json_t *sources0J = json_object_get(rootJ, "sources0");
  228. if (sources0J)
  229. sources[0] = json_integer_value(sources0J);
  230. json_t *sources1J = json_object_get(rootJ, "sources1");
  231. if (sources1J)
  232. sources[1] = json_integer_value(sources1J);
  233. // stepIndex
  234. json_t *stepIndexJ = json_object_get(rootJ, "stepIndex");
  235. if (stepIndexJ)
  236. stepIndex = json_integer_value(stepIndexJ);
  237. // pipeBlue (only saved the one corresponding to stepIndex, since others will get regenerated when moving to those steps)
  238. json_t *pipeBlueJ = json_object_get(rootJ, "pipeBlue");
  239. if (pipeBlueJ)
  240. pipeBlue[stepIndex] = json_is_true(pipeBlueJ);
  241. // randomCVs (only saved the one corresponding to stepIndex, since others will get regenerated when moving to those steps)
  242. json_t *randomCVs0J = json_object_get(rootJ, "randomCVs0");
  243. if (randomCVs0J)
  244. randomCVs[0] = json_number_value(randomCVs0J);
  245. json_t *randomCVs1J = json_object_get(rootJ, "randomCVs1");
  246. if (randomCVs1J)
  247. randomCVs[1] = json_number_value(randomCVs1J);
  248. // clkSource
  249. json_t *clkSourceJ = json_object_get(rootJ, "clkSource");
  250. if (clkSourceJ)
  251. clkSource = json_integer_value(clkSourceJ);
  252. rangeInc[0] = true;
  253. rangeInc[1] = true;
  254. stepIndexOld = stepIndex;
  255. }
  256. void step() override {
  257. float crossFadeTime = 0.005f;
  258. float sampleTime = engineGetSampleTime();
  259. //********** Buttons, knobs, switches and inputs **********
  260. // Run button
  261. if (runningTrigger.process(params[RUN_PARAM].value + inputs[RUN_INPUT].value)) {// no input refresh here, don't want to introduce startup skew
  262. running = !running;
  263. if (running) {
  264. if (resetOnRun)
  265. initRun();
  266. if (resetOnRun || clockIgnoreOnRun)
  267. clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
  268. }
  269. }
  270. if ((lightRefreshCounter & userInputsStepSkipMask) == 0) {
  271. // Length button and input
  272. bool lengthTrig = lengthTrigger.process(params[LENGTH_PARAM].value);
  273. if (inputs[LENGTH_INPUT].active) {
  274. length = clamp(8 - (int)(inputs[LENGTH_INPUT].value * 7.0f / 10.0f + 0.5f) , 1, 8);
  275. }
  276. else if (lengthTrig) {
  277. if (length > 1) length--;
  278. else length = 8;
  279. }
  280. // Plank buttons (quantize)
  281. if (plankTriggers[0].process(params[QUANTIZE_PARAMS + 0].value))
  282. quantize ^= 0x1;
  283. if (plankTriggers[1].process(params[QUANTIZE_PARAMS + 1].value))
  284. quantize ^= 0x2;
  285. // Range buttons and CV inputs
  286. for (int i = 0; i < 2; i++) {
  287. bool rangeTrig = octTriggers[i].process(params[OCT_PARAMS + i].value);
  288. if (inputs[OCTCV_INPUTS + i].active) {
  289. if (inputs[OCTCV_INPUTS + i].value <= -1.0f)
  290. ranges[i] = 0;
  291. else if (inputs[OCTCV_INPUTS + i].value < 1.0f)
  292. ranges[i] = 1;
  293. else
  294. ranges[i] = 2;
  295. }
  296. else if (rangeTrig) {
  297. if (rangeInc[i]) {
  298. ranges[i]++;
  299. if (ranges[i] >= 3) {
  300. ranges[i] = 1;
  301. rangeInc[i] = false;
  302. }
  303. }
  304. else {
  305. ranges[i]--;
  306. if (ranges[i] < 0) {
  307. ranges[i] = 1;
  308. rangeInc[i] = true;
  309. }
  310. }
  311. }
  312. }
  313. // Source buttons (fixedCV, random, ext)
  314. for (int i = 0; i < 2; i++) {
  315. if (rndSrcTriggers[i].process(params[RANDOM_PARAMS + i].value))
  316. sources[i] = SRC_RND;
  317. if (extSrcTriggers[i].process(params[EXTSIG_PARAMS + i].value))
  318. sources[i] = SRC_EXT;
  319. if (fixedSrcTriggers[i].process(params[FIXEDCV_PARAMS + i].value))
  320. sources[i] = SRC_CV;
  321. if (extAudioTriggers[i].process(params[EXTAUDIO_PARAMS + i].value))
  322. audio ^= (1 << i);
  323. }
  324. // addMode
  325. if (switchAddTrigger.process(params[SWITCHADD_PARAM].value + inputs[SWITCHADD_INPUT].value)) {
  326. addMode = !addMode;
  327. }
  328. // StateSwitch
  329. if (stateSwitchTrigger.process(params[STATESWITCH_PARAM].value + inputs[STATESWITCH_INPUT].value)) {
  330. pipeBlue[stepIndex] = !pipeBlue[stepIndex];
  331. stateSwitchLight = 1.0f;
  332. }
  333. // Reset on Run button
  334. if (resetOnRunTrigger.process(params[RESETONRUN_PARAM].value)) {
  335. resetOnRun = !resetOnRun;
  336. }
  337. if (clkSrcTrigger.process(params[CLKSRC_PARAM].value)) {
  338. if (++clkSource > 2)
  339. clkSource = 0;
  340. }
  341. }// userInputs refresh
  342. //********** Clock and reset **********
  343. // External clocks
  344. if (running && clockIgnoreOnReset == 0l) {
  345. bool certainClockTrig = certainClockTrigger.process(inputs[CERTAIN_CLK_INPUT].value);
  346. bool uncertainClockTrig = uncertainClockTrigger.process(inputs[UNCERTAIN_CLK_INPUT].value);
  347. certainClockTrig &= (clkSource < 2);
  348. if (certainClockTrig) {
  349. stepIndex++;
  350. }
  351. uncertainClockTrig &= ((clkSource & 0x1) == 0);
  352. if (uncertainClockTrig) {
  353. stepIndex += getWeighted1to8random();
  354. }
  355. if (certainClockTrig || uncertainClockTrig) {
  356. stepIndex %= length;
  357. crossFadeStepsToGo = (long)(crossFadeTime * engineGetSampleRate());;
  358. updatePipeBlue(stepIndex);
  359. updateRandomCVs();
  360. }
  361. }
  362. // Magnetic clock (manual step clock)
  363. if (stepClockTrigger.process(params[STEPCLOCK_PARAM].value)) {
  364. if (++stepIndex >= length) stepIndex = 0;
  365. crossFadeStepsToGo = (long)(crossFadeTime * engineGetSampleRate());
  366. updatePipeBlue(stepIndex);
  367. updateRandomCVs();
  368. stepClockLight = 1.0f;
  369. }
  370. // Reset
  371. if (resetTrigger.process(inputs[RESET_INPUT].value + params[RESET_PARAM].value)) {
  372. initRun();
  373. resetLight = 1.0f;
  374. clockIgnoreOnReset = (long) (clockIgnoreOnResetDuration * engineGetSampleRate());
  375. certainClockTrigger.reset();
  376. uncertainClockTrigger.reset();
  377. }
  378. //********** Outputs and lights **********
  379. // Output
  380. int crossFadeActive = audio;
  381. if (sources[0] != SRC_EXT) crossFadeActive &= ~0x1;
  382. if (sources[1] != SRC_EXT) crossFadeActive &= ~0x2;
  383. if (crossFadeStepsToGo > 0 && crossFadeActive != 0)
  384. {
  385. long crossFadeStepsToGoInit = (long)(crossFadeTime * engineGetSampleRate());
  386. float fadeRatio = ((float)crossFadeStepsToGo) / ((float)crossFadeStepsToGoInit);
  387. outputs[CV_OUTPUT].value = calcOutput(stepIndexOld) * fadeRatio + calcOutput(stepIndex) * (1.0f - fadeRatio);
  388. crossFadeStepsToGo--;
  389. if (crossFadeStepsToGo == 0)
  390. stepIndexOld = stepIndex;
  391. }
  392. else
  393. outputs[CV_OUTPUT].value = calcOutput(stepIndex);
  394. lightRefreshCounter++;
  395. if (lightRefreshCounter >= displayRefreshStepSkips) {
  396. lightRefreshCounter = 0;
  397. // Reset light
  398. lights[RESET_LIGHT].value = resetLight;
  399. resetLight -= (resetLight / lightLambda) * sampleTime * displayRefreshStepSkips;
  400. // Run light
  401. lights[RUN_LIGHT].value = running ? 1.0f : 0.0f;
  402. lights[RESETONRUN_LIGHT].value = resetOnRun ? 1.0f : 0.0f;
  403. // Length lights
  404. for (int i = 0; i < 8; i++)
  405. lights[LENGTH_LIGHTS + i].value = (i < length ? 0.0f : 1.0f);
  406. // Plank
  407. lights[QUANTIZE_LIGHTS + 0].value = (quantize & 0x1) ? 1.0f : 0.0f;// Blue
  408. lights[QUANTIZE_LIGHTS + 1].value = (quantize & 0x2) ? 1.0f : 0.0f;// Yellow
  409. // step and main output lights (GeoBlueYellowWhiteLight)
  410. lights[CV_LIGHT + 0].value = (pipeBlue[stepIndex]) ? 1.0f * cvLight : 0.0f;
  411. lights[CV_LIGHT + 1].value = (!pipeBlue[stepIndex] && !addMode) ? 1.0f * cvLight : 0.0f;
  412. lights[CV_LIGHT + 2].value = (!pipeBlue[stepIndex] && addMode) ? 1.0f * cvLight : 0.0f;
  413. cvLight -= (cvLight / lightLambda) * sampleTime * displayRefreshStepSkips;
  414. for (int i = 0; i < 8; i++) {
  415. lights[STEP_LIGHTS + i].value = ((pipeBlue[i] || addMode) && stepIndex == i) ? 1.0f : 0.0f;
  416. lights[STEP_LIGHTS + 8 + i].value = ((!pipeBlue[i]) && stepIndex == i) ? 1.0f : 0.0f;
  417. }
  418. // Range (energy) lights
  419. for (int i = 0; i < 3; i++) {
  420. lights[OCT_LIGHTS + i].value = (i <= ranges[0] ? 1.0f : 0.0f);
  421. lights[OCT_LIGHTS + 3 + i].value = (i <= ranges[1] ? 1.0f : 0.0f);
  422. }
  423. // Step clocks light
  424. lights[STEPCLOCK_LIGHT].value = stepClockLight;
  425. stepClockLight -= (stepClockLight / lightLambda) * sampleTime * displayRefreshStepSkips;
  426. // Swtich add light
  427. lights[SWITCHADD_LIGHT].value = (addMode ? 0.0f : 1.0f);
  428. lights[ADD_LIGHT].value = (addMode ? 1.0f : 0.0f);
  429. // State switch light
  430. lights[STATESWITCH_LIGHT].value = stateSwitchLight;
  431. stateSwitchLight -= (stateSwitchLight / lightLambda) * sampleTime * displayRefreshStepSkips;
  432. for (int i = 0; i < 2; i++) {
  433. // Sources lights
  434. lights[RANDOM_LIGHTS + i].value = (sources[i] == SRC_RND) ? 1.0f : 0.0f;
  435. lights[EXTSIG_LIGHTS + i].value = (sources[i] == SRC_EXT) ? 1.0f : 0.0f;
  436. lights[FIXEDCV_LIGHTS + i].value = (sources[i] == SRC_CV) ? 1.0f : 0.0f;
  437. // Audio lights
  438. lights[EXTAUDIO_LIGHTS + i].value = ((audio & (1 << i)) != 0) ? 1.0f : 0.0f;
  439. lights[EXTCV_LIGHTS + i].value = ((audio & (1 << i)) == 0) ? 1.0f : 0.0f;
  440. }
  441. // Clock source lights
  442. lights[CLKSRC_LIGHTS + 0].value = (clkSource < 2) ? 1.0f : 0.0f;
  443. lights[CLKSRC_LIGHTS + 1].value = ((clkSource & 0x1) == 0) ? 1.0f : 0.0f;
  444. }// lightRefreshCounter
  445. if (clockIgnoreOnReset > 0l)
  446. clockIgnoreOnReset--;
  447. }// step()
  448. inline float calcOutput(int stepIdx) {
  449. if (addMode)
  450. return getStepCV(stepIdx, true) + (pipeBlue[stepIdx] ? 0.0f : getStepCV(stepIdx, false));
  451. return getStepCV(stepIdx, pipeBlue[stepIdx]);
  452. }
  453. float getStepCV(int step, bool blue) {
  454. int colorIndex = blue ? 0 : 1;
  455. float knobVal = params[CV_PARAMS + (colorIndex << 3) + step].value;
  456. float cv = 0.0f;
  457. if (sources[colorIndex] == SRC_RND) {
  458. cv = randomCVs[colorIndex] * (knobVal * 10.0f - 5.0f);
  459. }
  460. else if (sources[colorIndex] == SRC_EXT) {
  461. float extOffset = ((audio & (1 << colorIndex)) != 0) ? 0.0f : -1.0f;
  462. cv = clamp(inputs[EXTSIG_INPUTS + colorIndex].value * (knobVal * 2.0f + extOffset), -10.0f, 10.0f);
  463. }
  464. else {// SRC_CV
  465. int range = ranges[colorIndex];
  466. if ( (blue && (quantize & 0x1) != 0) || (!blue && (quantize > 1)) ) {
  467. cv = (knobVal * (float)(range * 2 + 1) - (float)range);
  468. cv = quantizeCV(cv);
  469. }
  470. else {
  471. int maxCV = (range == 0 ? 1 : (range * 5));// maxCV is [1, 5, 10]
  472. cv = knobVal * (float)(maxCV * 2) - (float)maxCV;
  473. }
  474. }
  475. return cv;
  476. }
  477. };
  478. struct EntropiaWidget : ModuleWidget {
  479. struct PanelThemeItem : MenuItem {
  480. Entropia *module;
  481. int theme;
  482. void onAction(EventAction &e) override {
  483. module->panelTheme = theme;
  484. }
  485. void step() override {
  486. rightText = (module->panelTheme == theme) ? "✔" : "";
  487. }
  488. };
  489. Menu *createContextMenu() override {
  490. Menu *menu = ModuleWidget::createContextMenu();
  491. MenuLabel *spacerLabel = new MenuLabel();
  492. menu->addChild(spacerLabel);
  493. Entropia *module = dynamic_cast<Entropia*>(this->module);
  494. assert(module);
  495. MenuLabel *themeLabel = new MenuLabel();
  496. themeLabel->text = "Panel Theme";
  497. menu->addChild(themeLabel);
  498. PanelThemeItem *lightItem = new PanelThemeItem();
  499. lightItem->text = lightPanelID;// Geodesics.hpp
  500. lightItem->module = module;
  501. lightItem->theme = 0;
  502. menu->addChild(lightItem);
  503. PanelThemeItem *darkItem = new PanelThemeItem();
  504. darkItem->text = darkPanelID;// Geodesics.hpp
  505. darkItem->module = module;
  506. darkItem->theme = 1;
  507. menu->addChild(darkItem);
  508. return menu;
  509. }
  510. EntropiaWidget(Entropia *module) : ModuleWidget(module) {
  511. // Main panel from Inkscape
  512. DynamicSVGPanel *panel = new DynamicSVGPanel();
  513. panel->addPanel(SVG::load(assetPlugin(plugin, "res/WhiteLight/Entropia-WL.svg")));
  514. panel->addPanel(SVG::load(assetPlugin(plugin, "res/DarkMatter/Entropia-DM.svg")));
  515. box.size = panel->box.size;
  516. panel->mode = &module->panelTheme;
  517. addChild(panel);
  518. // Screws
  519. // part of svg panel, no code required
  520. static constexpr float colRulerCenter = 157.0f;//box.size.x / 2.0f;
  521. static constexpr float rowRulerOutput = 380.0f - 155.5f;
  522. static constexpr float radius1 = 50.0f;
  523. static constexpr float offset1 = 35.5f;
  524. static constexpr float radius3 = 105.0f;
  525. static constexpr float offset3 = 74.5f;
  526. static constexpr float offset2b = 74.5f;// big
  527. static constexpr float offset2s = 27.5f;// small
  528. // CV out and light
  529. addOutput(createDynamicPort<GeoPort>(Vec(colRulerCenter, rowRulerOutput), Port::OUTPUT, module, Entropia::CV_OUTPUT, &module->panelTheme));
  530. addChild(createLightCentered<SmallLight<GeoBlueYellowWhiteLight>>(Vec(colRulerCenter, rowRulerOutput - 21.5f), module, Entropia::CV_LIGHT));
  531. // Blue CV knobs
  532. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter, rowRulerOutput - radius1), module, Entropia::CV_PARAMS + 0, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  533. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter + offset1, rowRulerOutput - offset1), module, Entropia::CV_PARAMS + 1, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  534. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter + radius1, rowRulerOutput), module, Entropia::CV_PARAMS + 2, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  535. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter + offset1, rowRulerOutput + offset1), module, Entropia::CV_PARAMS + 3, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  536. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter, rowRulerOutput + radius1), module, Entropia::CV_PARAMS + 4, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  537. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter - offset1, rowRulerOutput + offset1), module, Entropia::CV_PARAMS + 5, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  538. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter - radius1, rowRulerOutput), module, Entropia::CV_PARAMS + 6, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  539. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter - offset1, rowRulerOutput - offset1), module, Entropia::CV_PARAMS + 7, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  540. // Yellow CV knobs
  541. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter, rowRulerOutput - radius3), module, Entropia::CV_PARAMS + 8 + 0, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  542. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter + offset3, rowRulerOutput - offset3), module, Entropia::CV_PARAMS + 8 + 1, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  543. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter + radius3, rowRulerOutput), module, Entropia::CV_PARAMS + 8 + 2, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  544. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter + offset3, rowRulerOutput + offset3), module, Entropia::CV_PARAMS + 8 + 3, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  545. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter, rowRulerOutput + radius3), module, Entropia::CV_PARAMS + 8 + 4, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  546. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter - offset3, rowRulerOutput + offset3), module, Entropia::CV_PARAMS + 8 + 5, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  547. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter - radius3, rowRulerOutput), module, Entropia::CV_PARAMS + 8 + 6, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  548. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter - offset3, rowRulerOutput - offset3), module, Entropia::CV_PARAMS + 8 + 7, 0.0f, 1.0f, 0.5f, &module->panelTheme));
  549. // Prob CV knobs
  550. addParam(createDynamicParam<GeoKnobRight>(Vec(colRulerCenter + offset2s, rowRulerOutput - offset2b - 3), module, Entropia::PROB_PARAMS + 0, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  551. addParam(createDynamicParam<GeoKnobBotRight>(Vec(colRulerCenter + offset2b, rowRulerOutput - offset2s - 8), module, Entropia::PROB_PARAMS + 1, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  552. addParam(createDynamicParam<GeoKnobBottom>(Vec(colRulerCenter + offset2b + 3, rowRulerOutput + offset2s), module, Entropia::PROB_PARAMS + 2, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  553. addParam(createDynamicParam<GeoKnobBotLeft>(Vec(colRulerCenter + offset2s + 8, rowRulerOutput + offset2b), module, Entropia::PROB_PARAMS + 3, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  554. addParam(createDynamicParam<GeoKnobLeft>(Vec(colRulerCenter - offset2s, rowRulerOutput + offset2b + 3), module, Entropia::PROB_PARAMS + 4, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  555. addParam(createDynamicParam<GeoKnobTopLeft>(Vec(colRulerCenter - offset2b, rowRulerOutput + offset2s + 8), module, Entropia::PROB_PARAMS + 5, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  556. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter - offset2b - 3, rowRulerOutput - offset2s), module, Entropia::PROB_PARAMS + 6, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  557. addParam(createDynamicParam<GeoKnobTopRight>(Vec(colRulerCenter - offset2s - 7.5f, rowRulerOutput - offset2b + 1.0f), module, Entropia::PROB_PARAMS + 7, 0.0f, 1.0f, 1.0f, &module->panelTheme));
  558. // Blue step lights
  559. float radiusBL = 228.5f - 155.5f;// radius blue lights
  560. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter, rowRulerOutput - radiusBL), module, Entropia::STEP_LIGHTS + 0));
  561. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter + radiusBL * 0.707f, rowRulerOutput - radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 1));
  562. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter + radiusBL, rowRulerOutput), module, Entropia::STEP_LIGHTS + 2));
  563. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter + radiusBL * 0.707f, rowRulerOutput + radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 3));
  564. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter, rowRulerOutput + radiusBL), module, Entropia::STEP_LIGHTS + 4));
  565. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - radiusBL * 0.707f, rowRulerOutput + radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 5));
  566. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - radiusBL, rowRulerOutput), module, Entropia::STEP_LIGHTS + 6));
  567. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - radiusBL * 0.707f, rowRulerOutput - radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 7));
  568. radiusBL += 9.0f;
  569. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter, rowRulerOutput - radiusBL), module, Entropia::STEP_LIGHTS + 8 + 0));
  570. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + radiusBL * 0.707f, rowRulerOutput - radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 8 + 1));
  571. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + radiusBL, rowRulerOutput), module, Entropia::STEP_LIGHTS + 8 + 2));
  572. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + radiusBL * 0.707f, rowRulerOutput + radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 8 + 3));
  573. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter, rowRulerOutput + radiusBL), module, Entropia::STEP_LIGHTS + 8 + 4));
  574. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter - radiusBL * 0.707f, rowRulerOutput + radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 8 + 5));
  575. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter - radiusBL, rowRulerOutput), module, Entropia::STEP_LIGHTS + 8 + 6));
  576. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter - radiusBL * 0.707f, rowRulerOutput - radiusBL * 0.707f), module, Entropia::STEP_LIGHTS + 8 + 7));
  577. // Length jack, button and lights
  578. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter + 116.5f, rowRulerOutput + 70.0f), Port::INPUT, module, Entropia::LENGTH_INPUT, &module->panelTheme));
  579. static float lenButtonX = colRulerCenter + 130.5f;
  580. static float lenButtonY = rowRulerOutput + 36.5f;
  581. addParam(createDynamicParam<GeoPushButton>(Vec(lenButtonX, lenButtonY), module, Entropia::LENGTH_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  582. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX , lenButtonY - 14.5f), module, Entropia::LENGTH_LIGHTS + 0));
  583. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX + 10.5f, lenButtonY - 10.5f), module, Entropia::LENGTH_LIGHTS + 1));
  584. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX + 14.5f, lenButtonY ), module, Entropia::LENGTH_LIGHTS + 2));
  585. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX + 10.5f, lenButtonY + 10.5f), module, Entropia::LENGTH_LIGHTS + 3));
  586. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX , lenButtonY + 14.5f), module, Entropia::LENGTH_LIGHTS + 4));
  587. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX - 10.5f, lenButtonY + 10.5f), module, Entropia::LENGTH_LIGHTS + 5));
  588. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX - 14.5f, lenButtonY ), module, Entropia::LENGTH_LIGHTS + 6));
  589. addChild(createLightCentered<SmallLight<GeoRedLight>>(Vec(lenButtonX - 10.5f, lenButtonY - 10.5f), module, Entropia::LENGTH_LIGHTS + 7));
  590. // Clock inputs
  591. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter - 130.5f, rowRulerOutput + 36.5f), Port::INPUT, module, Entropia::CERTAIN_CLK_INPUT, &module->panelTheme));
  592. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter - 116.5f, rowRulerOutput + 70.0f), Port::INPUT, module, Entropia::UNCERTAIN_CLK_INPUT, &module->panelTheme));
  593. // Clock source button and LEDs
  594. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(43.0f, 256.5f), module, Entropia::CLKSRC_LIGHTS + 0));
  595. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(55.0f, 284.5f), module, Entropia::CLKSRC_LIGHTS + 1));
  596. addParam(createDynamicParam<GeoPushButton>(Vec(46.0f, 272.5f), module, Entropia::CLKSRC_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  597. // Switch, add, state (jacks, buttons, ligths)
  598. // left side
  599. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter - 130.5f, rowRulerOutput - 36.0f), Port::INPUT, module, Entropia::SWITCHADD_INPUT, &module->panelTheme));
  600. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - 115.5f, rowRulerOutput - 69.0f), module, Entropia::SWITCHADD_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  601. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(colRulerCenter - 115.5f - 7.0f, rowRulerOutput - 69.0f + 13.0f), module, Entropia::SWITCHADD_LIGHT));
  602. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(colRulerCenter - 115.5f + 3.0f, rowRulerOutput - 69.0f + 14.0f), module, Entropia::ADD_LIGHT));
  603. // right side
  604. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter + 130.5f, rowRulerOutput - 36.0f), Port::INPUT, module, Entropia::STATESWITCH_INPUT, &module->panelTheme));
  605. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + 115.5f, rowRulerOutput - 69.0f), module, Entropia::STATESWITCH_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  606. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(colRulerCenter + 115.5f + 7.0f, rowRulerOutput - 69.0f + 13.0f), module, Entropia::STATESWITCH_LIGHT));
  607. // Plank constant (jack, light and button)
  608. // left side (blue)
  609. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter - 96.0f, rowRulerOutput - 96.0f), Port::INPUT, module, Entropia::OCTCV_INPUTS + 0, &module->panelTheme));
  610. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - 96.0f - 13.0f, rowRulerOutput - 96.0f - 13.0f), module, Entropia::QUANTIZE_LIGHTS + 0));
  611. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - 96.0f - 23.0f, rowRulerOutput - 96.0f - 23.0f), module, Entropia::QUANTIZE_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  612. // right side (yellow)
  613. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter + 96.0f, rowRulerOutput - 96.0f), Port::INPUT, module, Entropia::OCTCV_INPUTS + 1, &module->panelTheme));
  614. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + 96.0f + 13.0f, rowRulerOutput - 96.0f - 13.0f), module, Entropia::QUANTIZE_LIGHTS + 1));
  615. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + 96.0f + 23.0f, rowRulerOutput - 96.0f - 23.0f), module, Entropia::QUANTIZE_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  616. // Energy (button and lights)
  617. // left side (blue)
  618. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - 69.5f, rowRulerOutput - 116.0f), module, Entropia::OCT_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  619. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - 69.5f - 12.0f, rowRulerOutput - 116.0f + 9.0f), module, Entropia::OCT_LIGHTS + 0));
  620. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - 69.5f - 15.0f, rowRulerOutput - 116.0f - 1.0f), module, Entropia::OCT_LIGHTS + 1));
  621. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - 69.5f - 3.0f, rowRulerOutput - 116.0f + 14.0f), module, Entropia::OCT_LIGHTS + 1));
  622. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - 69.5f - 10.0f, rowRulerOutput - 116.0f - 11.0f), module, Entropia::OCT_LIGHTS + 2));
  623. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - 69.5f + 7.0f, rowRulerOutput - 116.0f + 12.0f), module, Entropia::OCT_LIGHTS + 2));
  624. // right side (yellow)
  625. // left side (blue)
  626. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + 69.5f, rowRulerOutput - 116.0f), module, Entropia::OCT_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  627. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + 69.5f + 12.0f, rowRulerOutput - 116.0f + 9.0f), module, Entropia::OCT_LIGHTS + 3 + 0));
  628. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + 69.5f + 15.0f, rowRulerOutput - 116.0f - 1.0f), module, Entropia::OCT_LIGHTS + 3 + 1));
  629. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + 69.5f + 3.0f, rowRulerOutput - 116.0f + 14.0f), module, Entropia::OCT_LIGHTS + 3 + 1));
  630. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + 69.5f + 10.0f, rowRulerOutput - 116.0f - 11.0f), module, Entropia::OCT_LIGHTS + 3 + 2));
  631. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + 69.5f - 7.0f, rowRulerOutput - 116.0f + 12.0f), module, Entropia::OCT_LIGHTS + 3 + 2));
  632. // Top portion
  633. static constexpr float rowRulerTop = rowRulerOutput - 150.0f;
  634. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter, rowRulerTop - 30.5f), Port::INPUT, module, Entropia::GPROB_INPUT, &module->panelTheme));
  635. addParam(createDynamicParam<GeoKnob>(Vec(colRulerCenter, rowRulerTop), module, Entropia::GPROB_PARAM, -1.0f, 1.0f, 0.0f, &module->panelTheme));
  636. // Left side top
  637. // ext
  638. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter - 77.5f, rowRulerTop), Port::INPUT, module, Entropia::EXTSIG_INPUTS + 0, &module->panelTheme));
  639. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - 41.5f, rowRulerTop), module, Entropia::EXTSIG_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  640. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - 26.5f, rowRulerTop), module, Entropia::EXTSIG_LIGHTS + 0));
  641. // random
  642. static constexpr float buttonOffsetX = 35.5f;// button
  643. static constexpr float buttonOffsetY = 20.5f;// button
  644. static constexpr float lightOffsetX = 22.5f;// light
  645. static constexpr float lightOffsetY = 12.5f;// light
  646. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - buttonOffsetX, rowRulerTop - buttonOffsetY), module, Entropia::RANDOM_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  647. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - lightOffsetX, rowRulerTop - lightOffsetY), module, Entropia::RANDOM_LIGHTS + 0));
  648. // fixed cv
  649. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - buttonOffsetX, rowRulerTop + buttonOffsetY), module, Entropia::FIXEDCV_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  650. addChild(createLightCentered<SmallLight<GeoBlueLight>>(Vec(colRulerCenter - lightOffsetX, rowRulerTop + lightOffsetY), module, Entropia::FIXEDCV_LIGHTS + 0));
  651. // audio
  652. addParam(createDynamicParam<GeoPushButton>(Vec(38.5f, 380.0f - 325.5f), module, Entropia::EXTAUDIO_PARAMS + 0, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  653. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(40.0f, 380.0f - 311.5f), module, Entropia::EXTAUDIO_LIGHTS + 0));
  654. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(48.5f, 380.0f - 315.5f), module, Entropia::EXTCV_LIGHTS + 0));
  655. // Right side top
  656. // ext
  657. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter + 77.5f, rowRulerTop), Port::INPUT, module, Entropia::EXTSIG_INPUTS + 1, &module->panelTheme));
  658. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + 41.5f, rowRulerTop), module, Entropia::EXTSIG_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  659. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + 26.5f, rowRulerTop), module, Entropia::EXTSIG_LIGHTS + 1));
  660. // random
  661. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + buttonOffsetX, rowRulerTop - buttonOffsetY), module, Entropia::RANDOM_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  662. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + lightOffsetX, rowRulerTop - lightOffsetY), module, Entropia::RANDOM_LIGHTS + 1));
  663. // fixed cv
  664. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + buttonOffsetX, rowRulerTop + buttonOffsetY), module, Entropia::FIXEDCV_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  665. addChild(createLightCentered<SmallLight<GeoYellowLight>>(Vec(colRulerCenter + lightOffsetX, rowRulerTop + lightOffsetY), module, Entropia::FIXEDCV_LIGHTS + 1));
  666. // audio
  667. addParam(createDynamicParam<GeoPushButton>(Vec(315.0f - 38.5f, 380.0f - 325.5f), module, Entropia::EXTAUDIO_PARAMS + 1, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  668. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(315.0f - 40.0f, 380.0f - 311.5f), module, Entropia::EXTAUDIO_LIGHTS + 1));
  669. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(315.0f - 48.5f, 380.0f - 315.5f), module, Entropia::EXTCV_LIGHTS + 1));
  670. // Bottom row
  671. // Run jack, light and button
  672. static constexpr float rowRulerRunJack = 380.0f - 32.5f;
  673. static constexpr float offsetRunJackX = 119.5f;
  674. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter - offsetRunJackX, rowRulerRunJack), Port::INPUT, module, Entropia::RUN_INPUT, &module->panelTheme));
  675. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(colRulerCenter - offsetRunJackX + 18.0f, rowRulerRunJack), module, Entropia::RUN_LIGHT));
  676. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - offsetRunJackX + 33.0f, rowRulerRunJack), module, Entropia::RUN_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  677. // Reset jack, light and button
  678. addInput(createDynamicPort<GeoPort>(Vec(colRulerCenter + offsetRunJackX, rowRulerRunJack), Port::INPUT, module, Entropia::RESET_INPUT, &module->panelTheme));
  679. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(colRulerCenter + offsetRunJackX - 18.0f, rowRulerRunJack), module, Entropia::RESET_LIGHT));
  680. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + offsetRunJackX - 33.0f, rowRulerRunJack), module, Entropia::RESET_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  681. static constexpr float offsetMagneticButton = 42.5f;
  682. // Magnetic clock (step clocks)
  683. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(colRulerCenter - offsetMagneticButton - 15.0f, rowRulerRunJack), module, Entropia::STEPCLOCK_LIGHT));
  684. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter - offsetMagneticButton, rowRulerRunJack), module, Entropia::STEPCLOCK_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  685. // Reset on Run light and button
  686. addChild(createLightCentered<SmallLight<GeoWhiteLight>>(Vec(colRulerCenter + offsetMagneticButton + 15.0f, rowRulerRunJack), module, Entropia::RESETONRUN_LIGHT));
  687. addParam(createDynamicParam<GeoPushButton>(Vec(colRulerCenter + offsetMagneticButton, rowRulerRunJack), module, Entropia::RESETONRUN_PARAM, 0.0f, 1.0f, 0.0f, &module->panelTheme));
  688. }
  689. };
  690. } // namespace rack_plugin_Geodesics
  691. using namespace rack_plugin_Geodesics;
  692. RACK_PLUGIN_MODEL_INIT(Geodesics, Entropia) {
  693. Model *modelEntropia = Model::create<Entropia, EntropiaWidget>("Geodesics", "Entropia", "Entropia", SEQUENCER_TAG);
  694. return modelEntropia;
  695. }
  696. /*CHANGE LOG
  697. 0.6.6:
  698. add audio/cv switch for ext source, and activate anti-pop when at least one channel has audio input
  699. make step knobs select a gain rather that attenuverter for ext sources
  700. 0.6.5:
  701. created
  702. */