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.

273 lines
8.0KB

  1. /*
  2. --no-title-yet-- (SlimSeq)
  3. 16 step seq (based on m.lueders' sequential switch)
  4. TODO:
  5. -if-bool cleanup
  6. -save modestates and resetpos
  7. -adjustable step count
  8. -playmodes: pendulum/ping-pong, one-shot
  9. -output slew (portamento)
  10. -useful unclocked/held operation
  11. -(maybe) momentary control inputs (instead of flips) / optional
  12. -(maybe) unipolar/bipolar options
  13. -(maybe) gates
  14. */////////////////////////////////////////////////////////////////////////////
  15. #include "pvc.hpp"
  16. #include "dsp/digital.hpp" // SchmittTrigger PulseGenerator
  17. #include "dsp/filter.hpp" // SlewLimiter
  18. namespace rack_plugin_PvC {
  19. #define STEPCOUNT 16
  20. struct SlimSeq : Module {
  21. enum ParamIds {
  22. STEP1_KNOB,
  23. SCL_KNOB = STEP1_KNOB + STEPCOUNT,
  24. STEP1_SEL,
  25. CLOCK_UI = STEP1_SEL + STEPCOUNT,
  26. REVERSE_UI,
  27. RNDMODE_UI,
  28. HOLD_UI,
  29. RESET_UI,
  30. OFF_KNOB,
  31. NUM_PARAMS
  32. };
  33. enum InputIds {
  34. CLOCK_IN,
  35. REVERSE_IN,
  36. RNDMODE_IN,
  37. HOLD_IN,
  38. RESET_IN,
  39. STEP1_IN,
  40. NUM_INPUTS = STEP1_IN + STEPCOUNT
  41. };
  42. enum OutputIds {
  43. OUT,
  44. NUM_OUTPUTS
  45. };
  46. enum LightIds {
  47. REVERSE_LIGHT,
  48. RNDMODE_LIGHT,
  49. FORWARD_LIGHT,
  50. STEP1_LIGHT,
  51. OUT_POS_LED = STEP1_LIGHT + STEPCOUNT,
  52. OUT_NEG_LED,
  53. NUM_LIGHTS
  54. };
  55. SlimSeq() : Module( NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS ) {
  56. reset();
  57. };
  58. void step() override;
  59. int currentPos = 0;
  60. int counterPos = 0;
  61. int resetPos = 0;
  62. bool isRunning = false;
  63. bool isHold = false;
  64. bool isRandom = false;
  65. bool isReverse = false;
  66. bool isHopper = false;
  67. float stepInput = 0.0f;
  68. SchmittTrigger clockTrigger, reverseTrigger, randomTrigger, holdTrigger, resetTrigger;
  69. SchmittTrigger posButtons[STEPCOUNT];
  70. void reset() override {
  71. currentPos = 0;
  72. counterPos = 0;
  73. resetPos = 0;
  74. isRunning = false;
  75. isHold = false;
  76. isRandom = false;
  77. isReverse = false;
  78. isHopper = false;
  79. stepInput = 0.0f;
  80. for(int i=0; i<STEPCOUNT; i++){
  81. lights[STEP1_LIGHT + i].value = 0.0f;
  82. }
  83. };
  84. };
  85. void SlimSeq::step() {
  86. stepInput = inputs[STEP1_IN+currentPos].normalize(5.0f) * params[STEP1_KNOB+currentPos].value;
  87. isRunning = (isHold) ? false : inputs[CLOCK_IN].active;
  88. // clocked
  89. if(isRunning) {
  90. if (clockTrigger.process(inputs[CLOCK_IN].value) ) {
  91. counterPos += (isReverse) ? -1 : 1;
  92. if (isRandom) {
  93. if (isHopper) {
  94. currentPos += round(randomUniform()*15.0f);
  95. }
  96. else {
  97. currentPos += (round(randomUniform())) ? 1 : -1;
  98. }
  99. }
  100. else {
  101. currentPos = counterPos;
  102. }
  103. }
  104. }
  105. // playmode triggers & controls
  106. if (isRandom) { // in rnd mode use rev trigger to switch hopper/walker
  107. if (reverseTrigger.process(inputs[REVERSE_IN].value + params[REVERSE_UI].value))
  108. isHopper = !isHopper;
  109. }
  110. else {
  111. if (reverseTrigger.process(inputs[REVERSE_IN].value + params[REVERSE_UI].value))
  112. isReverse = !isReverse;
  113. }
  114. if (randomTrigger.process(inputs[RNDMODE_IN].value + params[RNDMODE_UI].value))
  115. isRandom = !isRandom;
  116. if (holdTrigger.process(inputs[HOLD_IN].value + params[HOLD_UI].value))
  117. isHold = !isHold;
  118. if (resetTrigger.process(inputs[RESET_IN].value + params[RESET_UI].value)) {
  119. counterPos = resetPos;
  120. currentPos = resetPos;
  121. }
  122. // position selectors
  123. for(int i=0; i<STEPCOUNT; i++) {
  124. if(posButtons[i].process(params[STEP1_SEL+i].value)) {
  125. resetPos = i;
  126. currentPos = (!isRunning) ? i : currentPos;
  127. }
  128. }
  129. // loop bg counter (to have a good return point when leaving rnd)
  130. while ( counterPos < 0 )
  131. counterPos += STEPCOUNT;
  132. while ( counterPos > (STEPCOUNT-1) )
  133. counterPos -= STEPCOUNT;
  134. // loop the current
  135. while ( currentPos < 0 )
  136. currentPos += STEPCOUNT;
  137. while ( currentPos > (STEPCOUNT-1) )
  138. currentPos -= STEPCOUNT;
  139. // calc out
  140. outputs[OUT].value = clamp(stepInput * params[SCL_KNOB].value + params[OFF_KNOB].value, -10.0f, 10.0f);
  141. // lights
  142. // direction
  143. lights[REVERSE_LIGHT].value = isHold ? 1.0f : isRandom&&isHopper ? 0.0f : isRandom&&!isHopper ? 1.0f : isReverse ? 1.0f : 0.0f;
  144. lights[RNDMODE_LIGHT].value = isHold ? 1.0f : isRandom&&isHopper ? 1.0f : isRandom&&!isHopper ? 0.0f : 0.0f;
  145. lights[FORWARD_LIGHT].value = isHold ? 1.0f : isRandom&&isHopper ? 0.0f : isRandom&&!isHopper ? 1.0f : isReverse ? 0.0f : 1.0f;
  146. // steplights
  147. for(int i=0; i<STEPCOUNT; i++) {
  148. lights[STEP1_LIGHT+i].value = (i == currentPos) ? 1.0f : (i == resetPos) ? 0.5f : 0.0f;
  149. }
  150. // out
  151. lights[OUT_POS_LED].value = (outputs[OUT].value < 0) ? -outputs[OUT].value*0.1f : 0.0f;
  152. lights[OUT_NEG_LED].value = (outputs[OUT].value > 0) ? outputs[OUT].value*0.1f : 0.0f;
  153. };
  154. struct StepButton : SVGSwitch, MomentarySwitch {
  155. StepButton() {
  156. addFrame(SVG::load(assetPlugin(plugin, "res/components/empty.svg")));
  157. box.size = Vec(73,30);
  158. }
  159. };
  160. // struct ControlButton : StepButton {
  161. // ControlButton() {
  162. // box.size = Vec(73,30);
  163. // }
  164. // };
  165. struct SlimSeqWidget : ModuleWidget {
  166. SlimSeqWidget(SlimSeq *module);
  167. };
  168. SlimSeqWidget::SlimSeqWidget(SlimSeq *module) : ModuleWidget(module) {
  169. setPanel(SVG::load(assetPlugin(plugin, "res/panels/SlimSeq.svg")));
  170. // screws
  171. addChild(Widget::create<ScrewHead2>(Vec(15, 0)));
  172. addChild(Widget::create<ScrewHead4>(Vec(box.size.x-30, 0)));
  173. addChild(Widget::create<ScrewHead3>(Vec(15, 365)));
  174. addChild(Widget::create<ScrewHead1>(Vec(box.size.x-30, 365)));
  175. // control inputs
  176. addInput(Port::create<InPortBin>(Vec(4,22), Port::INPUT, module,SlimSeq::CLOCK_IN));
  177. //addParam(ParamWidget::create<ControlButton>(Vec(48,70),module,SlimSeq::CLOCK_UI , 0, 1, 0));
  178. addInput(Port::create<InPortBin>(Vec(34,22), Port::INPUT, module,SlimSeq::REVERSE_IN));
  179. // addParam(ParamWidget::create<ControlButton>(Vec(48,270),module,SlimSeq::REVERSE_UI , 0, 1, 0));
  180. addInput(Port::create<InPortBin>(Vec(64,22), Port::INPUT, module,SlimSeq::HOLD_IN));
  181. // addParam(ParamWidget::create<ControlButton>(Vec(48,270),module,SlimSeq::HOLD_UI , 0, 1, 0));
  182. addInput(Port::create<InPortBin>(Vec(94,22), Port::INPUT, module,SlimSeq::RNDMODE_IN));
  183. // addParam(ParamWidget::create<ControlButton>(Vec(48,270),module,SlimSeq::RNDMODE_UI , 0, 1, 0));
  184. addInput(Port::create<InPortBin>(Vec(124,22), Port::INPUT, module,SlimSeq::RESET_IN));
  185. // addParam(ParamWidget::create<ControlButton>(Vec(48,270),module,SlimSeq::RESET_UI , 0, 1, 0));
  186. // direction lights
  187. addChild(ModuleLightWidget::create<FourPixLight<OrangeLED>>(Vec(67,62),module,SlimSeq::REVERSE_LIGHT));
  188. addChild(ModuleLightWidget::create<FourPixLight<OrangeLED>>(Vec(73,62),module,SlimSeq::RNDMODE_LIGHT));
  189. addChild(ModuleLightWidget::create<FourPixLight<OrangeLED>>(Vec(79,62),module,SlimSeq::FORWARD_LIGHT));
  190. // steps
  191. for (int i = 0; i < 8; i++) {
  192. int top = 31;
  193. // left 16-9
  194. addParam(ParamWidget::create<StepButton>(Vec(1,69 + top * i),module,SlimSeq::STEP1_SEL + STEPCOUNT-1 - i , 0, 1, 0));
  195. addParam(ParamWidget::create<PvCKnob>(Vec(16,73 + top * i),module,SlimSeq::STEP1_KNOB + STEPCOUNT-1 - i , -1.0f, 1.0f, 0.0f));
  196. addInput(Port::create<InPortCtrl>(Vec(40,73 + top * i), Port::INPUT, module,SlimSeq::STEP1_IN + + STEPCOUNT-1 - i));
  197. addChild(ModuleLightWidget::create<FourPixLight<BlueLED>>(Vec(65,82 + top * i),module,SlimSeq::STEP1_LIGHT + STEPCOUNT-1 - i));
  198. // right 1-8
  199. addParam(ParamWidget::create<StepButton>(Vec(76,69 + top * i),module,SlimSeq::STEP1_SEL + i , 0, 1, 0));
  200. addChild(ModuleLightWidget::create<FourPixLight<BlueLED>>(Vec(81,82 + top * i),module,SlimSeq::STEP1_LIGHT + i));
  201. addInput(Port::create<InPortCtrl>(Vec(88,73 + top * i), Port::INPUT, module,SlimSeq::STEP1_IN + i));
  202. addParam(ParamWidget::create<PvCKnob>(Vec(112,73 + top * i),module,SlimSeq::STEP1_KNOB + i, -1.0f, 1.0f, 0.0f));
  203. }
  204. // main out
  205. addParam(ParamWidget::create<PvCKnob>(Vec(36,323),module,SlimSeq::SCL_KNOB, -1.0f, 1.0f, 1.0f));
  206. addChild(ModuleLightWidget::create<FourPixLight<GreenRedLED>>(Vec(73,330),module,SlimSeq::OUT_POS_LED));
  207. addOutput(Port::create<OutPortVal>(Vec(64,336), Port::OUTPUT, module,SlimSeq::OUT));
  208. addParam(ParamWidget::create<PvCKnob>(Vec(92,323),module,SlimSeq::OFF_KNOB, -5.0f, 5.0f, 0.0f));
  209. }
  210. } // namespace rack_plugin_PvC
  211. using namespace rack_plugin_PvC;
  212. RACK_PLUGIN_MODEL_INIT(PvC, SlimSeq) {
  213. Model *modelSlimSeq = Model::create<SlimSeq, SlimSeqWidget>(
  214. "PvC", "SlimSeq", "SlimSeq", SEQUENCER_TAG, SWITCH_TAG);
  215. return modelSlimSeq;
  216. }