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.

198 lines
5.6KB

  1. #include "FrozenWasteland.hpp"
  2. #include "dsp/digital.hpp"
  3. namespace rack_plugin_FrozenWasteland {
  4. struct LowFrequencyOscillator {
  5. float phase = 0.0;
  6. float pw = 0.5;
  7. float freq = 1.0;
  8. bool offset = false;
  9. bool invert = false;
  10. SchmittTrigger resetTrigger;
  11. LowFrequencyOscillator() {}
  12. void setPitch(float pitch) {
  13. pitch = fminf(pitch, 8.0);
  14. freq = powf(2.0, pitch);
  15. }
  16. void setPulseWidth(float pw_) {
  17. const float pwMin = 0.01;
  18. pw = clamp(pw_, pwMin, 1.0f - pwMin);
  19. }
  20. void setReset(float reset) {
  21. if (resetTrigger.process(reset)) {
  22. phase = 0.0;
  23. }
  24. }
  25. void step(float dt) {
  26. float deltaPhase = fminf(freq * dt, 0.5);
  27. phase += deltaPhase;
  28. if (phase >= 1.0)
  29. phase -= 1.0;
  30. }
  31. float sin() {
  32. if (offset)
  33. return 1.0 - cosf(2*M_PI * phase) * (invert ? -1.0 : 1.0);
  34. else
  35. return sinf(2*M_PI * phase) * (invert ? -1.0 : 1.0);
  36. }
  37. float tri(float x) {
  38. return 4.0 * fabsf(x - roundf(x));
  39. }
  40. float tri() {
  41. if (offset)
  42. return tri(invert ? phase - 0.5 : phase);
  43. else
  44. return -1.0 + tri(invert ? phase - 0.25 : phase - 0.75);
  45. }
  46. float saw(float x) {
  47. return 2.0 * (x - roundf(x));
  48. }
  49. float saw() {
  50. if (offset)
  51. return invert ? 2.0 * (1.0 - phase) : 2.0 * phase;
  52. else
  53. return saw(phase) * (invert ? -1.0 : 1.0);
  54. }
  55. float sqr() {
  56. float sqr = (phase < pw) ^ invert ? 1.0 : -1.0;
  57. return offset ? sqr + 1.0 : sqr;
  58. }
  59. float light() {
  60. return sinf(2*M_PI * phase);
  61. }
  62. };
  63. struct QuantussyCell : Module {
  64. enum ParamIds {
  65. FREQ_PARAM,
  66. CV_ATTENUVERTER_PARAM,
  67. NUM_PARAMS
  68. };
  69. enum InputIds {
  70. CASTLE_INPUT,
  71. CV_INPUT,
  72. CV_AMOUNT_INPUT,
  73. NUM_INPUTS
  74. };
  75. enum OutputIds {
  76. CASTLE_OUTPUT,
  77. SIN_OUTPUT,
  78. TRI_OUTPUT,
  79. SAW_OUTPUT,
  80. SQR_OUTPUT,
  81. NUM_OUTPUTS
  82. };
  83. enum LightIds {
  84. BLINK_LIGHT,
  85. NUM_LIGHTS
  86. };
  87. LowFrequencyOscillator oscillator;
  88. //Stuff for S&Hs
  89. SchmittTrigger _castleTrigger, _cvTrigger;
  90. float _value1, _value2;
  91. //Castle S&H is #1, CV #2
  92. QuantussyCell() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  93. void step() override;
  94. // For more advanced Module features, read Rack's engine.hpp header file
  95. // - toJson, fromJson: serialization of internal data
  96. // - onSampleRateChange: event triggered by a change of sample rate
  97. // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
  98. };
  99. void QuantussyCell::step() {
  100. oscillator.setPitch(params[FREQ_PARAM].value + _value2);
  101. oscillator.step(1.0 / engineGetSampleRate());
  102. outputs[SIN_OUTPUT].value = 5.0 * oscillator.sin();
  103. outputs[TRI_OUTPUT].value = 5.0 * oscillator.tri();
  104. outputs[SAW_OUTPUT].value = 5.0 * oscillator.saw();
  105. float squareOutput = 5.0 * oscillator.sqr(); //Used a lot :)
  106. outputs[SQR_OUTPUT].value = squareOutput;
  107. //Process Castle
  108. if (_castleTrigger.process(squareOutput)) {
  109. if (inputs[CASTLE_INPUT].active) {
  110. _value1 = inputs[CASTLE_INPUT].value;
  111. }
  112. else {
  113. _value1 = 0; //Maybe at some point add a default noise source, but not for now
  114. }
  115. }
  116. outputs[CASTLE_OUTPUT].value = _value1;
  117. //Process CV
  118. if (_cvTrigger.process(squareOutput)) {
  119. if (inputs[CV_INPUT].active) {
  120. float attenuverting = params[CV_ATTENUVERTER_PARAM].value + (inputs[CV_AMOUNT_INPUT].value / 10.0f);
  121. _value2 = inputs[CV_INPUT].value * attenuverting;
  122. }
  123. else {
  124. _value2 = 0; //Maybe at some point add a default noise source, but not for now
  125. }
  126. }
  127. lights[BLINK_LIGHT].setBrightnessSmooth(fmaxf(0.0, oscillator.light()));
  128. }
  129. struct QuantussyCellWidget : ModuleWidget {
  130. QuantussyCellWidget(QuantussyCell *module);
  131. };
  132. QuantussyCellWidget::QuantussyCellWidget(QuantussyCell *module) : ModuleWidget(module) {
  133. box.size = Vec(15*6, RACK_GRID_HEIGHT);
  134. {
  135. SVGPanel *panel = new SVGPanel();
  136. panel->box.size = box.size;
  137. panel->setBackground(SVG::load(assetPlugin(plugin, "res/QuantussyCell.svg")));
  138. addChild(panel);
  139. }
  140. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, 0)));
  141. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0)));
  142. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH - 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  143. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  144. addParam(ParamWidget::create<RoundLargeBlackKnob>(Vec(28, 64), module, QuantussyCell::FREQ_PARAM, -3.0, 3.0, 0.0));
  145. addParam(ParamWidget::create<RoundBlackKnob>(Vec(13, 182), module, QuantussyCell::CV_ATTENUVERTER_PARAM, -1.0, 1.0, 1.0));
  146. addInput(Port::create<PJ301MPort>(Vec(35, 113), Port::INPUT, module, QuantussyCell::CASTLE_INPUT));
  147. addInput(Port::create<PJ301MPort>(Vec(50, 205), Port::INPUT, module, QuantussyCell::CV_INPUT));
  148. addInput(Port::create<PJ301MPort>(Vec(15, 213), Port::INPUT, module, QuantussyCell::CV_AMOUNT_INPUT));
  149. addOutput(Port::create<PJ301MPort>(Vec(35, 160), Port::OUTPUT, module, QuantussyCell::CASTLE_OUTPUT));
  150. addOutput(Port::create<PJ301MPort>(Vec(15, 255), Port::OUTPUT, module, QuantussyCell::SIN_OUTPUT));
  151. addOutput(Port::create<PJ301MPort>(Vec(50, 255), Port::OUTPUT, module, QuantussyCell::TRI_OUTPUT));
  152. addOutput(Port::create<PJ301MPort>(Vec(15, 301), Port::OUTPUT, module, QuantussyCell::SQR_OUTPUT));
  153. addOutput(Port::create<PJ301MPort>(Vec(50, 301), Port::OUTPUT, module, QuantussyCell::SAW_OUTPUT));
  154. addChild(ModuleLightWidget::create<LargeLight<BlueLight>>(Vec(68, 70), module, QuantussyCell::BLINK_LIGHT));
  155. }
  156. } // namespace rack_plugin_FrozenWasteland
  157. using namespace rack_plugin_FrozenWasteland;
  158. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, QuantussyCell) {
  159. Model *modelQuantussyCell = Model::create<QuantussyCell, QuantussyCellWidget>("Frozen Wasteland", "QuantussyCell", "Quantussy Cell", LOGIC_TAG);
  160. return modelQuantussyCell;
  161. }