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.

482 lines
14KB

  1. #include "Computerscare.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "dsp/filter.hpp"
  4. #include <string>
  5. #include <sstream>
  6. #include <iomanip>
  7. namespace rack_plugin_computerscare {
  8. struct ComputerscarePatchSequencer : Module {
  9. enum ParamIds {
  10. STEPS_PARAM,
  11. MANUAL_CLOCK_PARAM,
  12. EDIT_PARAM,
  13. EDIT_PREV_PARAM,
  14. ENUMS(SWITCHES,100),
  15. NUM_PARAMS
  16. };
  17. enum InputIds {
  18. TRG_INPUT,
  19. ENUMS(INPUT_JACKS, 10),
  20. RANDOMIZE_INPUT,
  21. NUM_INPUTS
  22. };
  23. enum OutputIds {
  24. OUTPUTS,
  25. NUM_OUTPUTS = OUTPUTS + 10
  26. };
  27. enum LightIds {
  28. SWITCH_LIGHTS,
  29. NUM_LIGHTS = SWITCH_LIGHTS + 200
  30. };
  31. SchmittTrigger switch_triggers[10][10];
  32. SchmittTrigger nextAddressRead;
  33. SchmittTrigger nextAddressEdit;
  34. SchmittTrigger prevAddressEdit;
  35. SchmittTrigger clockTrigger;
  36. SchmittTrigger randomizeTrigger;
  37. int address = 0;
  38. int editAddress = 0;
  39. int addressPlusOne = 1;
  40. int editAddressPlusOne = 1;
  41. int numAddresses = 2;
  42. bool switch_states[16][10][10] =
  43. {{{0,0,0,0,0,0,0,0,0,0},
  44. {0,0,0,0,0,0,0,0,0,0},
  45. {0,0,0,0,0,0,0,0,0,0},
  46. {0,0,0,0,0,0,0,0,0,0},
  47. {0,0,0,0,0,0,0,0,0,0},
  48. {0,0,0,0,0,0,0,0,0,0},
  49. {0,0,0,0,0,0,0,0,0,0},
  50. {0,0,0,0,0,0,0,0,0,0},
  51. {0,0,0,0,0,0,0,0,0,0},
  52. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  53. {0,0,0,0,0,0,0,0,0,0},
  54. {0,0,0,0,0,0,0,0,0,0},
  55. {0,0,0,0,0,0,0,0,0,0},
  56. {0,0,0,0,0,0,0,0,0,0},
  57. {0,0,0,0,0,0,0,0,0,0},
  58. {0,0,0,0,0,0,0,0,0,0},
  59. {0,0,0,0,0,0,0,0,0,0},
  60. {0,0,0,0,0,0,0,0,0,0},
  61. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  62. {0,0,0,0,0,0,0,0,0,0},
  63. {0,0,0,0,0,0,0,0,0,0},
  64. {0,0,0,0,0,0,0,0,0,0},
  65. {0,0,0,0,0,0,0,0,0,0},
  66. {0,0,0,0,0,0,0,0,0,0},
  67. {0,0,0,0,0,0,0,0,0,0},
  68. {0,0,0,0,0,0,0,0,0,0},
  69. {0,0,0,0,0,0,0,0,0,0},
  70. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  71. {0,0,0,0,0,0,0,0,0,0},
  72. {0,0,0,0,0,0,0,0,0,0},
  73. {0,0,0,0,0,0,0,0,0,0},
  74. {0,0,0,0,0,0,0,0,0,0},
  75. {0,0,0,0,0,0,0,0,0,0},
  76. {0,0,0,0,0,0,0,0,0,0},
  77. {0,0,0,0,0,0,0,0,0,0},
  78. {0,0,0,0,0,0,0,0,0,0},
  79. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  80. {0,0,0,0,0,0,0,0,0,0},
  81. {0,0,0,0,0,0,0,0,0,0},
  82. {0,0,0,0,0,0,0,0,0,0},
  83. {0,0,0,0,0,0,0,0,0,0},
  84. {0,0,0,0,0,0,0,0,0,0},
  85. {0,0,0,0,0,0,0,0,0,0},
  86. {0,0,0,0,0,0,0,0,0,0},
  87. {0,0,0,0,0,0,0,0,0,0},
  88. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  89. {0,0,0,0,0,0,0,0,0,0},
  90. {0,0,0,0,0,0,0,0,0,0},
  91. {0,0,0,0,0,0,0,0,0,0},
  92. {0,0,0,0,0,0,0,0,0,0},
  93. {0,0,0,0,0,0,0,0,0,0},
  94. {0,0,0,0,0,0,0,0,0,0},
  95. {0,0,0,0,0,0,0,0,0,0},
  96. {0,0,0,0,0,0,0,0,0,0},
  97. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  98. {0,0,0,0,0,0,0,0,0,0},
  99. {0,0,0,0,0,0,0,0,0,0},
  100. {0,0,0,0,0,0,0,0,0,0},
  101. {0,0,0,0,0,0,0,0,0,0},
  102. {0,0,0,0,0,0,0,0,0,0},
  103. {0,0,0,0,0,0,0,0,0,0},
  104. {0,0,0,0,0,0,0,0,0,0},
  105. {0,0,0,0,0,0,0,0,0,0},
  106. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  107. {0,0,0,0,0,0,0,0,0,0},
  108. {0,0,0,0,0,0,0,0,0,0},
  109. {0,0,0,0,0,0,0,0,0,0},
  110. {0,0,0,0,0,0,0,0,0,0},
  111. {0,0,0,0,0,0,0,0,0,0},
  112. {0,0,0,0,0,0,0,0,0,0},
  113. {0,0,0,0,0,0,0,0,0,0},
  114. {0,0,0,0,0,0,0,0,0,0},
  115. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  116. {0,0,0,0,0,0,0,0,0,0},
  117. {0,0,0,0,0,0,0,0,0,0},
  118. {0,0,0,0,0,0,0,0,0,0},
  119. {0,0,0,0,0,0,0,0,0,0},
  120. {0,0,0,0,0,0,0,0,0,0},
  121. {0,0,0,0,0,0,0,0,0,0},
  122. {0,0,0,0,0,0,0,0,0,0},
  123. {0,0,0,0,0,0,0,0,0,0},
  124. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  125. {0,0,0,0,0,0,0,0,0,0},
  126. {0,0,0,0,0,0,0,0,0,0},
  127. {0,0,0,0,0,0,0,0,0,0},
  128. {0,0,0,0,0,0,0,0,0,0},
  129. {0,0,0,0,0,0,0,0,0,0},
  130. {0,0,0,0,0,0,0,0,0,0},
  131. {0,0,0,0,0,0,0,0,0,0},
  132. {0,0,0,0,0,0,0,0,0,0},
  133. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  134. {0,0,0,0,0,0,0,0,0,0},
  135. {0,0,0,0,0,0,0,0,0,0},
  136. {0,0,0,0,0,0,0,0,0,0},
  137. {0,0,0,0,0,0,0,0,0,0},
  138. {0,0,0,0,0,0,0,0,0,0},
  139. {0,0,0,0,0,0,0,0,0,0},
  140. {0,0,0,0,0,0,0,0,0,0},
  141. {0,0,0,0,0,0,0,0,0,0},
  142. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  143. {0,0,0,0,0,0,0,0,0,0},
  144. {0,0,0,0,0,0,0,0,0,0},
  145. {0,0,0,0,0,0,0,0,0,0},
  146. {0,0,0,0,0,0,0,0,0,0},
  147. {0,0,0,0,0,0,0,0,0,0},
  148. {0,0,0,0,0,0,0,0,0,0},
  149. {0,0,0,0,0,0,0,0,0,0},
  150. {0,0,0,0,0,0,0,0,0,0},
  151. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  152. {0,0,0,0,0,0,0,0,0,0},
  153. {0,0,0,0,0,0,0,0,0,0},
  154. {0,0,0,0,0,0,0,0,0,0},
  155. {0,0,0,0,0,0,0,0,0,0},
  156. {0,0,0,0,0,0,0,0,0,0},
  157. {0,0,0,0,0,0,0,0,0,0},
  158. {0,0,0,0,0,0,0,0,0,0},
  159. {0,0,0,0,0,0,0,0,0,0},
  160. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  161. {0,0,0,0,0,0,0,0,0,0},
  162. {0,0,0,0,0,0,0,0,0,0},
  163. {0,0,0,0,0,0,0,0,0,0},
  164. {0,0,0,0,0,0,0,0,0,0},
  165. {0,0,0,0,0,0,0,0,0,0},
  166. {0,0,0,0,0,0,0,0,0,0},
  167. {0,0,0,0,0,0,0,0,0,0},
  168. {0,0,0,0,0,0,0,0,0,0},
  169. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  170. {0,0,0,0,0,0,0,0,0,0},
  171. {0,0,0,0,0,0,0,0,0,0},
  172. {0,0,0,0,0,0,0,0,0,0},
  173. {0,0,0,0,0,0,0,0,0,0},
  174. {0,0,0,0,0,0,0,0,0,0},
  175. {0,0,0,0,0,0,0,0,0,0},
  176. {0,0,0,0,0,0,0,0,0,0},
  177. {0,0,0,0,0,0,0,0,0,0},
  178. {0,0,0,0,0,0,0,0,0,0}},{{0,0,0,0,0,0,0,0,0,0},
  179. {0,0,0,0,0,0,0,0,0,0},
  180. {0,0,0,0,0,0,0,0,0,0},
  181. {0,0,0,0,0,0,0,0,0,0},
  182. {0,0,0,0,0,0,0,0,0,0},
  183. {0,0,0,0,0,0,0,0,0,0},
  184. {0,0,0,0,0,0,0,0,0,0},
  185. {0,0,0,0,0,0,0,0,0,0},
  186. {0,0,0,0,0,0,0,0,0,0},
  187. {0,0,0,0,0,0,0,0,0,0}}
  188. };
  189. float input_values[10] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
  190. float sums[10] = {0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0};
  191. ComputerscarePatchSequencer() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  192. void step() override;
  193. json_t *toJson() override
  194. {
  195. json_t *rootJ = json_object();
  196. // button states
  197. json_t *button_statesJ = json_array();
  198. for(int k = 0; k < 16; k++) {
  199. for (int i = 0; i < 10; i++)
  200. {
  201. for (int j = 0; j < 10; j++)
  202. {
  203. json_t *button_stateJ = json_integer((int) switch_states[k][i][j]);
  204. json_array_append_new(button_statesJ, button_stateJ);
  205. }
  206. }
  207. }
  208. json_object_set_new(rootJ, "buttons", button_statesJ);
  209. return rootJ;
  210. }
  211. void fromJson(json_t *rootJ) override
  212. {
  213. // button states
  214. json_t *button_statesJ = json_object_get(rootJ, "buttons");
  215. if (button_statesJ)
  216. {
  217. for(int k = 0; k < 16; k++) {
  218. for (int i = 0; i < 10; i++)
  219. {
  220. for (int j = 0; j < 10; j++)
  221. {
  222. json_t *button_stateJ = json_array_get(button_statesJ, k*100+i*10 + j);
  223. if (button_stateJ)
  224. switch_states[k][i][j] = !!json_integer_value(button_stateJ);
  225. }
  226. }
  227. }
  228. }
  229. }
  230. void onRandomize() override {
  231. randomizePatchMatrix();
  232. }
  233. // For more advanced Module features, read Rack's engine.hpp header file
  234. // - toJson, fromJson: serialization of internal data
  235. // - onSampleRateChange: event triggered by a change of sample rate
  236. // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
  237. void randomizePatchMatrix()
  238. {
  239. int randomIndex;
  240. for (int i = 0; i < 10; i++)
  241. {
  242. randomIndex = floor(randomUniform()*10);
  243. for (int j = 0; j < 10; j++)
  244. {
  245. if(j==randomIndex)
  246. switch_states[editAddress][j][i] = 1;
  247. else
  248. switch_states[editAddress][j][i]=0;
  249. //fully randomize everything. a bit intense for its use in patching
  250. //switch_states[editAddress][i][j] = (randomUniform() > 0.5f);
  251. }
  252. }
  253. }; // end randomize()
  254. void onReset() override
  255. {
  256. for(int k =0; k < 16; k++) {
  257. for (int i = 0; i < 10; i++)
  258. {
  259. for (int j = 0; j < 10; j++)
  260. {
  261. switch_states[k][i][j] = 0;
  262. }
  263. }
  264. }
  265. }; // end randomize()
  266. };
  267. void ComputerscarePatchSequencer::step() {
  268. int numStepsKnobPosition = (int) clamp(roundf(params[STEPS_PARAM].value), 1.0f, 16.0f);
  269. for ( int i = 0 ; i < 10 ; i++)
  270. {
  271. sums[i] = 0.0;
  272. }
  273. for (int i = 0 ; i < 10 ; i++)
  274. {
  275. for (int j = 0 ; j < 10 ; j++)
  276. {
  277. if (switch_triggers[i][j].process(params[SWITCHES+j*10 + i].value))
  278. {
  279. // handle button clicks in the patch matrix
  280. switch_states[editAddress][i][j] = !switch_states[editAddress][i][j];
  281. }
  282. // update the green lights (step you are editing) and the red lights (current active step)
  283. lights[SWITCH_LIGHTS + i + j * 10].value = (switch_states[editAddress][i][j]) ? 1.0 : 0.0;
  284. lights[SWITCH_LIGHTS + i + j * 10+100].value = (switch_states[address][i][j]) ? 1.0 : 0.0;
  285. }
  286. }
  287. if(numStepsKnobPosition != numAddresses) {
  288. numAddresses = numStepsKnobPosition;
  289. }
  290. if(randomizeTrigger.process(inputs[RANDOMIZE_INPUT].value / 2.f)) {
  291. randomizePatchMatrix();
  292. }
  293. if(nextAddressEdit.process(params[EDIT_PARAM].value) ) {
  294. editAddress = editAddress + 1;
  295. editAddress = editAddress % 16;
  296. }
  297. if(prevAddressEdit.process(params[EDIT_PREV_PARAM].value) ) {
  298. editAddress = editAddress - 1;
  299. editAddress = editAddress + 16;
  300. editAddress = editAddress % 16;
  301. }
  302. if(nextAddressRead.process(params[MANUAL_CLOCK_PARAM].value) || clockTrigger.process(inputs[TRG_INPUT].value / 2.f)) {
  303. numAddresses = (int) clamp(roundf(params[STEPS_PARAM].value /*+ inputs[STEPS_INPUT].value*/), 1.0f, 16.0f);
  304. address = address + 1;
  305. address = address % numAddresses;
  306. }
  307. addressPlusOne = address + 1;
  308. editAddressPlusOne = editAddress + 1;
  309. for (int i = 0 ; i < 10 ; i++)
  310. {
  311. input_values[i] = inputs[INPUT_JACKS + i].value;
  312. }
  313. for (int i = 0 ; i < 10 ; i++)
  314. {
  315. for (int j = 0 ; j < 10 ; j++)
  316. {
  317. // todo: toggle for each output of how to combine multiple active signals in a column
  318. // sum, average, and, or etc
  319. if (switch_states[address][j][i]) sums[i] += input_values[j];
  320. }
  321. }
  322. /// outputs
  323. for (int i = 0 ; i < 10 ; i++)
  324. {
  325. outputs[OUTPUTS + i].value = sums[i];
  326. }
  327. }
  328. ////////////////////////////////////
  329. struct NumberDisplayWidget3 : TransparentWidget {
  330. int *value;
  331. std::shared_ptr<Font> font;
  332. NumberDisplayWidget3() {
  333. font = Font::load(assetPlugin(plugin, "res/digital-7.ttf"));
  334. };
  335. void draw(NVGcontext *vg) override
  336. {
  337. // Background
  338. NVGcolor backgroundColor = nvgRGB(0x00, 0x00, 0x00);
  339. nvgBeginPath(vg);
  340. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  341. nvgFillColor(vg, backgroundColor);
  342. nvgFill(vg);
  343. // text
  344. nvgFontSize(vg, 13);
  345. nvgFontFaceId(vg, font->handle);
  346. nvgTextLetterSpacing(vg, 2.5);
  347. std::stringstream to_display;
  348. to_display << std::setw(3) << *value;
  349. Vec textPos = Vec(6.0f, 17.0f);
  350. NVGcolor textColor = nvgRGB(0xC0, 0xE7, 0xDE);
  351. nvgFillColor(vg, textColor);
  352. nvgText(vg, textPos.x, textPos.y, to_display.str().c_str(), NULL);
  353. }
  354. };
  355. struct ComputerscarePatchSequencerWidget : ModuleWidget {
  356. ComputerscarePatchSequencerWidget(ComputerscarePatchSequencer *module) : ModuleWidget(module) {
  357. setPanel(SVG::load(assetPlugin(plugin, "res/ComputerscarePatchSequencerPanel.svg")));
  358. int top_row = 70;
  359. int row_spacing = 26;
  360. int column_spacing = 26;
  361. for (int i = 0 ; i < 10 ; i++)
  362. {
  363. addInput(Port::create<InPort>(Vec(3, i * row_spacing + top_row), Port::INPUT, module, ComputerscarePatchSequencer::INPUT_JACKS + i));
  364. addOutput(Port::create<InPort>(Vec(33 + i * column_spacing , top_row + 10 * row_spacing), Port::OUTPUT, module, ComputerscarePatchSequencer::OUTPUTS + i));
  365. for(int j = 0 ; j < 10 ; j++ )
  366. {
  367. // the part you click
  368. addParam(ParamWidget::create<LEDButton>(Vec(35 + column_spacing * j, top_row + row_spacing * i), module, ComputerscarePatchSequencer::SWITCHES + i + j * 10, 0.0, 1.0, 0.0));
  369. // green light indicates the state of the matrix that is being edited
  370. addChild(ModuleLightWidget::create<LargeLight<GreenLight>>(Vec(35 + column_spacing * j +1.4, top_row + row_spacing * i +1.4 ), module, ComputerscarePatchSequencer::SWITCH_LIGHTS + i + j * 10));
  371. // red light indicates the state of the matrix that is the active step
  372. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(35 + column_spacing * j + 4.3, top_row + row_spacing * i + 4.3), module, ComputerscarePatchSequencer::SWITCH_LIGHTS + i + j * 10+100));
  373. }
  374. }
  375. //clock input
  376. addInput(Port::create<InPort>(Vec(3, 0), Port::INPUT, module, ComputerscarePatchSequencer::TRG_INPUT));
  377. //manual clock button
  378. addParam(ParamWidget::create<LEDButton>(Vec(7 , 41), module, ComputerscarePatchSequencer::MANUAL_CLOCK_PARAM, 0.0, 1.0, 0.0));
  379. //randomize input
  380. addInput(Port::create<InPort>(Vec(270, 0), Port::INPUT, module, ComputerscarePatchSequencer::RANDOMIZE_INPUT));
  381. //active step display
  382. NumberDisplayWidget3 *display = new NumberDisplayWidget3();
  383. display->box.pos = Vec(30,40);
  384. display->box.size = Vec(50, 20);
  385. display->value = &module->addressPlusOne;
  386. addChild(display);
  387. // number of steps display
  388. NumberDisplayWidget3 *stepsDisplay = new NumberDisplayWidget3();
  389. stepsDisplay->box.pos = Vec(150,40);
  390. stepsDisplay->box.size = Vec(50, 20);
  391. stepsDisplay->value = &module->numAddresses;
  392. addChild(stepsDisplay);
  393. //number-of-steps dial. Discrete, 16 positions
  394. ParamWidget* stepsKnob = ParamWidget::create<LrgKnob>(Vec(108,30), module, ComputerscarePatchSequencer::STEPS_PARAM, 1.0f, 16.0f, 2.0f);
  395. addParam(stepsKnob);
  396. //editAddressNext button
  397. addParam(ParamWidget::create<LEDButton>(Vec(227 , 41), module, ComputerscarePatchSequencer::EDIT_PARAM, 0.0, 1.0, 0.0));
  398. //editAddressPrevious button
  399. addParam(ParamWidget::create<LEDButton>(Vec(208 , 41), module, ComputerscarePatchSequencer::EDIT_PREV_PARAM, 0.0, 1.0, 0.0));
  400. // currently editing step #:
  401. NumberDisplayWidget3 *displayEdit = new NumberDisplayWidget3();
  402. displayEdit->box.pos = Vec(245,40);
  403. displayEdit->box.size = Vec(50, 20);
  404. displayEdit->value = &module->editAddressPlusOne;
  405. addChild(displayEdit);
  406. }
  407. };
  408. } // namespace rack_plugin_computerscare
  409. using namespace rack_plugin_computerscare;
  410. RACK_PLUGIN_MODEL_INIT(computerscare, ComputerscarePatchSequencer) {
  411. Model *modelComputerscarePatchSequencer = Model::create<ComputerscarePatchSequencer, ComputerscarePatchSequencerWidget>("computerscare", "computerscare-patch-sequencer", "Father & Son Patch Sequencer", UTILITY_TAG,SEQUENCER_TAG);
  412. return modelComputerscarePatchSequencer;
  413. }