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.

299 lines
10KB

  1. #include "plugin.hpp"
  2. struct SamplingModulator : Module {
  3. const static int numSteps = 8;
  4. enum ParamIds {
  5. RATE_PARAM,
  6. FINE_PARAM,
  7. INT_EXT_PARAM,
  8. ENUMS(STEP_PARAM, numSteps),
  9. NUM_PARAMS
  10. };
  11. enum InputIds {
  12. SYNC_INPUT,
  13. VOCT_INPUT,
  14. HOLD_INPUT,
  15. IN_INPUT,
  16. NUM_INPUTS
  17. };
  18. enum OutputIds {
  19. CLOCK_OUTPUT,
  20. TRIGG_OUTPUT,
  21. OUT_OUTPUT,
  22. NUM_OUTPUTS
  23. };
  24. enum LightIds {
  25. ENUMS(STEP_LIGHT, numSteps),
  26. NUM_LIGHTS
  27. };
  28. enum StepState {
  29. STATE_RESET,
  30. STATE_OFF,
  31. STATE_ON
  32. };
  33. enum ClockMode {
  34. CLOCK_EXTERNAL,
  35. CLOCK_INTERNAL
  36. };
  37. struct ClockTypeParam : ParamQuantity {
  38. std::string getDisplayValueString() override {
  39. if (module != nullptr && paramId == INT_EXT_PARAM) {
  40. return (module->params[INT_EXT_PARAM].getValue() == CLOCK_EXTERNAL) ? "External" : "Internal";
  41. }
  42. else {
  43. return "";
  44. }
  45. }
  46. };
  47. struct StepTypeParam : ParamQuantity {
  48. std::string getDisplayValueString() override {
  49. if (module != nullptr && STEP_PARAM <= paramId && STEP_PARAM < STEP_PARAM_LAST) {
  50. StepState stepState = (StepState) module->params[paramId].getValue();
  51. if (stepState == STATE_RESET) {
  52. return "Reset";
  53. }
  54. else if (stepState == STATE_OFF) {
  55. return "Off";
  56. }
  57. else {
  58. return "On";
  59. }
  60. }
  61. else {
  62. return "";
  63. }
  64. }
  65. };
  66. int numEffectiveSteps = numSteps;
  67. int currentStep = 0;
  68. StepState stepStates[numSteps];
  69. dsp::PulseGenerator triggerGenerator;
  70. dsp::SchmittTrigger holdDetector;
  71. dsp::SchmittTrigger clock;
  72. dsp::MinBlepGenerator<16, 32> squareMinBlep;
  73. dsp::MinBlepGenerator<16, 32> triggMinBlep;
  74. dsp::MinBlepGenerator<16, 32> holdMinBlep;
  75. bool removeDC = true;
  76. float stepPhase = 0.f;
  77. float heldValue = 0.f;
  78. /** Whether we are past the pulse width already */
  79. bool halfPhase = false;
  80. SamplingModulator() {
  81. config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
  82. configParam(RATE_PARAM, 0.0f, 1.f, 0.f, "Rate");
  83. configParam(FINE_PARAM, 0.f, 1.f, 0.f, "Fine tune");
  84. configParam<ClockTypeParam>(INT_EXT_PARAM, 0.f, 1.f, CLOCK_INTERNAL, "Clock");
  85. for (int i = 0; i < numSteps; i++) {
  86. configParam<StepTypeParam>(STEP_PARAM + i, 0.f, 2.f, STATE_ON, "Step " + std::to_string(i + 1));
  87. }
  88. }
  89. void process(const ProcessArgs& args) override {
  90. bool advanceStep = false;
  91. const bool isHoldOutRequired = outputs[OUT_OUTPUT].isConnected() && inputs[IN_INPUT].isConnected();
  92. const bool isClockOutRequired = outputs[CLOCK_OUTPUT].isConnected();
  93. const bool isTriggOutRequired = outputs[TRIGG_OUTPUT].isConnected();
  94. if (params[INT_EXT_PARAM].getValue() == CLOCK_EXTERNAL) {
  95. // if external mode, the SYNC/EXT. CLOCK input acts as a clock
  96. advanceStep = clock.process(rescale(inputs[SYNC_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f));
  97. }
  98. else {
  99. // if internal mode, the SYNC/EXT. CLOCK input acts as oscillator sync, resetting the phase
  100. if (clock.process(rescale(inputs[SYNC_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) {
  101. advanceStep = true;
  102. stepPhase = 0.f;
  103. halfPhase = false;
  104. }
  105. }
  106. if (holdDetector.process(rescale(inputs[HOLD_INPUT].getVoltage(), 0.1f, 2.f, 0.f, 1.f))) {
  107. float oldHeldValue = heldValue;
  108. heldValue = inputs[IN_INPUT].getVoltage();
  109. holdMinBlep.insertDiscontinuity(0, heldValue - oldHeldValue);
  110. }
  111. for (int i = 0; i < numSteps; i++) {
  112. stepStates[i] = (StepState) params[STEP_PARAM + i].getValue();
  113. }
  114. int numActiveSteps = 0;
  115. numEffectiveSteps = 8;
  116. for (int i = 0; i < numSteps; i++) {
  117. numActiveSteps += (stepStates[i] == STATE_ON);
  118. if (stepStates[i] == STATE_RESET) {
  119. numEffectiveSteps = i;
  120. break;
  121. }
  122. }
  123. const float pitch = 16.f * params[RATE_PARAM].getValue() + params[FINE_PARAM].getValue() + inputs[VOCT_INPUT].getVoltage();
  124. const float minDialFrequency = 1.0f;
  125. const float frequency = minDialFrequency * simd::pow(2.f, pitch);
  126. const float oldPhase = stepPhase;
  127. float deltaPhase = clamp(args.sampleTime * frequency, 1e-6f, 0.5f);
  128. stepPhase += deltaPhase;
  129. if (!halfPhase && stepPhase >= 0.5) {
  130. float crossing = -(stepPhase - 0.5) / deltaPhase;
  131. if (isClockOutRequired) {
  132. squareMinBlep.insertDiscontinuity(crossing, -2.f);
  133. }
  134. if (isTriggOutRequired && stepStates[currentStep] == STATE_ON) {
  135. triggMinBlep.insertDiscontinuity(crossing, -2.f);
  136. }
  137. halfPhase = true;
  138. }
  139. if (stepPhase >= 1.0f) {
  140. stepPhase -= 1.0f;
  141. if (isClockOutRequired) {
  142. float crossing = -stepPhase / deltaPhase;
  143. squareMinBlep.insertDiscontinuity(crossing, +2.f);
  144. }
  145. halfPhase = false;
  146. if (params[INT_EXT_PARAM].getValue() == CLOCK_INTERNAL) {
  147. advanceStep = true;
  148. }
  149. }
  150. if (advanceStep) {
  151. currentStep = (currentStep + 1) % std::max(1, numEffectiveSteps);
  152. if (stepStates[currentStep] == STATE_ON) {
  153. const float crossing = -(oldPhase + deltaPhase - 1.0) / deltaPhase;
  154. triggMinBlep.insertDiscontinuity(crossing, +2.f);
  155. triggerGenerator.trigger();
  156. if (!holdDetector.isHigh() && isHoldOutRequired) {
  157. float oldHeldValue = heldValue;
  158. heldValue = inputs[IN_INPUT].getVoltage();
  159. holdMinBlep.insertDiscontinuity(crossing, heldValue - oldHeldValue);
  160. }
  161. }
  162. }
  163. const float holdOutput = isHoldOutRequired ? (heldValue + holdMinBlep.process()) : 0.f;
  164. outputs[OUT_OUTPUT].setVoltage(holdOutput);
  165. if (isClockOutRequired) {
  166. float square = (stepPhase < 0.5) ? 2.f : 0.f;
  167. square += squareMinBlep.process();
  168. square -= 1.0f * removeDC;
  169. outputs[CLOCK_OUTPUT].setVoltage(5.f * square);
  170. }
  171. else {
  172. outputs[CLOCK_OUTPUT].setVoltage(0.f);
  173. }
  174. if (params[INT_EXT_PARAM].getValue() == CLOCK_INTERNAL) {
  175. if (isTriggOutRequired) {
  176. float trigger = (stepPhase < 0.5 && stepStates[currentStep] == STATE_ON) ? 2.f : 0.f;
  177. trigger += triggMinBlep.process();
  178. if (removeDC) {
  179. trigger -= 1.0f;
  180. if (numEffectiveSteps > 0) {
  181. trigger += (float)(numEffectiveSteps - numActiveSteps) / (numEffectiveSteps);
  182. }
  183. }
  184. outputs[TRIGG_OUTPUT].setVoltage(5.f * trigger);
  185. }
  186. else {
  187. outputs[TRIGG_OUTPUT].setVoltage(0.f);
  188. }
  189. }
  190. else {
  191. // if externally clocked, just give standard triggers
  192. outputs[TRIGG_OUTPUT].setVoltage(10.f * triggerGenerator.process(args.sampleTime));
  193. }
  194. for (int i = 0; i < numSteps; i++) {
  195. lights[STEP_LIGHT + i].setBrightness(currentStep == i);
  196. }
  197. }
  198. };
  199. struct SamplingModulatorWidget : ModuleWidget {
  200. SamplingModulatorWidget(SamplingModulator* module) {
  201. setModule(module);
  202. setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/SamplingModulator.svg")));
  203. addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, 0)));
  204. addChild(createWidget<Knurlie>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  205. addChild(createWidget<Knurlie>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  206. addChild(createWidget<Knurlie>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  207. addParam(createParamCentered<Davies1900hWhiteKnob>(mm2px(Vec(9.72, 38.019)), module, SamplingModulator::RATE_PARAM));
  208. addParam(createParamCentered<Davies1900hWhiteKnob>(mm2px(Vec(30.921, 38.019)), module, SamplingModulator::FINE_PARAM));
  209. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(20.313, 52.642)), module, SamplingModulator::INT_EXT_PARAM));
  210. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(8.319, 57.761)), module, SamplingModulator::STEP_PARAM + 0));
  211. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(8.319, 71.758)), module, SamplingModulator::STEP_PARAM + 1));
  212. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(8.319, 85.769)), module, SamplingModulator::STEP_PARAM + 2));
  213. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(8.319, 99.804)), module, SamplingModulator::STEP_PARAM + 3));
  214. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(32.326, 57.761)), module, SamplingModulator::STEP_PARAM + 4));
  215. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(32.326, 71.758)), module, SamplingModulator::STEP_PARAM + 5));
  216. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(32.326, 85.769)), module, SamplingModulator::STEP_PARAM + 6));
  217. addParam(createParamCentered<BefacoSwitch>(mm2px(Vec(32.326, 99.804)), module, SamplingModulator::STEP_PARAM + 7));
  218. addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(7.426, 16.737)), module, SamplingModulator::SYNC_INPUT));
  219. addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(20.313, 28.175)), module, SamplingModulator::VOCT_INPUT));
  220. addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(20.342, 111.762)), module, SamplingModulator::HOLD_INPUT));
  221. addInput(createInputCentered<BefacoInputPort>(mm2px(Vec(7.426, 114.484)), module, SamplingModulator::IN_INPUT));
  222. addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(20.313, 14.417)), module, SamplingModulator::CLOCK_OUTPUT));
  223. addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(33.224, 16.737)), module, SamplingModulator::TRIGG_OUTPUT));
  224. addOutput(createOutputCentered<BefacoOutputPort>(mm2px(Vec(33.224, 114.484)), module, SamplingModulator::OUT_OUTPUT));
  225. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(16.921, 62.208)), module, SamplingModulator::STEP_LIGHT + 0));
  226. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(16.921, 73.011)), module, SamplingModulator::STEP_LIGHT + 1));
  227. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(16.921, 83.814)), module, SamplingModulator::STEP_LIGHT + 2));
  228. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(16.921, 94.617)), module, SamplingModulator::STEP_LIGHT + 3));
  229. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(23.722, 62.208)), module, SamplingModulator::STEP_LIGHT + 4));
  230. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(23.722, 73.011)), module, SamplingModulator::STEP_LIGHT + 5));
  231. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(23.722, 83.814)), module, SamplingModulator::STEP_LIGHT + 6));
  232. addChild(createLightCentered<SmallLight<RedLight>>(mm2px(Vec(23.722, 94.617)), module, SamplingModulator::STEP_LIGHT + 7));
  233. }
  234. struct DCMenuItem : MenuItem {
  235. SamplingModulator* module;
  236. void onAction(const event::Action& e) override {
  237. module->removeDC ^= true;
  238. }
  239. };
  240. void appendContextMenu(Menu* menu) override {
  241. SamplingModulator* module = dynamic_cast<SamplingModulator*>(this->module);
  242. assert(module);
  243. menu->addChild(new MenuSeparator());
  244. DCMenuItem* dcItem = createMenuItem<DCMenuItem>("Remove DC Offset", CHECKMARK(module->removeDC));
  245. dcItem->module = module;
  246. menu->addChild(dcItem);
  247. }
  248. };
  249. Model* modelSamplingModulator = createModel<SamplingModulator, SamplingModulatorWidget>("SamplingModulator");