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.

350 lines
11KB

  1. #include "CatroModulo.hpp"
  2. //Catro-Modulo CM3: PreSetSeq
  3. struct CM3Module : Module {
  4. enum ParamIds {
  5. ENUMS(PARAM_REC, 8),
  6. ENUMS(PARAM_EYE, 8),
  7. PARAM_PATTERN,
  8. PARAM_MORPH,
  9. PARAM_LENGTH,
  10. PARAM_TRYME,
  11. PARAM_SCAN,
  12. PARAM_SELECT,
  13. PARAM_Q,
  14. PARAM_SEQ,
  15. PARAM_RESET,
  16. PARAM_STEP,
  17. NUM_PARAMS,
  18. };
  19. enum InputIds {
  20. ENUMS(INPUT_REC, 8),
  21. ENUMS(INPUT_EYE, 8),
  22. INPUT_PATTERN,
  23. INPUT_STEP,
  24. INPUT_MORPH,
  25. INPUT_RESET,
  26. INPUT_LENGTH,
  27. INPUT_SELECT,
  28. INPUT_BPM,
  29. NUM_INPUTS
  30. };
  31. enum OutputIds {
  32. ENUMS(OUTPUT_EYE, 8),
  33. NUM_OUTPUTS
  34. };
  35. enum LightIds {
  36. NUM_LIGHTS
  37. };
  38. //initializations
  39. std::string display_pat = "";
  40. std::string display_len = "";
  41. std::string strings_pat[16] = {"SEQ", "REV", "S0Q", "R0V", "NZN", "CUC", "ZZ1", "ZZ2", "U7D", "U4D", "U3D", ">-<", "/\\/", ".-.", "\\/\\", "RND"};
  42. std::string strings_len[16] = {" 01", " 02", " 03", " 04", " 05", " 06", " 07", " 08", " 09", " 10", " 11", " 12", " 13", " 14", "15", "16"};
  43. SchmittTrigger recordTrigger[16];
  44. float iselect = 0.0f;
  45. float recsel = 0.0f;
  46. float recball_x = 178.8;
  47. float recball_y = 89.5;
  48. float recball_xarray[8] = {178.8f , 212.4f , 242.7f , 212.4f , 178.8f , 145.3f , 115.0f , 145.3f};
  49. float recball_yarray[8] = {89.5f , 119.9f , 153.4f , 186.9f , 217.2f , 186.9f , 153.4f , 119.9f };
  50. float eyepatch_val[8] = {};
  51. CM_SelSeq sequencer;
  52. CM_Recorder recorder;
  53. CM_BpmClock bpmclock;
  54. CM3Module() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  55. void step() override;
  56. json_t *toJson() override {
  57. json_t *rootJ = json_object();
  58. json_t *recordsJ = json_array();
  59. for (int i = 0; i < 8; i++){
  60. for (int j = 0; j < 8; j++){
  61. json_array_append_new(recordsJ, json_real(recorder.save(i,j)));
  62. }
  63. }
  64. json_object_set_new(rootJ, "recorder", recordsJ);
  65. return rootJ;
  66. }
  67. void fromJson(json_t *rootJ) override {
  68. // running
  69. json_t *recorderJ = json_object_get(rootJ, "recorder");
  70. for (int i = 0; i < 8; i++){
  71. for (int j = 0; j < 8; j++){
  72. recorder.load(i, j, json_real_value(json_array_get(recorderJ, 8 * i + j)));
  73. }
  74. }
  75. }
  76. // For more advanced Module features, read Rack's engine.hpp header file
  77. // - toJson, fromJson: serialization of internal data
  78. // - onSampleRateChange: event triggered by a change of sample rate
  79. // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
  80. };
  81. void CM3Module::step() {
  82. //mix params and inputs
  83. float morph = (inputs[INPUT_MORPH].active) ? inputs[INPUT_MORPH].value * 0.1f + params[PARAM_MORPH].value : params[PARAM_MORPH].value;
  84. float seq_active = 1.0 - params[PARAM_SEQ].value;
  85. float seq_reset = (inputs[INPUT_RESET].value || params[PARAM_RESET].value);
  86. float seq_pattern = clamp(roundf((inputs[INPUT_PATTERN].active) ? inputs[INPUT_PATTERN].value * 0.1f * params[PARAM_PATTERN].value : params[PARAM_PATTERN].value), 0.0, 15.0);
  87. float seq_len = clamp(roundf((inputs[INPUT_LENGTH].active) ? inputs[INPUT_LENGTH].value * 0.1f * params[PARAM_LENGTH].value : params[PARAM_LENGTH].value), 0.0, 15.0);
  88. float doscan = (params[PARAM_SCAN].value && params[PARAM_SEQ].value);
  89. //check for bpm cv
  90. float seq_step = 0;
  91. if (inputs[INPUT_BPM].active){
  92. bpmclock.setcv(inputs[INPUT_BPM].value);
  93. }
  94. bpmclock.setReset(inputs[INPUT_RESET].value || params[PARAM_RESET].value);
  95. if (inputs[INPUT_BPM].active){
  96. bpmclock.step(engineGetSampleTime());
  97. seq_step = bpmclock.track(1);
  98. }else{
  99. seq_step = (inputs[INPUT_STEP].value || params[PARAM_STEP].value);
  100. }
  101. //process tryme button
  102. recorder.tryme(params[PARAM_TRYME].value);
  103. //process eyes
  104. float eyeval[8] = {};
  105. for (int i = 0; i < 8; i++) {
  106. float in = 1.0f;
  107. float eye = params[i+PARAM_EYE].value;
  108. if (inputs[i+PARAM_EYE].active){
  109. in = inputs[i+PARAM_EYE].value * 0.1f;
  110. }
  111. eyeval[i] = clamp(in * eye, -1.0f, 1.0f);
  112. }
  113. //record when requested
  114. for (int i = 0; i < 8; i++) {
  115. if (recordTrigger[i].process((inputs[INPUT_REC+i].value || params[PARAM_REC+i].value))){
  116. recorder.record(eyeval, i);
  117. }
  118. }
  119. //process sequencer
  120. if (seq_active == 1.0){
  121. sequencer.reset(seq_reset);
  122. sequencer.step(seq_step, seq_len);
  123. if (sequencer.patternized == true) {
  124. iselect = sequencer.sequence(seq_pattern);
  125. }
  126. }else{
  127. iselect = clamp((inputs[INPUT_SELECT].active) ? inputs[INPUT_SELECT].value * 0.1f * params[PARAM_SELECT].value : params[PARAM_SELECT].value, 0.0, 7.99999f);
  128. }
  129. recorder.scan(iselect, doscan);
  130. recorder.mix(eyeval,morph);
  131. for (int i = 0; i < 8; i++) {
  132. if (iselect != -1.0){
  133. outputs[OUTPUT_EYE + i].value = recorder.output(i);
  134. }else{
  135. outputs[OUTPUT_EYE + i].value = 0.0f;
  136. }
  137. }
  138. //set eyepatches
  139. for (int i = 0; i < 8; i++) {
  140. eyepatch_val[i] = recorder.callget(i);
  141. }
  142. //set displays
  143. display_pat = (seq_active) ? strings_pat[int(seq_pattern)] : "OFF";
  144. display_len = strings_len[int(seq_len)];
  145. //recball
  146. if (iselect != -1.0){
  147. recball_x = recball_xarray[int(iselect)] + 9.0;
  148. recball_y = recball_yarray[int(iselect)] + 9.0;
  149. }
  150. }
  151. struct CM3ModuleWidget : ModuleWidget {
  152. CM3ModuleWidget(CM3Module *module) : ModuleWidget(module) {
  153. setPanel(SVG::load(assetPlugin(plugin, "res/CM-3.svg")));
  154. addChild(Widget::create<ScrewSilver>(Vec(10, 0)));
  155. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 20, 0)));
  156. addChild(Widget::create<ScrewSilver>(Vec(30, 365)));
  157. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 40, 365)));
  158. int y = 0; //initialize reusable counter
  159. //REC BUTTONS
  160. float recbuttons[16] = {178.8 , 89.5 ,
  161. 212.4 , 119.9 ,
  162. 242.7 , 153.4 ,
  163. 212.4 , 186.9 ,
  164. 178.8 , 217.2 ,
  165. 145.3 , 186.9 ,
  166. 115.0 , 153.4 ,
  167. 145.3 , 119.9 };
  168. y = 0;
  169. for(int i = 0; i < 16; i += 2){
  170. addParam(ParamWidget::create<CM_Recbutton>(Vec(recbuttons[i],recbuttons[i+1] - 0.5), module, CM3Module::PARAM_REC + y, 0.0f, 1.0f, 0.0f));
  171. y++;
  172. }
  173. //REC INPUTS
  174. float recin[16] = { 185.5 , 127.8 ,
  175. 196.5 , 149.0 ,
  176. 217.4 , 160.0 ,
  177. 196.5 , 171.0 ,
  178. 185.5 , 192.3 ,
  179. 174.5 , 171.0 ,
  180. 153.2 , 160.0 ,
  181. 174.5 , 149.0 };
  182. y = 0;
  183. for(int i = 0; i < 16; i += 2){
  184. addInput(Port::create<CM_Input_small>(Vec(recin[i],recin[i+1] - 0.5), Port::INPUT, module, CM3Module::INPUT_REC + y));
  185. y++;
  186. }
  187. //BIGEYES
  188. float bigeyes[16] = {54.9 , 94.9 ,
  189. 32.7 , 146.6 ,
  190. 54.9 , 198.4 ,
  191. 84.5 , 250.1 ,
  192. 290.7 , 94.9 ,
  193. 312.9 , 146.6 ,
  194. 290.7 , 198.4 ,
  195. 261.1 , 250.1};
  196. y = 0;
  197. for(int i = 0; i < 16; i += 2){
  198. addParam(ParamWidget::create<CM_Knob_bigeye>(Vec(bigeyes[i],bigeyes[i+1] - 0.5), module, CM3Module::PARAM_EYE + y, -1.0f, 1.0f, 0.0f));
  199. y++;
  200. }
  201. //EYE INPUTS
  202. float eyein[16] = {104.7 , 117.5 ,
  203. 84.1 , 159.3 ,
  204. 104.7 , 201.1 ,
  205. 130.7 , 243.4 ,
  206. 266.0 , 117.5 ,
  207. 287.1 , 159.3 ,
  208. 266.0 , 201.1 ,
  209. 240.3 , 243.4};
  210. y = 0;
  211. for(int i = 0; i < 16; i += 2){
  212. addInput(Port::create<CM_Input_small>(Vec(eyein[i],eyein[i+1] - 0.5), Port::INPUT, module, CM3Module::INPUT_EYE + y));
  213. y++;
  214. }
  215. //EYE OUTPUTS
  216. float eyeout[16] = {30.2 , 97.1 ,
  217. 6.4 , 158.8 ,
  218. 30.2 , 220.5 ,
  219. 63.6 , 281.4 ,
  220. 340.0 , 97.1 ,
  221. 363.5 , 158.8 ,
  222. 340.0 , 220.5 ,
  223. 304.5 , 281.4};
  224. y = 0;
  225. for(int i = 0; i < 16; i += 2){
  226. addOutput(Port::create<CM_Output_small>(Vec(eyeout[i],eyeout[i+1] - 0.5), Port::OUTPUT, module, CM3Module::OUTPUT_EYE + y));
  227. y++;
  228. }
  229. //OTHER ELEMENTS
  230. addParam(ParamWidget::create<CM_Knob_small_def_half>(Vec(33.4 , 34.7), module, CM3Module::PARAM_PATTERN, 0.0f, 15.0f, 0.0f));
  231. addParam(ParamWidget::create<CM_Slider_big_red>(Vec(156.5 , 17.9), module, CM3Module::PARAM_MORPH, -1.0f, 1.0f, 0.0f));
  232. addParam(ParamWidget::create<CM_Knob_small_def_half>(Vec(326.0 , 34.7), module, CM3Module::PARAM_LENGTH, 0.0f, 15.0f, 7.0f));
  233. addParam(ParamWidget::create<CM_TryMe_button>(Vec(17.0 , 322.1), module, CM3Module::PARAM_TRYME, 0.0f, 1.0f, 0.0f));
  234. addParam(ParamWidget::create<CM_Switch_small>(Vec(137.8 , 309.0), module, CM3Module::PARAM_SCAN, 0.0f, 1.0f, 0.0f));
  235. addParam(ParamWidget::create<CM_Knob_huge_red_os>(Vec(161.3 , 286.0), module, CM3Module::PARAM_SELECT, 0.0f, 7.99999f, 0.0f));
  236. //addParam(ParamWidget::create<CM_Knob_small_def>(Vec(232.2 , 304.5), module,PARAM_Q, 0.1f, 0.9f, 0.5f)); //maybe implement later?
  237. addParam(ParamWidget::create<CM_Switch_small>(Vec(366. , 309.0), module, CM3Module::PARAM_SEQ, 0.0f, 1.0f, 1.0f));
  238. addParam(ParamWidget::create<CM_I_def_tinybuttonR>(Vec(263.0 , 38.7), module, CM3Module::PARAM_RESET, 0.0f, 1.0f, 0.0f));
  239. addParam(ParamWidget::create<CM_I_def_tinybuttonL>(Vec(85.4 , 38.7), module, CM3Module::PARAM_STEP, 0.0f, 1.0f, 0.0f));
  240. addInput(Port::create<CM_Input_def>(Vec(15.7 , 60.1), Port::INPUT, module, CM3Module::INPUT_PATTERN));
  241. addInput(Port::create<CM_Input_def>(Vec(94.0 , 38.7), Port::INPUT, module, CM3Module::INPUT_STEP));
  242. addInput(Port::create<CM_Input_bpm>(Vec(127.5 , 38.7), Port::INPUT, module, CM3Module::INPUT_BPM));
  243. addInput(Port::create<CM_Input_def>(Vec(183.5 , 45.4), Port::INPUT, module, CM3Module::INPUT_MORPH));
  244. addInput(Port::create<CM_Input_def>(Vec(250.8 , 38.7), Port::INPUT, module, CM3Module::INPUT_RESET));
  245. addInput(Port::create<CM_Input_def>(Vec(352.3 , 61.4), Port::INPUT, module, CM3Module::INPUT_LENGTH));
  246. addInput(Port::create<CM_Input_def>(Vec(183.5 , 259.0), Port::INPUT, module, CM3Module::INPUT_SELECT));
  247. //LCD display pattern
  248. TxtDisplayWidget *dispat = new TxtDisplayWidget();
  249. dispat->box.pos = Vec(29.9, 11.0);
  250. dispat->box.size = Vec(38.0 , 20.4);
  251. dispat->txt = &module->display_pat;
  252. addChild(dispat);
  253. //LCD display length
  254. TxtDisplayWidget *dislen = new TxtDisplayWidget();
  255. dislen->box.pos = Vec(322.4 , 11.0);
  256. dislen->box.size = Vec(38.0 , 20.4);
  257. dislen->txt = &module->display_len;
  258. addChild(dislen);
  259. //selector indicator yellow
  260. CM3_RecBall *recball = new CM3_RecBall();
  261. recball->box.size = Vec(32.0, 32.0);
  262. recball->recball_x = &module->recball_x;
  263. recball->recball_y = &module->recball_y;
  264. addChild(recball);
  265. //eyepatches: indicate the actual output
  266. float dd = 20.5; //distance from origin
  267. float rr = 2.5; //radius of circle
  268. CM3_EyePatch *eyepatch[8] = {
  269. new CM3_EyePatch(77.4, 117.4 , dd, rr),
  270. new CM3_EyePatch(55.2, 169.1 , dd, rr),
  271. new CM3_EyePatch(77.4 , 220.9 , dd, rr),
  272. new CM3_EyePatch(107.0 , 272.6 , dd, rr),
  273. new CM3_EyePatch(313.2 , 117.4 , dd, rr),
  274. new CM3_EyePatch(335.4 , 169.1 , dd, rr),
  275. new CM3_EyePatch(313.2 , 220.9 , dd, rr),
  276. new CM3_EyePatch(283.6 , 272.6 , dd, rr)
  277. };
  278. for(int i = 0; i < 8; i ++){
  279. eyepatch[i]->eyepatch_val = &module->eyepatch_val[i];
  280. addChild(eyepatch[i]);
  281. }
  282. };
  283. };
  284. // Specify the Module and ModuleWidget subclass, human-readable
  285. // author name for categorization per plugin, module slug (should never
  286. // change), human-readable module name, and any number of tags
  287. // (found in `include/tags.hpp`) separated by commas.
  288. // Model *modelCM3Module = Model::create<CM3Module, CM3ModuleWidget>("CatroModulo", "CatroModulo_CM-3", "C/M3 : PreSetSeq", SEQUENCER_TAG);
  289. RACK_PLUGIN_MODEL_INIT(CatroModulo, CM3Module) {
  290. Model *model = Model::create<CM3Module, CM3ModuleWidget>("CatroModulo", "CatroModulo_CM3", "C/M3 : PreSetSeq", SEQUENCER_TAG);
  291. return model;
  292. }