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.

473 lines
15KB

  1. //***********************************************************************************************
  2. //16-step sequencer module for VCV Rack by Alfredo Santamaria - AS - https://github.com/AScustomWorks/AS
  3. //
  4. //Based on SEQ16 VCV Rack by Autodafe http://www.autodafe.net
  5. //Based on code taken from the Fundamentals plugins by Andrew Belt http://www.vcvrack.com
  6. //***********************************************************************************************
  7. #include "AS.hpp"
  8. #include "dsp/digital.hpp"
  9. struct SEQ16 : Module {
  10. enum ParamIds {
  11. CLOCK_PARAM,
  12. RUN_PARAM,
  13. RESET_PARAM,
  14. STEPS_PARAM,
  15. TRIGGER_PARAM,
  16. PREV_STEP,
  17. NEXT_STEP,
  18. GATE_MODE_PARAM,
  19. ROW1_PARAM,
  20. ROW2_PARAM = ROW1_PARAM + 16,
  21. ROW3_PARAM = ROW2_PARAM + 16,
  22. GATE_PARAM = ROW3_PARAM + 16,
  23. NUM_PARAMS = GATE_PARAM + 16
  24. };
  25. enum InputIds {
  26. CLOCK_INPUT,
  27. EXT_CLOCK_INPUT,
  28. RESET_INPUT,
  29. STEPS_INPUT,
  30. NUM_INPUTS
  31. };
  32. enum OutputIds {
  33. GATES_OUTPUT,
  34. ROW1_OUTPUT,
  35. ROW2_OUTPUT,
  36. ROW3_OUTPUT,
  37. GATE_OUTPUT,
  38. NUM_OUTPUTS = GATE_OUTPUT + 16
  39. };
  40. enum LightIds {
  41. RUNNING_LIGHT,
  42. RESET_LIGHT,
  43. GATES_LIGHT,
  44. TRIGGER_LIGHT,
  45. ROW_LIGHTS,
  46. GATE_LIGHTS = ROW_LIGHTS + 3,
  47. NUM_LIGHTS = GATE_LIGHTS + 16
  48. };
  49. bool running = true;
  50. bool triggerActive = false;
  51. // for external clock
  52. SchmittTrigger clockTrigger;
  53. // For buttons
  54. SchmittTrigger runningTrigger;
  55. SchmittTrigger resetTrigger;
  56. SchmittTrigger prevTrigger;
  57. SchmittTrigger nextTrigger;
  58. SchmittTrigger manualTrigger;
  59. SchmittTrigger gateTriggers[16];
  60. float phase = 0.0f;
  61. float blinkPhase = 0.0f;
  62. int index = 0;
  63. int stepIndex = index+1;
  64. int modeIndex = 0;
  65. bool nextStep = false;
  66. bool gateState[16] = {};
  67. float resetLight = 0.0f;
  68. float stepLights[16] = {};
  69. const float lightLambda = 0.075f;
  70. enum GateMode {
  71. TRIGGER,
  72. RETRIGGER,
  73. CONTINUOUS,
  74. };
  75. GateMode gateMode = TRIGGER;
  76. PulseGenerator gatePulse;
  77. SEQ16() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  78. reset();
  79. }
  80. void step() override;
  81. int numSteps;
  82. json_t *toJson() override {
  83. json_t *rootJ = json_object();
  84. // running
  85. json_object_set_new(rootJ, "running", json_boolean(running));
  86. // gates
  87. json_t *gatesJ = json_array();
  88. for (int i = 0; i < 16; i++) {
  89. json_t *gateJ = json_integer((int) gateState[i]);
  90. json_array_append_new(gatesJ, gateJ);
  91. }
  92. json_object_set_new(rootJ, "gates", gatesJ);
  93. // gateMode
  94. json_t *gateModeJ = json_integer((int) gateMode);
  95. json_object_set_new(rootJ, "gateMode", gateModeJ);
  96. return rootJ;
  97. }
  98. void fromJson(json_t *rootJ) override {
  99. // running
  100. json_t *runningJ = json_object_get(rootJ, "running");
  101. if (runningJ)
  102. running = json_is_true(runningJ);
  103. // gates
  104. json_t *gatesJ = json_object_get(rootJ, "gates");
  105. if (gatesJ) {
  106. for (int i = 0; i < 16; i++) {
  107. json_t *gateJ = json_array_get(gatesJ, i);
  108. if (gateJ)
  109. gateState[i] = !!json_integer_value(gateJ);
  110. }
  111. }
  112. // gateMode
  113. json_t *gateModeJ = json_object_get(rootJ, "gateMode");
  114. if (gateModeJ)
  115. gateMode = (GateMode)json_integer_value(gateModeJ);
  116. }
  117. void reset() override {
  118. for (int i = 0; i < 16; i++) {
  119. gateState[i] = true;
  120. }
  121. }
  122. void randomize() override {
  123. for (int i = 0; i < 16; i++) {
  124. gateState[i] = (randomUniform() > 0.5);
  125. }
  126. }
  127. };
  128. void SEQ16::step() {
  129. numSteps = roundf(clamp(params[STEPS_PARAM].value, 1.0f, 16.0f));
  130. stepIndex = index+1;
  131. // Gate mode Switch with 3 way switch
  132. modeIndex = params[GATE_MODE_PARAM].value;
  133. gateMode = (GateMode)int(modeIndex);
  134. // Run
  135. if (runningTrigger.process(params[RUN_PARAM].value)) {
  136. running = !running;
  137. }
  138. lights[RUNNING_LIGHT].value = running ? 1.0f : 0.0f;
  139. nextStep = false;
  140. if (running) {
  141. if (inputs[EXT_CLOCK_INPUT].active) {
  142. // External clock
  143. if (clockTrigger.process(inputs[EXT_CLOCK_INPUT].value)) {
  144. phase = 0.0f;
  145. nextStep = true;
  146. }
  147. }
  148. else {
  149. // Internal clock
  150. float clockTime = powf(2.0, params[CLOCK_PARAM].value + inputs[CLOCK_INPUT].value);
  151. phase += clockTime / engineGetSampleRate();
  152. if (phase >= 1.0f) {
  153. phase -= 1.0f;
  154. nextStep = true;
  155. }
  156. }
  157. }
  158. // Reset
  159. if (resetTrigger.process(params[RESET_PARAM].value + inputs[RESET_INPUT].value)) {
  160. phase = 0.0f;
  161. index = 16;
  162. nextStep = true;
  163. resetLight = 1.0;
  164. }
  165. if (nextStep) {
  166. // Advance step
  167. int numSteps = clamp(round(params[STEPS_PARAM].value + inputs[STEPS_INPUT].value), 1.0f, 16.0f);
  168. index += 1;
  169. if (index >= numSteps) {
  170. index = 0;
  171. }
  172. stepLights[index] = 1.0f;
  173. gatePulse.trigger(1e-3);
  174. }
  175. resetLight -= resetLight / lightLambda / engineGetSampleRate();
  176. bool pulse = gatePulse.process(1.0 / engineGetSampleRate());
  177. // Gate buttons
  178. for (int i = 0; i < 16; i++) {
  179. if (gateTriggers[i].process(params[GATE_PARAM + i].value)) {
  180. gateState[i] = !gateState[i];
  181. }
  182. bool gateOn = (running && i == index && gateState[i]);
  183. if (gateMode == TRIGGER)
  184. gateOn = gateOn && pulse;
  185. else if (gateMode == RETRIGGER)
  186. gateOn = gateOn && !pulse;
  187. outputs[GATE_OUTPUT + i].value = gateOn ? 10.0f : 0.0f;
  188. stepLights[i] -= stepLights[i] / lightLambda / engineGetSampleRate();
  189. lights[GATE_LIGHTS + i].value = gateState[i] ? 1.0f - stepLights[i] : stepLights[i];
  190. }
  191. // Rows
  192. float row1 = params[ROW1_PARAM + index].value;
  193. float row2 = params[ROW2_PARAM + index].value;
  194. float row3 = params[ROW3_PARAM + index].value;
  195. bool gatesOn = (running && gateState[index]);
  196. if (gateMode == TRIGGER)
  197. gatesOn = gatesOn && pulse;
  198. else if (gateMode == RETRIGGER)
  199. gatesOn = gatesOn && !pulse;
  200. // Outputs
  201. outputs[ROW1_OUTPUT].value = row1;
  202. outputs[ROW2_OUTPUT].value = row2;
  203. outputs[ROW3_OUTPUT].value = row3;
  204. lights[RESET_LIGHT].value = resetLight;
  205. lights[GATES_LIGHT].value = gatesOn ? 1.0f : 0.0f;
  206. lights[ROW_LIGHTS].value = row1;
  207. lights[ROW_LIGHTS + 1].value = row2;
  208. lights[ROW_LIGHTS + 2].value = row3;
  209. //mod to make the manual trigger work
  210. if (running) {
  211. outputs[GATES_OUTPUT].value = gatesOn ? 10.0f : 0.0f;
  212. lights[TRIGGER_LIGHT].value = 0.0f;
  213. //disable manual trigger
  214. triggerActive = false;
  215. }
  216. //Edit mode
  217. if(manualTrigger.process(params[TRIGGER_PARAM].value)){
  218. triggerActive = !triggerActive;
  219. }
  220. lights[TRIGGER_LIGHT].value = triggerActive ? 1.0f : 0.0f;
  221. // Manual trigger/manual step, only when the seq is not running
  222. if (triggerActive) {
  223. running=false;
  224. outputs[GATES_OUTPUT].value = 10.0f;
  225. // Blink light at 1Hz
  226. float deltaTime = 5.0f / engineGetSampleRate();
  227. blinkPhase += deltaTime;
  228. if (blinkPhase >= 1.0f){
  229. blinkPhase -= 1.0f;
  230. }
  231. // step edit light indicator
  232. for (int i = 0; i < 16; i++) {
  233. if(i==index){
  234. lights[GATE_LIGHTS + i].value = (blinkPhase < 0.5f) ? 1.0f : 0.0f;
  235. }else{
  236. lights[GATES_LIGHT].value = gatesOn ? 1.0f : 0.0f;
  237. }
  238. }
  239. }else{
  240. outputs[GATES_OUTPUT].value = gatesOn ? 10.0f : 0.0f;
  241. lights[GATES_LIGHT].value = gatesOn ? 1.0f : 0.0f;
  242. }
  243. //Prev/next step buttons only work when seq is not running
  244. if(!running){
  245. if (prevTrigger.process(params[PREV_STEP].value)) {
  246. index -= 1;
  247. if(index<0){
  248. index = numSteps-1;
  249. }
  250. }
  251. if (nextTrigger.process(params[NEXT_STEP].value)) {
  252. index += 1;
  253. if(index>numSteps-1){
  254. index = 0;
  255. }
  256. }
  257. }
  258. }
  259. struct StepsDisplayWidget : TransparentWidget {
  260. int *value;
  261. std::shared_ptr<Font> font;
  262. StepsDisplayWidget() {
  263. font = Font::load(assetPlugin(plugin, "res/Segment7Standard.ttf"));
  264. };
  265. void draw(NVGcontext *vg) {
  266. // Background
  267. //NVGcolor backgroundColor = nvgRGB(0x20, 0x20, 0x20);
  268. NVGcolor backgroundColor = nvgRGB(0x20, 0x10, 0x10);
  269. NVGcolor borderColor = nvgRGB(0x10, 0x10, 0x10);
  270. nvgBeginPath(vg);
  271. nvgRoundedRect(vg, 0.0, 0.0, box.size.x, box.size.y, 4.0);
  272. nvgFillColor(vg, backgroundColor);
  273. nvgFill(vg);
  274. nvgStrokeWidth(vg, 1.5);
  275. nvgStrokeColor(vg, borderColor);
  276. nvgStroke(vg);
  277. nvgFontSize(vg, 22);
  278. nvgFontFaceId(vg, font->handle);
  279. nvgTextLetterSpacing(vg, 2.5);
  280. char displayStr[3];
  281. sprintf(displayStr, "%2u", (unsigned) *value);
  282. Vec textPos = Vec(6, 23);
  283. NVGcolor textColor = nvgRGB(0xdf, 0xd2, 0x2c);
  284. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  285. nvgText(vg, textPos.x, textPos.y, "~~", NULL);
  286. textColor = nvgRGB(0xda, 0xe9, 0x29);
  287. nvgFillColor(vg, nvgTransRGBA(textColor, 16));
  288. nvgText(vg, textPos.x, textPos.y, "\\\\", NULL);
  289. textColor = nvgRGB(0xf0, 0x00, 0x00);
  290. nvgFillColor(vg, textColor);
  291. nvgText(vg, textPos.x, textPos.y, displayStr, NULL);
  292. }
  293. };
  294. template <typename BASE>
  295. struct MuteLight : BASE {
  296. MuteLight() {
  297. //this->box.size = Vec(20.0, 20.0);
  298. this->box.size = mm2px(Vec(6.0, 6.0));
  299. }
  300. };
  301. struct SEQ16Widget : ModuleWidget {
  302. SEQ16Widget(SEQ16 *module);
  303. Menu *createContextMenu() override;
  304. };
  305. SEQ16Widget::SEQ16Widget(SEQ16 *module) : ModuleWidget(module) {
  306. box.size = Vec(44 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT);
  307. {
  308. SVGPanel *panel = new SVGPanel();
  309. panel->box.size = box.size;
  310. panel->setBackground(SVG::load(assetPlugin(plugin, "res/SEQ16.svg")));
  311. addChild(panel);
  312. }
  313. //LCD STEPS SCREEN
  314. StepsDisplayWidget *display = new StepsDisplayWidget();
  315. display->box.pos = Vec(341,60);
  316. display->box.size = Vec(40, 30);
  317. display->value = &module->numSteps;
  318. addChild(display);
  319. //LCD CURRENT STEP SCREEN
  320. StepsDisplayWidget *display2 = new StepsDisplayWidget();
  321. display2->box.pos = Vec(401,60);
  322. display2->box.size = Vec(40, 30);
  323. display2->value = &module->stepIndex;
  324. addChild(display2);
  325. //SCREWS
  326. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, 0)));
  327. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  328. addChild(Widget::create<as_HexScrew>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  329. addChild(Widget::create<as_HexScrew>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  330. //
  331. static const float portX[16] = {20,60,100,140,180,220,260,300,340,380,420,460,500,540,580,620};
  332. static const float elements_offst = 8;
  333. static const float main_lds_y = 64.4;
  334. //CLOCK KNOB
  335. addParam(ParamWidget::create<as_KnobBlack>(Vec(portX[1]-elements_offst, 56), module, SEQ16::CLOCK_PARAM, -2.0f, 6.0f, 2.0f));
  336. //RUN RESET SWITCHES & LEDS
  337. addParam(ParamWidget::create<LEDBezel>(Vec(portX[2], main_lds_y), module, SEQ16::RUN_PARAM , 0.0f, 1.0f, 0.0f));
  338. addChild(ModuleLightWidget::create<MuteLight<RedLight>>(Vec(portX[2]+2.2, main_lds_y+2), module, SEQ16::RUNNING_LIGHT));
  339. addParam(ParamWidget::create<LEDBezel>(Vec(portX[3], main_lds_y), module, SEQ16::RESET_PARAM , 0.0f, 1.0f, 0.0f));
  340. addChild(ModuleLightWidget::create<MuteLight<RedLight>>(Vec(portX[3]+2.2, main_lds_y+2), module, SEQ16::RESET_LIGHT));
  341. //STEP TRIGGER
  342. addParam(ParamWidget::create<LEDBezel>(Vec(portX[11], main_lds_y+35), module, SEQ16::TRIGGER_PARAM , 0.0f, 1.0f, 0.0f));
  343. addChild(ModuleLightWidget::create<MuteLight<RedLight>>(Vec(portX[11]+2.2, main_lds_y+2+35), module, SEQ16::TRIGGER_LIGHT));
  344. addParam(ParamWidget::create<TL1105>(Vec(portX[9]+20, main_lds_y+40), module, SEQ16::PREV_STEP, 0.0f, 1.0f, 0.0f));
  345. addParam(ParamWidget::create<TL1105>(Vec(portX[10]+5, main_lds_y+40), module, SEQ16::NEXT_STEP, 0.0f, 1.0f, 0.0f));
  346. //GATE MODE SWITCH
  347. addParam(ParamWidget::create<as_CKSSThree>(Vec(portX[6]+2, main_lds_y-4), module, SEQ16::GATE_MODE_PARAM, 0.0f, 2.0f, 0.0f));
  348. //STEPS KNOBS
  349. addParam(ParamWidget::create<as_KnobBlack>(Vec(portX[7]-elements_offst, 56), module, SEQ16::STEPS_PARAM, 1.0f, 16.0f, 16.0f));
  350. static const float main_inputs_offst = 1;
  351. static const float main_inputs_y = 98;
  352. //SEQ VC INPUTS
  353. addInput(Port::create<as_PJ301MPort>(Vec(portX[1]- main_inputs_offst, main_inputs_y), Port::INPUT, module, SEQ16::CLOCK_INPUT));
  354. addInput(Port::create<as_PJ301MPort>(Vec(portX[2]-main_inputs_offst, main_inputs_y), Port::INPUT, module, SEQ16::EXT_CLOCK_INPUT));
  355. addInput(Port::create<as_PJ301MPort>(Vec(portX[3]-main_inputs_offst, main_inputs_y), Port::INPUT, module, SEQ16::RESET_INPUT));
  356. addInput(Port::create<as_PJ301MPort>(Vec(portX[7]-main_inputs_offst, main_inputs_y), Port::INPUT, module, SEQ16::STEPS_INPUT));
  357. //GATE/ROW LEDS
  358. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(portX[12]+elements_offst, main_lds_y+6), module, SEQ16::GATES_LIGHT));
  359. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(portX[13]+elements_offst, main_lds_y+6), module, SEQ16::ROW_LIGHTS));
  360. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(portX[14]+elements_offst, main_lds_y+6), module, SEQ16::ROW_LIGHTS + 1));
  361. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(portX[15]+elements_offst, main_lds_y+6), module, SEQ16::ROW_LIGHTS + 2));
  362. //GATE/ROW OUTPUTS
  363. addOutput(Port::create<as_PJ301MPort>(Vec(portX[12], 98), Port::OUTPUT, module, SEQ16::GATES_OUTPUT));
  364. addOutput(Port::create<as_PJ301MPort>(Vec(portX[13], 98), Port::OUTPUT, module, SEQ16::ROW1_OUTPUT));
  365. addOutput(Port::create<as_PJ301MPort>(Vec(portX[14], 98), Port::OUTPUT, module, SEQ16::ROW2_OUTPUT));
  366. addOutput(Port::create<as_PJ301MPort>(Vec(portX[15], 98), Port::OUTPUT, module, SEQ16::ROW3_OUTPUT));
  367. for (int i = 0; i < 16; i++) {
  368. //ROW KNOBS
  369. addParam(ParamWidget::create<as_KnobBlack>(Vec(portX[i]-elements_offst, 157), module, SEQ16::ROW1_PARAM + i, 0.0f, 10.0f, 0.0f));
  370. addParam(ParamWidget::create<as_KnobBlack>(Vec(portX[i]-elements_offst, 198), module, SEQ16::ROW2_PARAM + i, 0.0f, 10.0f, 0.0f));
  371. addParam(ParamWidget::create<as_KnobBlack>(Vec(portX[i]-elements_offst, 240), module, SEQ16::ROW3_PARAM + i, 0.0f, 10.0f, 0.0f));
  372. //GATE LEDS
  373. addParam(ParamWidget::create<LEDButton>(Vec(portX[i]+1.5, 284), module, SEQ16::GATE_PARAM + i, 0.0f, 1.0f, 0.0f));
  374. addChild(ModuleLightWidget::create<MediumLight<RedLight>>(Vec(portX[i]+5.8, 287.9), module, SEQ16::GATE_LIGHTS + i));
  375. //GATE STEPS OUT
  376. addOutput(Port::create<as_PJ301MPort>(Vec(portX[i]-2, 310), Port::OUTPUT, module, SEQ16::GATE_OUTPUT + i));
  377. }
  378. }
  379. struct SEQ16GateModeItem : MenuItem {
  380. SEQ16 *seq16;
  381. SEQ16::GateMode gateMode;
  382. void onAction(EventAction &e) override {
  383. seq16->gateMode = gateMode;
  384. }
  385. void step() override {
  386. rightText = CHECKMARK(seq16->gateMode == gateMode);
  387. }
  388. };
  389. Menu *SEQ16Widget::createContextMenu() {
  390. Menu *menu = ModuleWidget::createContextMenu();
  391. MenuLabel *spacerLabel = new MenuLabel();
  392. menu->addChild(spacerLabel);
  393. SEQ16 *seq16 = dynamic_cast<SEQ16*>(module);
  394. assert(seq16);
  395. MenuLabel *modeLabel = new MenuLabel();
  396. modeLabel->text = "Gate Mode";
  397. menu->addChild(modeLabel);
  398. SEQ16GateModeItem *triggerItem = new SEQ16GateModeItem();
  399. triggerItem->text = "Trigger";
  400. triggerItem->seq16 = seq16;
  401. triggerItem->gateMode = SEQ16::TRIGGER;
  402. menu->addChild(triggerItem);
  403. SEQ16GateModeItem *retriggerItem = new SEQ16GateModeItem();
  404. retriggerItem->text = "Retrigger";
  405. retriggerItem->seq16 = seq16;
  406. retriggerItem->gateMode = SEQ16::RETRIGGER;
  407. menu->addChild(retriggerItem);
  408. SEQ16GateModeItem *continuousItem = new SEQ16GateModeItem();
  409. continuousItem->text = "Continuous";
  410. continuousItem->seq16 = seq16;
  411. continuousItem->gateMode = SEQ16::CONTINUOUS;
  412. menu->addChild(continuousItem);
  413. return menu;
  414. }
  415. Model *modelSEQ16 = Model::create<SEQ16, SEQ16Widget>("AS", "SEQ16", "16-Step Sequencer", SEQUENCER_TAG);