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.

1043 lines
43KB

  1. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  2. ////// RKD with "Break" is a 2x 4 HP module, combining RKD module (4 HP) and "BRK" panel (4 HP). //////
  3. ////// The "Break" panel provides six switches to access "jumpers" (setting) more easily //////
  4. ////// (without need to access jumpers located on module's PCB!). //////
  5. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  6. ////// Inspired from existing Eurorack hardware RCDBO combo module, by 4ms Company. //////
  7. ////// Done with restricted 4ms Company permission (thank you 4ms). //////
  8. ////// This module uses its own algorithm (no original part of firmware code was used). //////
  9. ////// 4ms Company name, logo, RCD, RCDBO, Rotating Clock Divider & RCD Breakout as TRADEMARKED! //////
  10. ///////////////////////////////////////////////////////////////////////////////////////////////////////
  11. #include "Ohmer.hpp"
  12. #include <dsp/digital.hpp>
  13. namespace rack_plugin_Ohmer {
  14. struct RKDBRK : Module {
  15. enum ParamIds {
  16. JUMPER_COUNTINGDOWN,
  17. JUMPER_GATE,
  18. JUMPER_MAXDIVRANGE16,
  19. JUMPER_MAXDIVRANGE32,
  20. JUMPER_SPREAD,
  21. JUMPER_AUTORESET,
  22. NUM_PARAMS
  23. };
  24. enum InputIds {
  25. ROTATE_INPUT,
  26. RESET_INPUT,
  27. CLK_INPUT,
  28. NUM_INPUTS
  29. };
  30. enum OutputIds {
  31. OUTPUT_1,
  32. OUTPUT_2,
  33. OUTPUT_3,
  34. OUTPUT_4,
  35. OUTPUT_5,
  36. OUTPUT_6,
  37. OUTPUT_7,
  38. OUTPUT_8,
  39. NUM_OUTPUTS
  40. };
  41. enum LightIds {
  42. LED_OUT_1,
  43. LED_OUT_2,
  44. LED_OUT_3,
  45. LED_OUT_4,
  46. LED_OUT_5,
  47. LED_OUT_6,
  48. LED_OUT_7,
  49. LED_OUT_8,
  50. LED_CLK,
  51. LED_RESET_RED,
  52. LED_RESET_ORANGE,
  53. LED_RESET_BLUE,
  54. NUM_LIGHTS
  55. };
  56. RKDBRK() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  57. // NOT USED FOR RKD+BRK MODULE! (this flag indicates if jumpers (PCB) is visible, or not).
  58. //bool bViewPCB = false; NOT USED FOR RKD+BRK MODULE!
  59. // This flag is set when module is running (CLK jack is wired).
  60. bool bCLKisActive = false;
  61. // Schmitt trigger, for RESET input port.
  62. SchmittTrigger RESET_Port;
  63. // Schmitt trigger, for CLK input port.
  64. SchmittTrigger CLK_Port;
  65. // This flag, when true, indicates the CLK rising edge at the current step.
  66. bool bIsRisingEdge = false;
  67. // Next incoming rising edge will be the first rising edge. Required to handle gate modes together with counting up or down.
  68. bool bIsEarlyRisingEdge = true;
  69. // This flag, when true, indicates the CLK falling edge at the current step.
  70. bool bIsFallingEdge = false;
  71. // This flag, when true, indicates the CLK is high (voltage equal or higher +2V).
  72. bool bCLKisHigh = false;
  73. // Assumed timeout at start.
  74. bool bCLKTimeOut = true;
  75. // Default jumpers/switches setting (false = Off, true = On).
  76. bool jmprCountingDown = false; // Factory is Off: Counting Up.
  77. bool jmprCountingDownPrevious = false;
  78. bool jmprGate = false; // Factory is Off: Trig.
  79. bool jmprGatePrevious = false;
  80. bool jmprMaxDivRange16 = true; // Factory is On (combined with Max-Div-Range 32, also On by default): Max Div amount = 8.
  81. bool jmprMaxDivRange16Previous = true;
  82. bool jmprMaxDivRange32 = true; // Factory is On (combined with Max-Div-Range 16, also On by default): Max Div amount = 8.
  83. bool jmprMaxDivRange32Previous = true;
  84. bool jmprSpread = false; // Factory is Off: Spread Off.
  85. bool jmprSpreadPrevious = false;
  86. bool jmprAutoReset = false; // Factory is Off = Auto-Reset Off.
  87. // Table set (0: Manufacturer, 1: Prime numbers, 2: Perfect squares, 3: Fibonacci sequence, 4: Triplet & 16ths).
  88. int tableSet = 0; // This variable is persistent (json).
  89. int tableSetPrev = 0; // Used to change detection across consecutive steps.
  90. // RKD default dividers table.
  91. int tblDividersR0[NUM_OUTPUTS] = {1, 2, 3, 4, 5, 6, 7, 8}; // default dividers (R+0) when using factory jumpers/switches setting.
  92. // Prime numbers base table.
  93. int tblPrimes[18] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61};
  94. // Perfect squares base table.
  95. int tblSquares[8] = {1, 4, 9, 16, 25, 36, 49, 64};
  96. // Fibonacci sequence base table.
  97. int tblFibonacci[9] = {1, 2, 3, 5, 8, 13, 21, 34, 55};
  98. // Triplet & 16ths table.
  99. int tblTripletSixteenths[8] = {1, 2, 3, 4, 8, 16, 32, 64};
  100. // Current (active) dividers table.
  101. int tblActiveDividers[NUM_OUTPUTS] = {1, 2, 3, 4, 5, 6, 7, 8};
  102. // Rotation dividers table (as prepared table).
  103. // Future dividers table, when rotation is required. Last value of this array will be used as "temp backup/restore", during rotation.
  104. int tblDividersRt[NUM_OUTPUTS + 1] = {1, 2, 3, 4, 5, 6, 7, 8, 0};
  105. // When set (armed), indicates table have been changed (eg after jumper/switch change/context-menu table).
  106. bool bTableChange = true;
  107. // When set (armed), prepare the rotation (set new dividers table).
  108. bool bDoRotation = false;
  109. // When set (armed), doing rotation on next rising-edge (coming on CLK input port).
  110. bool bDoRotationOnRisingEdge = false;
  111. // Displayed dividers into segment-LED displays (assuming defaults are "--" because the CLK isn't patched).
  112. char dispDiv1[3] = "--";
  113. char dispDiv2[3] = "--";
  114. char dispDiv3[3] = "--";
  115. char dispDiv4[3] = "--";
  116. char dispDiv5[3] = "--";
  117. char dispDiv6[3] = "--";
  118. char dispDiv7[3] = "--";
  119. char dispDiv8[3] = "--";
  120. // Maximum divide amount, default is 8 (for manufacter table).
  121. int maxDivAmount = 8;
  122. // ROTATE (CV) voltage.
  123. float cvRotate = 0.0f;
  124. int cvRotateTblIndex = 0;
  125. int cvRotateTblIndexPrevious = 0;
  126. // RESET voltage (trigger input port).
  127. float cvReset = 0.0f;
  128. bool bResetOnJack = false;
  129. bool bRegisteredResetOnJack = false;
  130. // Step-based (sample) counters.
  131. long long int currentStep = 0;
  132. long long int previousStep = 0;
  133. long long int expectedStep = 0;
  134. // Source (CLK) frequency flag (set when source frequency is known).
  135. bool bCLKFreqKnown = false;
  136. // Dividers counters (one per output jack).
  137. int divCounters[NUM_OUTPUTS] = {0, 0, 0, 0, 0, 0, 0, 0};
  138. // Global Auto-Reset sequence counter.
  139. int divCountersAutoReset = 0;
  140. // This flag is set on "Auto-Reset" event.
  141. bool bIsAutoReset = false;
  142. // This flag allow/inhibit Auto-Reset - temporary (Auto-Reset will not fired after a timeout/reset, or a reset done via RESET jack).
  143. bool bAllowAutoReset = false;
  144. // This flag is used only for blue RESET (Auto-Reset) LED (too avoid too long flashing LED).
  145. bool bAutoResetLEDfired = false;
  146. // True if output jack is fired (pulsing).
  147. bool bJackIsFired[NUM_OUTPUTS] = {false, false, false, false, false, false, false, false};
  148. // RESET LED afterglow (0: end of afterglow/unlit LED, other positive values indicate how many steps the LED is lit.
  149. int ledResetAfterglow = 0;
  150. //
  151. // Methods (void functions).
  152. // DSP method.
  153. void step() override;
  154. // While using "Initialize" from context-menu, or by using Ctrl+I/Command+I shortcut over module.
  155. void reset() override {
  156. // While using "Initialize" from context-menu (or by using Ctrl+I/Command+I over module).
  157. this->jmprCountingDown = false;
  158. jmprCountingDownPrevious = false;
  159. this->jmprGate = false;
  160. jmprGatePrevious = false;
  161. this->jmprMaxDivRange16 = true;
  162. jmprMaxDivRange16Previous = true;
  163. this->jmprMaxDivRange32 = true;
  164. jmprMaxDivRange32Previous = true;
  165. this->jmprSpread = false;
  166. jmprSpreadPrevious = false;
  167. this->jmprAutoReset = false;
  168. this->tableSet = 0;
  169. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  170. tblDividersR0[i] = i + 1; // Default dividers for all output ports (manufacturer table).
  171. // Default factory maximum divide amount is 8.
  172. maxDivAmount = 8;
  173. // Set module in timeout (sleeping) mode, to reset some variables/flags/counters...
  174. ModuleTimeOut();
  175. }
  176. void ModuleTimeOut() {
  177. // Reset Schmitt trigger used by RESET input jack.
  178. RESET_Port.reset();
  179. // Defining trigger thresholds for RESET input jack (rescale).
  180. //RESET_Port.setThresholds(0.2f, 3.5f);
  181. bResetOnJack = false;
  182. bRegisteredResetOnJack = false;
  183. // Reset Schmitt trigger used by CLK input jack.
  184. CLK_Port.reset();
  185. // Defining thresholds for CLK input jack (rescale).
  186. //CLK_Port.setThresholds(0.2f, 3.5f);
  187. // CLK is low (not wired = no signal = false).
  188. bCLKisHigh = false;
  189. // Reset ROTATE indexes.
  190. cvRotateTblIndex = 0;
  191. cvRotateTblIndexPrevious = 0;
  192. // Table rotation is on Initialize. For now we're using standard "R+0" base table.
  193. bDoRotation = true;
  194. bDoRotationOnRisingEdge = false;
  195. //
  196. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++) {
  197. divCounters[i] = 0; // Reset all dividers counters to 0 (for all output jacks).
  198. pulseOutputJack(i, false); // Be sure this jack isn't pulsing.
  199. }
  200. // Reset "Auto-Reset" counter and related flags.
  201. divCountersAutoReset = 0;
  202. bIsAutoReset = false;
  203. bAllowAutoReset = false;
  204. bAutoResetLEDfired = false;
  205. // Unlit CLK LED.
  206. lights[LED_CLK].value = 0.0f;
  207. // Source (CLK) frequency is reset (because CLK signal is lost/absent).
  208. bCLKFreqKnown = false;
  209. // Reset step-based counters.
  210. currentStep = 0;
  211. previousStep = 0;
  212. expectedStep = 0;
  213. // Early rising edge flag. When set, this meaning the next rising edge will be considered as early (first) rising edge. Required for gate modes!
  214. bIsEarlyRisingEdge = true;
  215. // Set time out flag (this will lit RESET red LED).
  216. bCLKTimeOut = true;
  217. }
  218. // Pulse manager.
  219. void pulseOutputJack(int givenOutputJack, bool bJackPulseState) {
  220. outputs[givenOutputJack].value = bJackPulseState ? 5.0f : 0.0f;
  221. lights[givenOutputJack].value = bJackPulseState ? 1.0f : 0.0f;
  222. bJackIsFired[givenOutputJack] = bJackPulseState;
  223. }
  224. // Persistence for extra datas via json functions (in particular setting defined via jumpers/switches, and table set).
  225. // These extra datas are saved into .vcv files (including "autosave.vcv").
  226. // Also these extra datas are "transfered" as soon as you duplicate (clone) module on the rack.
  227. json_t *toJson() override {
  228. json_t *rootJ = json_object();
  229. json_object_set_new(rootJ, "jmprCountingDown", json_boolean(jmprCountingDown)); // "Counting" jumper/switch.
  230. json_object_set_new(rootJ, "jmprGate", json_boolean(jmprGate)); // "Trig./Gate" jumper/switch.
  231. json_object_set_new(rootJ, "jmprMaxDivRange16", json_boolean(jmprMaxDivRange16)); // "Max-Div-Range 16" jumper/switch.
  232. json_object_set_new(rootJ, "jmprMaxDivRange32", json_boolean(jmprMaxDivRange32)); // "Max-Div-Range 32" jumper/switch.
  233. json_object_set_new(rootJ, "jmprSpread", json_boolean(jmprSpread)); // "Spread" jumper/switch.
  234. json_object_set_new(rootJ, "jmprAutoReset", json_boolean(jmprAutoReset)); // "Auto-Reset" jumper/switch.
  235. json_object_set_new(rootJ, "tableSet", json_integer(tableSet)); // Table set (0: Manufacturer, 1: Prime numbers, 2: Perfect squares, 3: Fibonacci sequence).
  236. return rootJ;
  237. }
  238. // Retrieving "json" persistent settings.
  239. void fromJson(json_t *rootJ) override {
  240. json_t *jmprCountingDownJ = json_object_get(rootJ, "jmprCountingDown");
  241. if (jmprCountingDownJ)
  242. jmprCountingDown = json_is_true(jmprCountingDownJ);
  243. json_t *jmprGateJ = json_object_get(rootJ, "jmprGate");
  244. if (jmprGateJ)
  245. jmprGate = json_is_true(jmprGateJ);
  246. json_t *jmprMaxDivRange16J = json_object_get(rootJ, "jmprMaxDivRange16");
  247. if (jmprMaxDivRange16J)
  248. jmprMaxDivRange16 = json_is_true(jmprMaxDivRange16J);
  249. json_t *jmprMaxDivRange32J = json_object_get(rootJ, "jmprMaxDivRange32");
  250. if (jmprMaxDivRange32J)
  251. jmprMaxDivRange32 = json_is_true(jmprMaxDivRange32J);
  252. json_t *jmprSpreadJ = json_object_get(rootJ, "jmprSpread");
  253. if (jmprSpreadJ)
  254. jmprSpread = json_is_true(jmprSpreadJ);
  255. json_t *jmprAutoResetJ = json_object_get(rootJ, "jmprAutoReset");
  256. if (jmprAutoResetJ)
  257. jmprAutoReset = json_is_true(jmprAutoResetJ);
  258. json_t *tableSetJ = json_object_get(rootJ, "tableSet");
  259. if (tableSetJ)
  260. tableSet = json_integer_value(tableSetJ);
  261. }
  262. }; // End of module (object) definition.
  263. void RKDBRK::step() {
  264. // DSP processing.
  265. // Reading jumpers/switches setting.
  266. jmprGate = (params[JUMPER_GATE].value == 1.0);
  267. jmprGatePrevious = jmprGate;
  268. jmprCountingDown = (params[JUMPER_COUNTINGDOWN].value == 1.0);
  269. // Gate mode only: if "Counting" is changed on the fly, invert firing status for each output jack.
  270. if ((jmprGate) && (jmprCountingDownPrevious != jmprCountingDown))
  271. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  272. bJackIsFired[i] = !bJackIsFired[i];
  273. jmprCountingDownPrevious = jmprCountingDown;
  274. jmprMaxDivRange16 = (params[JUMPER_MAXDIVRANGE16].value == 1.0);
  275. bTableChange = bTableChange || (jmprMaxDivRange16 != jmprMaxDivRange16Previous);
  276. jmprMaxDivRange16Previous = jmprMaxDivRange16;
  277. jmprMaxDivRange32 = (params[JUMPER_MAXDIVRANGE32].value == 1.0);
  278. bTableChange = bTableChange || (jmprMaxDivRange32 != jmprMaxDivRange32Previous);
  279. jmprMaxDivRange32Previous = jmprMaxDivRange32;
  280. jmprSpread = (params[JUMPER_SPREAD].value == 1.0);
  281. if (tableSet == 0)
  282. bTableChange = bTableChange || (jmprSpread != jmprSpreadPrevious); // Spread concerns manufacturer table only. Have no effect on other tables.
  283. jmprSpreadPrevious = jmprSpread;
  284. jmprAutoReset = (params[JUMPER_AUTORESET].value == 1.0);
  285. // Checking if table set was changed via context-menu.
  286. if (!bTableChange)
  287. bTableChange = (tableSetPrev != tableSet);
  288. // Is table change?
  289. if (bTableChange) {
  290. // Yep! assuming table have been changed (either by jumpers/switches setting, or table set via module's context-menu).
  291. if (tableSet == 0) {
  292. // Define new "Mav Div" amount, regardling current "Max-Div-Range 16", "Max-Div-Range 32" and "Spread" jumpers/switches setting.
  293. if (jmprMaxDivRange16 && jmprMaxDivRange32 && jmprSpread)
  294. maxDivAmount = 16; // Max-Div-Range 16 = On, Max-Div-Range 32 = On: Max Div = 8, but Spread On --> Max Div 16.
  295. else if (jmprMaxDivRange16 && jmprMaxDivRange32 && !jmprSpread)
  296. maxDivAmount = 8; // Max-Div-Range 16 = On, Max-Div-Range 32 = On, Spread Off: Max Div = 8 (it's the default factory).
  297. else if (jmprMaxDivRange16 && !jmprMaxDivRange32)
  298. maxDivAmount = 16; // Max-Div-Range 16 = On, Max-Div-Range 32 = Off: Max Div = 16.
  299. else if (!jmprMaxDivRange16 && jmprMaxDivRange32)
  300. maxDivAmount = 32; // Max-Div-Range 16 = Off, Max-Div-Range 32 = On: Max Div = 32.
  301. else maxDivAmount = 64; // Last possible remaining case is... Max-Div-Range 16 = Off, Max-Div-Range 32 = Off: Max Div = 64.
  302. }
  303. else maxDivAmount = 64; // Max Div = 64 for all "extra" tables.
  304. switch (tableSet) {
  305. case 0:
  306. // Now we're defining "future" table, but based on "Manufacturer" table.
  307. if (jmprMaxDivRange16 && jmprMaxDivRange32 && jmprSpread) {
  308. // Max-Div-Range 16 = On, Max-Div-Range 32 = On, Spread = On.
  309. // Special case of "musical" divisions (triplets, 16ths).
  310. tblDividersR0[OUTPUT_1] = 1;
  311. tblDividersR0[OUTPUT_2] = 2;
  312. tblDividersR0[OUTPUT_3] = 3;
  313. tblDividersR0[OUTPUT_4] = 4;
  314. tblDividersR0[OUTPUT_5] = 6;
  315. tblDividersR0[OUTPUT_6] = 8;
  316. tblDividersR0[OUTPUT_7] = 12;
  317. tblDividersR0[OUTPUT_8] = 16;
  318. }
  319. else if (jmprMaxDivRange16 && jmprMaxDivRange32 && !jmprSpread) {
  320. // Max-Div-Range 16 = On, Max-Div-Range 32 = On, Spread = Off (factory setting).
  321. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  322. tblDividersR0[i] = i + 1;
  323. }
  324. else if (jmprMaxDivRange16 && !jmprMaxDivRange32 && jmprSpread) {
  325. // Max-Div-Range 16 is On, Max-Div-Range 32 is Off, Spread is On.
  326. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  327. tblDividersR0[i] = 2 * i + 2;
  328. }
  329. else if (jmprMaxDivRange16 && !jmprMaxDivRange32 && !jmprSpread) {
  330. // Max-Div-Range 16 is On, Max-Div-Range 32 is Off, Spread is Off.
  331. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  332. tblDividersR0[i] = i + 9;
  333. }
  334. else if (!jmprMaxDivRange16 && jmprMaxDivRange32 && jmprSpread) {
  335. // Max-Div-Range 16 is Off, Max-Div-Range 32 is On, Spread is On.
  336. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  337. tblDividersR0[i] = 4 * i + 4;
  338. }
  339. else if (!jmprMaxDivRange16 && jmprMaxDivRange32 && !jmprSpread) {
  340. // Max-Div-Range 16 is Off, Max-Div-Range 32 is On, Spread is Off.
  341. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  342. tblDividersR0[i] = i + 17;
  343. }
  344. else if (!jmprMaxDivRange16 && !jmprMaxDivRange32 && jmprSpread) {
  345. // Max-Div-Range 16 is Off, Max-Div-Range 32 is Off, Spread is On.
  346. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  347. tblDividersR0[i] = 8 * i + 8;
  348. }
  349. else if (!jmprMaxDivRange16 && !jmprMaxDivRange32 && !jmprSpread) {
  350. // Last possibility: Max-Div-Range 16 is Off, Max-Div-Range 32 is Off, Spread is Off.
  351. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  352. tblDividersR0[i] = i + 33;
  353. }
  354. // Now we're defining "future" table.
  355. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  356. tblDividersRt[i] = tblDividersR0[i];
  357. break;
  358. case 1:
  359. // Now we're defining "future" table, but based on prime numbers.
  360. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  361. tblDividersRt[i] = tblPrimes[i];
  362. break;
  363. case 2:
  364. // Now we're defining "future" table, but based on perfect squares.
  365. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  366. tblDividersRt[i] = tblSquares[i];
  367. break;
  368. case 3:
  369. // Now we're defining "future" table, but based on Fibonacci sequence.
  370. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  371. tblDividersRt[i] = tblFibonacci[i];
  372. break;
  373. case 4:
  374. // Now we're defining "future" table, but based on triplet & 16ths.
  375. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  376. tblDividersRt[i] = tblTripletSixteenths[i];
  377. }
  378. // Clearing flag about table change "preparation".
  379. bTableChange = false;
  380. // Arming table rotation (required after table change).
  381. bDoRotation = true;
  382. bDoRotationOnRisingEdge = false;
  383. }
  384. tableSetPrev = tableSet;
  385. // CV ROTATE analysis: is module receive ROTATE voltage?
  386. if (inputs[ROTATE_INPUT].active) {
  387. // CV ROTATE voltage must be between 0V to +5V (inclusive) - otherwise, voltage is clipped.
  388. cvRotate = clamp(inputs[ROTATE_INPUT].value, 0.0f, 5.0f);
  389. }
  390. else cvRotate = 0.0f; // Assuming 0V while ROTATE input port isn't wired.
  391. // "cvRotateTblIndex" is a kind of index to dividers table.
  392. switch (tableSet) {
  393. case 0:
  394. // Manufacturer table is also based on "Max-Div" amount (jumpers J3-J4, or Max Div switches setting on BRK panel).
  395. cvRotateTblIndex = int(cvRotate / 5.0f * (float)(maxDivAmount));
  396. if (cvRotateTblIndex >= maxDivAmount)
  397. cvRotateTblIndex = maxDivAmount - 1; // Possible number of table rotations is based on Max Div amount!
  398. break;
  399. case 1:
  400. // Prime is based on 18 possible values, meaning 11 possible "sliding windows" to get access to 8 (consecutive) prime numbers (one per output jack).
  401. cvRotateTblIndex = int(cvRotate / 5.0f * 11.0f);
  402. if (cvRotateTblIndex >= 11)
  403. cvRotateTblIndex = 10;
  404. break;
  405. case 2:
  406. case 4:
  407. // "Perfect squares" and "Triplet & 16ths" are based on 8 possible values (one per output jack).
  408. cvRotateTblIndex = int(cvRotate / 5.0f * 8.0f);
  409. if (cvRotateTblIndex >= 8)
  410. cvRotateTblIndex = 7;
  411. break;
  412. case 3:
  413. // Fibonacci sequence is based on 8 possible values, 1 possible rotation (first), then 10 possible translations.
  414. cvRotateTblIndex = int(cvRotate / 5.0f * 11.0f);
  415. if (cvRotateTblIndex >= 11)
  416. cvRotateTblIndex = 10;
  417. break;
  418. }
  419. // If table index have changed (or rotation was previously set), rotation is required.
  420. bDoRotation = bDoRotation || (cvRotateTblIndexPrevious != cvRotateTblIndex);
  421. // Is table rotation required?
  422. if (bDoRotation) {
  423. // Clear "preparation" flag.
  424. bDoRotation = false;
  425. // Table rotation is required. Set (arm) another/next flag, by this way, real rotation will occur on next CLK rising-edge.
  426. bDoRotationOnRisingEdge = true;
  427. // Depending table set (Manufacturer, Prime numbers, Perfect squares, or Fibonacci sequence).
  428. switch (tableSet) {
  429. case 0:
  430. // Manufacturer table.
  431. if (jmprMaxDivRange16 && jmprMaxDivRange32 && jmprSpread) {
  432. // Particular case when Max-Divide-Amount is 8 by jumpers/switches --AND-- Spread is On...
  433. // In this special case, all ports will output standard "musical" divisions of 16ths & triplets.
  434. // We're using "cell moves" (like a shorting routine will do).
  435. // Firstly, duplicating base "R+0" table...
  436. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  437. tblDividersRt[i] = tblDividersR0[i];
  438. // ...then doing "j" moves (from bottom to top).
  439. for (int j = 0; j < cvRotateTblIndex; j++) {
  440. // Saving OUTPUT 1.
  441. tblDividersRt[8] = tblDividersRt[OUTPUT_1];
  442. tblDividersRt[OUTPUT_1] = tblDividersRt[OUTPUT_2];
  443. tblDividersRt[OUTPUT_2] = tblDividersRt[OUTPUT_3];
  444. tblDividersRt[OUTPUT_3] = tblDividersRt[OUTPUT_4];
  445. tblDividersRt[OUTPUT_4] = tblDividersRt[OUTPUT_5];
  446. tblDividersRt[OUTPUT_5] = tblDividersRt[OUTPUT_6];
  447. tblDividersRt[OUTPUT_6] = tblDividersRt[OUTPUT_7];
  448. tblDividersRt[OUTPUT_7] = tblDividersRt[OUTPUT_8];
  449. // Previously saved OUTPUT 1 is restored to... OUTPUT 8!
  450. tblDividersRt[OUTPUT_8] = tblDividersRt[8];
  451. }
  452. }
  453. else {
  454. // All other cases are standard shifting.
  455. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++) {
  456. // Duplicating "R+0" reference table, then adding number of rotation(s).
  457. tblDividersRt[i] = tblDividersR0[i] + cvRotateTblIndex;
  458. if (tblDividersRt[i] > maxDivAmount) {
  459. // Applying "modulo" if necessary!
  460. tblDividersRt[i] = (tblDividersRt[i] % maxDivAmount);
  461. }
  462. }
  463. }
  464. break;
  465. case 1:
  466. // Prime numbers-based table.
  467. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  468. tblDividersRt[i] = tblPrimes[i + cvRotateTblIndex];
  469. break;
  470. case 2:
  471. case 4:
  472. // "Perfect squares" or "Triplet & 16ths" based table.
  473. // Firstly, duplicating perfect squares table...
  474. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  475. if (tableSet == 2)
  476. tblDividersRt[i] = tblSquares[i];
  477. else tblDividersRt[i] = tblTripletSixteenths[i];
  478. // ...then doing "j" moves (from bottom to top).
  479. for (int j = 0; j < cvRotateTblIndex; j++) {
  480. // Saving OUTPUT 1.
  481. tblDividersRt[8] = tblDividersRt[OUTPUT_1];
  482. tblDividersRt[OUTPUT_1] = tblDividersRt[OUTPUT_2];
  483. tblDividersRt[OUTPUT_2] = tblDividersRt[OUTPUT_3];
  484. tblDividersRt[OUTPUT_3] = tblDividersRt[OUTPUT_4];
  485. tblDividersRt[OUTPUT_4] = tblDividersRt[OUTPUT_5];
  486. tblDividersRt[OUTPUT_5] = tblDividersRt[OUTPUT_6];
  487. tblDividersRt[OUTPUT_6] = tblDividersRt[OUTPUT_7];
  488. tblDividersRt[OUTPUT_7] = tblDividersRt[OUTPUT_8];
  489. // Previously saved OUTPUT 1 is restored to... OUTPUT 8!
  490. tblDividersRt[OUTPUT_8] = tblDividersRt[8];
  491. }
  492. break;
  493. case 3:
  494. // Fibonacci-based table.
  495. // Firstly, duplicating Fibonacci table.
  496. if (cvRotateTblIndex < 2) {
  497. // No rotation use 1, 2, 3, 5, 8, 13, 21, 34.
  498. // First rotation uses 2, 3, 5, 8, 13, 21, 34, 55.
  499. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  500. tblDividersRt[i] = tblFibonacci[i + cvRotateTblIndex];
  501. }
  502. else {
  503. // Next rotations are based on 2, 3, 5, 8, 13, 21, 34, 55, then applying +R shift.
  504. // On second and later rotation, then applying "+R" on all ports.
  505. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  506. tblDividersRt[i] = tblFibonacci[i + 1] + cvRotateTblIndex - 1;
  507. }
  508. }
  509. }
  510. // By default assuming this step isn't a CLK rising edge.
  511. bIsRisingEdge = false;
  512. // By default assuming this step isn't a CLK falling edge.
  513. bIsFallingEdge = false;
  514. // Module is running (enabled) as long as its CLK input jack is wired.
  515. bCLKisActive = inputs[CLK_INPUT].active;
  516. if (!bCLKisActive) {
  517. // Module in timeout (idle) mode.
  518. ModuleTimeOut();
  519. // CLK isn't connected: display "--" in all segment-LED displays (using -1).
  520. strcpy(dispDiv1, "--");
  521. strcpy(dispDiv2, "--");
  522. strcpy(dispDiv3, "--");
  523. strcpy(dispDiv4, "--");
  524. strcpy(dispDiv5, "--");
  525. strcpy(dispDiv6, "--");
  526. strcpy(dispDiv7, "--");
  527. strcpy(dispDiv8, "--");
  528. }
  529. else {
  530. // CLK input port is wired.
  531. // Update segment-LEDs to display the eight dividers alongside their jacks.
  532. // Based on "future" table!
  533. snprintf(dispDiv1, sizeof(dispDiv1), "%2i", tblDividersRt[OUTPUT_1]);
  534. snprintf(dispDiv2, sizeof(dispDiv2), "%2i", tblDividersRt[OUTPUT_2]);
  535. snprintf(dispDiv3, sizeof(dispDiv3), "%2i", tblDividersRt[OUTPUT_3]);
  536. snprintf(dispDiv4, sizeof(dispDiv4), "%2i", tblDividersRt[OUTPUT_4]);
  537. snprintf(dispDiv5, sizeof(dispDiv5), "%2i", tblDividersRt[OUTPUT_5]);
  538. snprintf(dispDiv6, sizeof(dispDiv6), "%2i", tblDividersRt[OUTPUT_6]);
  539. snprintf(dispDiv7, sizeof(dispDiv7), "%2i", tblDividersRt[OUTPUT_7]);
  540. snprintf(dispDiv8, sizeof(dispDiv8), "%2i", tblDividersRt[OUTPUT_8]);
  541. // Increment step number.
  542. currentStep++;
  543. // Using Schmitt trigger (SchmittTrigger is provided by dsp/digital.hpp) to detect triggers on CLK input jack.
  544. if (CLK_Port.process(rescale(inputs[CLK_INPUT].value, 0.2f, 3.5f, 0.0f, 1.0f))) {
  545. // It's a rising edge.
  546. bIsRisingEdge = true;
  547. // Disarm timeout flag.
  548. bCLKTimeOut = false;
  549. // CLK input is receiving a compliant trigger voltage (trigger on rising edge).
  550. // If rotation was requested, it becomes effective on received rising edge. Set the new current dividers table.
  551. if (bDoRotationOnRisingEdge) {
  552. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  553. tblActiveDividers[i] = tblDividersRt[i];
  554. // Define adaptative "Max Div" amount, but only for "Primes numbers" table!
  555. // "Max Div" can be 32 or 64, depending highest divider.
  556. if (tableSet == 1) {
  557. if (tblActiveDividers[7] > 32)
  558. maxDivAmount = 64;
  559. else maxDivAmount = 32;
  560. }
  561. // Rotation was done.
  562. bDoRotationOnRisingEdge = false;
  563. }
  564. // Frequency of source CLK.
  565. if (previousStep == 0) {
  566. // Source CLK frequency is unknown.
  567. bCLKFreqKnown = false;
  568. expectedStep = 0;
  569. }
  570. else {
  571. // But perhaps this rising edge comes too early (source frequency is increased).
  572. if (bCLKFreqKnown) {
  573. if (currentStep != expectedStep) {
  574. bCLKFreqKnown = false;
  575. expectedStep = 0;
  576. currentStep = 1;
  577. }
  578. else {
  579. // Source CLK frequency is stable.
  580. bCLKFreqKnown = true;
  581. expectedStep = currentStep - previousStep + currentStep;
  582. }
  583. }
  584. else {
  585. // Source CLK frequency was unknow at previous rising edge, but for now it can be established on this (next) rising edge.
  586. bCLKFreqKnown = true;
  587. expectedStep = currentStep - previousStep + currentStep;
  588. }
  589. }
  590. // Of course, on rising edgen the CLK signal is high!
  591. bCLKisHigh = true;
  592. // ...and this current step (on rising edge) becomes... previous step, for next rising edge detection!
  593. previousStep = currentStep;
  594. }
  595. else {
  596. // At this point it's not a rising edge (maybe incoming signal is already at high state, or low, or a falling edge).
  597. // Is it a falling edge?
  598. if (bCLKisHigh && (clamp(inputs[CLK_INPUT].value, 0.0f, 15.0f) < 0.2f)) {
  599. // At previous step it was high, but now is low, meaning this step is a falling edge.
  600. bCLKisHigh = false; // Below 2V, disarm the flag to stop counting.
  601. // It's a falling edge.
  602. bIsFallingEdge = true;
  603. }
  604. // Also, be sure the CLK source frequency wasn't lower (slower signal).
  605. if (expectedStep != 0) {
  606. if (currentStep >= expectedStep) {
  607. // CLK frequency is lower (slower), or... no more signal (kind of "timeout").
  608. if (bCLKFreqKnown) {
  609. // If the frequency was previously known, we give an extra delay prior timeout.
  610. expectedStep = currentStep - previousStep + currentStep; // Give an extra delay prior timeout.
  611. bCLKFreqKnown = false;
  612. }
  613. else ModuleTimeOut(); // Timeout: module becomes "idle".
  614. }
  615. }
  616. }
  617. }
  618. // "CLK" white LED state.
  619. lights[LED_CLK].value = bCLKisHigh ? 1.0f : 0.0f;
  620. // Using Schmitt trigger (SchmittTrigger is provided by dsp/digital.hpp) to register incoming trigger signal on RESET jack (anytime).
  621. if (!bRegisteredResetOnJack)
  622. bRegisteredResetOnJack = RESET_Port.process(rescale(inputs[RESET_INPUT].value, 0.2f, 3.5f, 0.0f, 1.0f));
  623. // Registered RESET on jack becomes effective on next incoming rising edge.
  624. if (bIsRisingEdge) {
  625. // Clearing "Auto-Reset" flag.
  626. bIsAutoReset = false;
  627. // Is RESET jack was triggered?
  628. bResetOnJack = bRegisteredResetOnJack;
  629. bRegisteredResetOnJack = false;
  630. // Global dividers counters reset for all output jacks, due to received pulse on "RESET" jack.
  631. if (bResetOnJack) {
  632. // Reset Schmitt trigger used by RESET input jack.
  633. RESET_Port.reset();
  634. // This will "force" disabling RESET LED afterglow, in order to lit orange LED.
  635. ledResetAfterglow = 0;
  636. // Reset dividers counters.
  637. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  638. divCounters[i] = 0;
  639. // Temporary inhibit Auto-Reset.
  640. bAllowAutoReset = false;
  641. // Restart Auto-Reset sequence (counter).
  642. divCountersAutoReset = 0;
  643. }
  644. }
  645. else bResetOnJack = false;
  646. // Determine initial pulsing state, only on early rising edge!
  647. if (bIsEarlyRisingEdge)
  648. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++)
  649. bJackIsFired[i] = !jmprCountingDown;
  650. // Auto-reset and pulsing management (for each output jack).
  651. for (int i = OUTPUT_1; i < NUM_OUTPUTS; i++) {
  652. if (bIsRisingEdge) {
  653. // On rising edge.
  654. // Is the "Auto-Reset", for this current jack, must be applied first, or not?
  655. // "Auto-Reset" may occurs only if "Auto-Reset" jumper/switch is On, not temporary disabled, and Auto-Reset counter is 0, and for certain dividers.
  656. if ((jmprAutoReset) && (bAllowAutoReset) && (divCountersAutoReset == 0) && (((2 * maxDivAmount) % tblActiveDividers[i]) != 0)) {
  657. divCounters[i] = 0;
  658. bIsAutoReset = true;
  659. bAutoResetLEDfired = true;
  660. }
  661. // Pulse generators.
  662. if (jmprGate) {
  663. // Gate modes.
  664. if ((tblActiveDividers[i] % 2) == 0) {
  665. // On all even dividers...
  666. if (divCounters[i] % (tblActiveDividers[i] / 2) == 0)
  667. pulseOutputJack(i, !bJackIsFired[i]); // Invert state of pulse.
  668. }
  669. else {
  670. // On all odd dividers...
  671. // /1 in (gate modes) must be considered differently, in fact like... trigger mode! (TBC).
  672. if (tblActiveDividers[i] == 1)
  673. pulseOutputJack(i, true); // Degraded" pulse while source frequency isn't stable.
  674. else if ((divCounters[i] % tblActiveDividers[i]) == 0)
  675. pulseOutputJack(i, !bJackIsFired[i]); // Invert state of pulse.
  676. }
  677. }
  678. else {
  679. // Trigger modes (default).
  680. if (jmprCountingDown)
  681. pulseOutputJack(i, (divCounters[i] % tblActiveDividers[i]) == 0); // Counting down mode.
  682. else pulseOutputJack(i, ((divCounters[i] + 1) % tblActiveDividers[i]) == 0); // Counting up mode (default).
  683. }
  684. // Advances next divider counter for this jack.
  685. // Increment divider counter...
  686. divCounters[i]++;
  687. // ...and restart to 0 when division value (for current jack) is reached (or over).
  688. if (divCounters[i] >= tblActiveDividers[i])
  689. divCounters[i] = 0;
  690. }
  691. else if (bIsFallingEdge) {
  692. // On falling edge (only).
  693. if (jmprGate) {
  694. // Gate mode.
  695. if ((tblActiveDividers[i] % 2) == 1) {
  696. if (((divCounters[i] + ((tblActiveDividers[i] - 1) / 2)) % tblActiveDividers[i]) == 0)
  697. pulseOutputJack(i, !bJackIsFired[i]); // Invert state of pulse.
  698. }
  699. }
  700. else pulseOutputJack(i, false); // Trigger mode: always stop pulsing on falling edge (for any divider).
  701. }
  702. }
  703. // Advance "Auto-Reset" counter (global, on rising edges only).
  704. if (bIsRisingEdge) {
  705. // Increment "Auto-Reset" sequence counter...
  706. divCountersAutoReset++;
  707. // ...and restart to 0 when "2 x Max-Div" is reached.
  708. if ((divCountersAutoReset % (2 * maxDivAmount)) == 0)
  709. divCountersAutoReset = 0;
  710. // Now next rising edge aren't first rising edge.
  711. bIsEarlyRisingEdge = false;
  712. // Allow next Auto-Reset events.
  713. bAllowAutoReset = true;
  714. }
  715. else bResetOnJack = false;
  716. // "RESET" small red LED management (having afterglow).
  717. if (ledResetAfterglow > 0) {
  718. ledResetAfterglow--;
  719. }
  720. else {
  721. if ((bIsAutoReset && bAutoResetLEDfired) || bResetOnJack || bCLKTimeOut) {
  722. if (bCLKTimeOut) {
  723. // Highest priority LED.
  724. // Setup counter for red LED afterglow.
  725. ledResetAfterglow = round(engineGetSampleRate() / 4);
  726. // Lit "RESET" LED (red) on CLK timeout.
  727. lights[LED_RESET_RED].value = 1.0f;
  728. lights[LED_RESET_ORANGE].value = 0.0f;
  729. lights[LED_RESET_BLUE].value = 0.0f;
  730. }
  731. else if (bResetOnJack) {
  732. // Setup counter for orange LED afterglow.
  733. ledResetAfterglow = round(engineGetSampleRate() / 6);
  734. // Lit "RESET" LED (orange) on RESET via jack.
  735. lights[LED_RESET_RED].value = 0.0f;
  736. lights[LED_RESET_ORANGE].value = 1.0f;
  737. lights[LED_RESET_BLUE].value = 0.0f;
  738. }
  739. else {
  740. // Setup counter for blue LED afterglow.
  741. ledResetAfterglow = round(engineGetSampleRate() / 8);
  742. // Lit "RESET" LED (blue) on "Auto-Reset" event.
  743. lights[LED_RESET_RED].value = 0.0f;
  744. lights[LED_RESET_ORANGE].value = 0.0f;
  745. lights[LED_RESET_BLUE].value = 1.0f;
  746. bAutoResetLEDfired = false;
  747. }
  748. }
  749. else {
  750. // Unlit "RESET" LEDs.
  751. lights[LED_RESET_RED].value = 0.0f;
  752. lights[LED_RESET_ORANGE].value = 0.0f;
  753. lights[LED_RESET_BLUE].value = 0.0f;
  754. }
  755. }
  756. // Update current rotation index to become "previous". This will be useful to detect possible "table rotation" on next step.
  757. cvRotateTblIndexPrevious = cvRotateTblIndex;
  758. } // End of DSP processing...
  759. // Segment-LED displays.
  760. struct RKDBRK_Displays : TransparentWidget {
  761. RKDBRK *module;
  762. std::shared_ptr<Font> font;
  763. RKDBRK_Displays() {
  764. font = Font::load(assetPlugin(plugin, "res/fonts/digital-readout.medium.ttf"));
  765. }
  766. void updateD1(NVGcontext *vg, Vec pos, char* dispDiv1) {
  767. nvgFontSize(vg, 14);
  768. nvgFontFaceId(vg, font->handle);
  769. nvgTextLetterSpacing(vg, -1);
  770. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  771. nvgText(vg, pos.x + 16, pos.y + 29.5, dispDiv1, NULL);
  772. }
  773. void updateD2(NVGcontext *vg, Vec pos, char* dispDiv2) {
  774. nvgFontSize(vg, 14);
  775. nvgFontFaceId(vg, font->handle);
  776. nvgTextLetterSpacing(vg, -1);
  777. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  778. nvgText(vg, pos.x + 16, pos.y + 59.5, dispDiv2, NULL);
  779. }
  780. void updateD3(NVGcontext *vg, Vec pos, char* dispDiv3) {
  781. nvgFontSize(vg, 14);
  782. nvgFontFaceId(vg, font->handle);
  783. nvgTextLetterSpacing(vg, -1);
  784. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  785. nvgText(vg, pos.x + 16, pos.y + 89.5, dispDiv3, NULL);
  786. }
  787. void updateD4(NVGcontext *vg, Vec pos, char* dispDiv4) {
  788. nvgFontSize(vg, 14);
  789. nvgFontFaceId(vg, font->handle);
  790. nvgTextLetterSpacing(vg, -1);
  791. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  792. nvgText(vg, pos.x + 16, pos.y + 119.5, dispDiv4, NULL);
  793. }
  794. void updateD5(NVGcontext *vg, Vec pos, char* dispDiv5) {
  795. nvgFontSize(vg, 14);
  796. nvgFontFaceId(vg, font->handle);
  797. nvgTextLetterSpacing(vg, -1);
  798. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  799. nvgText(vg, pos.x + 16, pos.y + 149.5, dispDiv5, NULL);
  800. }
  801. void updateD6(NVGcontext *vg, Vec pos, char* dispDiv6) {
  802. nvgFontSize(vg, 14);
  803. nvgFontFaceId(vg, font->handle);
  804. nvgTextLetterSpacing(vg, -1);
  805. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  806. nvgText(vg, pos.x + 16, pos.y + 179.5, dispDiv6, NULL);
  807. }
  808. void updateD7(NVGcontext *vg, Vec pos, char* dispDiv7) {
  809. nvgFontSize(vg, 14);
  810. nvgFontFaceId(vg, font->handle);
  811. nvgTextLetterSpacing(vg, -1);
  812. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  813. nvgText(vg, pos.x + 16, pos.y + 209.5, dispDiv7, NULL);
  814. }
  815. void updateD8(NVGcontext *vg, Vec pos, char* dispDiv8) {
  816. nvgFontSize(vg, 14);
  817. nvgFontFaceId(vg, font->handle);
  818. nvgTextLetterSpacing(vg, -1);
  819. nvgFillColor(vg, nvgTransRGBA(nvgRGB(0x6c, 0xff, 0xff), 0xff));
  820. nvgText(vg, pos.x + 16, pos.y + 239.5, dispDiv8, NULL);
  821. }
  822. void draw(NVGcontext *vg) override {
  823. updateD1(vg, Vec(0, box.size.y - 150), module->dispDiv1);
  824. updateD2(vg, Vec(0, box.size.y - 150), module->dispDiv2);
  825. updateD3(vg, Vec(0, box.size.y - 150), module->dispDiv3);
  826. updateD4(vg, Vec(0, box.size.y - 150), module->dispDiv4);
  827. updateD5(vg, Vec(0, box.size.y - 150), module->dispDiv5);
  828. updateD6(vg, Vec(0, box.size.y - 150), module->dispDiv6);
  829. updateD7(vg, Vec(0, box.size.y - 150), module->dispDiv7);
  830. updateD8(vg, Vec(0, box.size.y - 150), module->dispDiv8);
  831. }
  832. };
  833. struct RKDBRKWidget : ModuleWidget {
  834. RKDBRKWidget(RKDBRK *module);
  835. // Action for "Randomize", from context-menu, is (for now) bypassed.
  836. void randomize() override {
  837. };
  838. // RKDBRK module uses a custom context-menu, to access to different table sets.
  839. Menu* createContextMenu() override;
  840. };
  841. RKDBRKWidget::RKDBRKWidget(RKDBRK *module) : ModuleWidget(module) {
  842. box.size = Vec(8 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  843. {
  844. SVGPanel *panel = new SVGPanel();
  845. panel->setBackground(SVG::load(assetPlugin(plugin, "res/RKDBRK.svg")));
  846. panel->box.size = box.size;
  847. addChild(panel);
  848. }
  849. // Using four screws configuration for 8 HP module.
  850. // Top-left screw.
  851. addChild(Widget::create<Torx_Silver>(Vec(RACK_GRID_WIDTH, 0)));
  852. // Top-right screw.
  853. addChild(Widget::create<Torx_Silver>(Vec(box.size.x - 3 * RACK_GRID_WIDTH, 0)));
  854. // Bottom-left screw.
  855. addChild(Widget::create<Torx_Silver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  856. // Bottom-right screw.
  857. addChild(Widget::create<Torx_Silver>(Vec(box.size.x - 3 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  858. // Segment-LED displays.
  859. {
  860. RKDBRK_Displays *display = new RKDBRK_Displays();
  861. display->module = module;
  862. display->box.pos = Vec(0, 0);
  863. display->box.size = Vec(box.size.x, 234);
  864. addChild(display);
  865. }
  866. // Input jack: ROTATE.
  867. addInput(Port::create<CL1362_In_RR>(Vec(2.4, 35), Port::INPUT, module, RKDBRK::ROTATE_INPUT));
  868. // Input jack: RESET.
  869. addInput(Port::create<CL1362_In_RR>(Vec(32.2, 35), Port::INPUT, module, RKDBRK::RESET_INPUT));
  870. // Input jack: CLK.
  871. addInput(Port::create<CL1362_In>(Vec(30.8, 66), Port::INPUT, module, RKDBRK::CLK_INPUT));
  872. // Output jack: 1+R.
  873. addOutput(Port::create<CL1362_Out>(Vec(30.8, 96), Port::OUTPUT, module, RKDBRK::OUTPUT_1));
  874. // Output jack: 2+R.
  875. addOutput(Port::create<CL1362_Out>(Vec(30.8, 126), Port::OUTPUT, module, RKDBRK::OUTPUT_2));
  876. // Output jack: 3+R.
  877. addOutput(Port::create<CL1362_Out>(Vec(30.8, 156), Port::OUTPUT, module, RKDBRK::OUTPUT_3));
  878. // Output jack: 4+R.
  879. addOutput(Port::create<CL1362_Out>(Vec(30.8, 186), Port::OUTPUT, module, RKDBRK::OUTPUT_4));
  880. // Output jack: 5+R.
  881. addOutput(Port::create<CL1362_Out>(Vec(30.8, 216), Port::OUTPUT, module, RKDBRK::OUTPUT_5));
  882. // Output jack: 6+R.
  883. addOutput(Port::create<CL1362_Out>(Vec(30.8, 246), Port::OUTPUT, module, RKDBRK::OUTPUT_6));
  884. // Output jack: 7+R.
  885. addOutput(Port::create<CL1362_Out>(Vec(30.8, 276), Port::OUTPUT, module, RKDBRK::OUTPUT_7));
  886. // Output jack: 8+R.
  887. addOutput(Port::create<CL1362_Out>(Vec(30.8, 306), Port::OUTPUT, module, RKDBRK::OUTPUT_8));
  888. // White LED for CLK.
  889. addChild(ModuleLightWidget::create<MediumLight<RKDWhiteLight>>(Vec(3.7, 73.4), module, RKDBRK::LED_CLK));
  890. // Red LED for output 1+R.
  891. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(3.7, 103.4), module, RKDBRK::LED_OUT_1));
  892. // Orange LED for output 2+R.
  893. addChild(ModuleLightWidget::create<MediumLight<RKDOrangeLight>>(Vec(3.7, 133.4), module, RKDBRK::LED_OUT_2));
  894. // Yellow LED for output 3+R.
  895. addChild(ModuleLightWidget::create<MediumLight<YellowLight>>(Vec(3.7, 163.4), module, RKDBRK::LED_OUT_3));
  896. // Green LED for output 4+R.
  897. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(3.7, 193.4), module, RKDBRK::LED_OUT_4));
  898. // Green LED for output 5+R.
  899. addChild(ModuleLightWidget::create<MediumLight<GreenLight>>(Vec(3.7, 223.4), module, RKDBRK::LED_OUT_5));
  900. // Blue (cyan) LED for output 6+R.
  901. addChild(ModuleLightWidget::create<MediumLight<BlueLight>>(Vec(3.7, 253.4), module, RKDBRK::LED_OUT_6));
  902. // Purple LED for output 7+R.
  903. addChild(ModuleLightWidget::create<MediumLight<RKDPurpleLight>>(Vec(3.7, 283.4), module, RKDBRK::LED_OUT_7));
  904. // White LED for output 8+R.
  905. addChild(ModuleLightWidget::create<MediumLight<RKDWhiteLight>>(Vec(3.7, 313.4), module, RKDBRK::LED_OUT_8));
  906. // Small red LED near RESET input jack (tri-colored: red, orange or blue).
  907. addChild(ModuleLightWidget::create<SmallLight<RedOrangeBlueLight>>(Vec(50.8, 33.2), module, RKDBRK::LED_RESET_RED));
  908. // Switch "Counting Up/Down". By default Off (counting up).
  909. addParam(ParamWidget::create<RKDBRK_Switch>(Vec(70.3, 64.2), module, RKDBRK::JUMPER_COUNTINGDOWN, 0.0, 1.0, 0.0));
  910. // Switch "Trig/Gate". By default Off (trigger).
  911. addParam(ParamWidget::create<RKDBRK_Switch>(Vec(70.3, 112.2), module, RKDBRK::JUMPER_GATE, 0.0, 1.0, 0.0));
  912. // Switch "Max-Div-Range 16". By default On. Working together with "Max-Div-Range 32".
  913. addParam(ParamWidget::create<RKDBRK_Switch>(Vec(70.3, 160.2), module, RKDBRK::JUMPER_MAXDIVRANGE16, 0.0, 1.0, 1.0));
  914. // Switch "Max-Div-Range 32". By default On. Working together with "Max-Div-Range 16".
  915. addParam(ParamWidget::create<RKDBRK_Switch>(Vec(70.3, 206.2), module, RKDBRK::JUMPER_MAXDIVRANGE32, 0.0, 1.0, 1.0));
  916. // Switch "Spread Off/On". By default Off (spread off).
  917. addParam(ParamWidget::create<RKDBRK_Switch>(Vec(70.3, 256.2), module, RKDBRK::JUMPER_SPREAD, 0.0, 1.0, 0.0));
  918. // Switch "Auto-Reset Off/On". By default Off (auto-reset is disabled).
  919. addParam(ParamWidget::create<RKDBRK_Switch>(Vec(70.3, 304.2), module, RKDBRK::JUMPER_AUTORESET, 0.0, 1.0, 0.0));
  920. }
  921. // Context-menu entry routine: select "RKD (factory)" table.
  922. struct RKDBRKManufacturerItem : MenuItem {
  923. RKDBRK *rkdbrk;
  924. void onAction(EventAction &e) override {
  925. rkdbrk->tableSet = 0;
  926. }
  927. void step() override {
  928. rightText = (rkdbrk->tableSet == 0) ? "✔" : "";
  929. MenuItem::step();
  930. }
  931. };
  932. // Context-menu entry routine: select "Prime numbers" table.
  933. struct RKDBRKPrimesItem : MenuItem {
  934. RKDBRK *rkdbrk;
  935. void onAction(EventAction &e) override {
  936. rkdbrk->tableSet = 1;
  937. }
  938. void step() override {
  939. rightText = (rkdbrk->tableSet == 1) ? "✔" : "";
  940. MenuItem::step();
  941. }
  942. };
  943. // Context-menu entry routine: select "Perfect squares" table.
  944. struct RKDBRKSquaresItem : MenuItem {
  945. RKDBRK *rkdbrk;
  946. void onAction(EventAction &e) override {
  947. rkdbrk->tableSet = 2;
  948. }
  949. void step() override {
  950. rightText = (rkdbrk->tableSet == 2) ? "✔" : "";
  951. MenuItem::step();
  952. }
  953. };
  954. // Context-menu entry routine: select "Fibonacci sequence" table.
  955. struct RKDBRKFibonacciItem : MenuItem {
  956. RKDBRK *rkdbrk;
  957. void onAction(EventAction &e) override {
  958. rkdbrk->tableSet = 3;
  959. }
  960. void step() override {
  961. rightText = (rkdbrk->tableSet == 3) ? "✔" : "";
  962. MenuItem::step();
  963. }
  964. };
  965. // Context-menu entry routine: select "Triplet & 16ths" table.
  966. struct RKDBRKTripletSixteenthsItem : MenuItem {
  967. RKDBRK *rkdbrk;
  968. void onAction(EventAction &e) override {
  969. rkdbrk->tableSet = 4;
  970. }
  971. void step() override {
  972. rightText = (rkdbrk->tableSet == 4) ? "✔" : "";
  973. MenuItem::step();
  974. }
  975. };
  976. // CONTEXT-MENU CONSTRUCTION.
  977. Menu* RKDBRKWidget::createContextMenu() {
  978. Menu* menu = ModuleWidget::createContextMenu();
  979. RKDBRK *rkdbrk = dynamic_cast<RKDBRK*>(module);
  980. assert(rkdbrk);
  981. menu->addChild(construct<MenuEntry>());
  982. menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Dividers table:"));
  983. menu->addChild(construct<RKDBRKManufacturerItem>(&RKDBRKManufacturerItem::text, "Manufacturer", &RKDBRKManufacturerItem::rkdbrk, rkdbrk));
  984. menu->addChild(construct<RKDBRKPrimesItem>(&RKDBRKPrimesItem::text, "Prime numbers", &RKDBRKPrimesItem::rkdbrk, rkdbrk));
  985. menu->addChild(construct<RKDBRKSquaresItem>(&RKDBRKSquaresItem::text, "Perfect squares", &RKDBRKSquaresItem::rkdbrk, rkdbrk));
  986. menu->addChild(construct<RKDBRKFibonacciItem>(&RKDBRKFibonacciItem::text, "Fibonacci sequence", &RKDBRKFibonacciItem::rkdbrk, rkdbrk));
  987. menu->addChild(construct<RKDBRKTripletSixteenthsItem>(&RKDBRKTripletSixteenthsItem::text, "Triplet & 16ths", &RKDBRKTripletSixteenthsItem::rkdbrk, rkdbrk));
  988. return menu;
  989. }
  990. } // namespace rack_plugin_Ohmer
  991. using namespace rack_plugin_Ohmer;
  992. RACK_PLUGIN_MODEL_INIT(Ohmer, RKDBRK) {
  993. Model *modelRKDBRK = Model::create<RKDBRK, RKDBRKWidget>("Ohmer Modules", "RKDBRK", "RKD with \"Break\"", CLOCK_MODULATOR_TAG);
  994. return modelRKDBRK;
  995. }