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.

169 lines
5.6KB

  1. #include "dsp/digital.hpp"
  2. #include <iostream>
  3. #include "RJModules.hpp"
  4. namespace rack_plugin_RJModules {
  5. struct LowFrequencyOscillator {
  6. float phase = 0.0;
  7. float pw = 0.5;
  8. float freq = 1.0;
  9. bool offset = false;
  10. bool invert = false;
  11. SchmittTrigger resetTrigger;
  12. LowFrequencyOscillator() {}
  13. void setPitch(float pitch) {
  14. pitch = fminf(pitch, 8.0);
  15. freq = powf(2.0, pitch);
  16. }
  17. float getFreq() {
  18. return freq;
  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 saw(float x) {
  32. return 2.0 * (x - roundf(x));
  33. }
  34. float saw() {
  35. if (offset)
  36. return invert ? 2.0 * (1.0 - phase) : 2.0 * phase;
  37. else
  38. return saw(phase) * (invert ? -1.0 : 1.0);
  39. }
  40. float light() {
  41. return sinf(2*M_PI * phase);
  42. }
  43. };
  44. struct Supersaw : Module {
  45. enum ParamIds {
  46. OFFSET_PARAM,
  47. INVERT_PARAM,
  48. FREQ_PARAM,
  49. DETUNE_PARAM,
  50. MIX_PARAM,
  51. THREE_OSC_PARAM,
  52. NUM_PARAMS
  53. };
  54. enum InputIds {
  55. FREQ_CV_INPUT,
  56. DETUNE_CV_INPUT,
  57. MIX_CV_INPUT,
  58. RESET_INPUT,
  59. PW_INPUT,
  60. NUM_INPUTS
  61. };
  62. enum OutputIds {
  63. SAW_OUTPUT,
  64. NUM_OUTPUTS
  65. };
  66. enum LightIds {
  67. PHASE_POS_LIGHT,
  68. PHASE_NEG_LIGHT,
  69. NUM_LIGHTS
  70. };
  71. LowFrequencyOscillator oscillator;
  72. LowFrequencyOscillator oscillator2;
  73. LowFrequencyOscillator oscillator3;
  74. float DETUNE_STEP = .075;
  75. Supersaw() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  76. void step() override;
  77. };
  78. void Supersaw::step() {
  79. float root_pitch = params[FREQ_PARAM].value * clamp(inputs[FREQ_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f);
  80. oscillator.setPitch(root_pitch);
  81. oscillator.offset = (params[OFFSET_PARAM].value > 0.0);
  82. oscillator.invert = (params[INVERT_PARAM].value <= 0.0);
  83. oscillator.step(1.0 / engineGetSampleRate());
  84. oscillator.setReset(inputs[RESET_INPUT].value);
  85. oscillator2.setPitch(root_pitch + (params[DETUNE_PARAM].value * DETUNE_STEP * clamp(inputs[DETUNE_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f)));
  86. oscillator2.offset = (params[OFFSET_PARAM].value > 0.0);
  87. oscillator2.invert = (params[INVERT_PARAM].value <= 0.0);
  88. oscillator2.step(1.0 / engineGetSampleRate());
  89. oscillator2.setReset(inputs[RESET_INPUT].value);
  90. oscillator3.setPitch(root_pitch - (params[DETUNE_PARAM].value * DETUNE_STEP * clamp(inputs[DETUNE_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f)));
  91. oscillator3.offset = (params[OFFSET_PARAM].value > 0.0);
  92. oscillator3.invert = (params[INVERT_PARAM].value <= 0.0);
  93. oscillator3.step(1.0 / engineGetSampleRate());
  94. oscillator3.setReset(inputs[RESET_INPUT].value);
  95. float osc3_saw = oscillator3.saw();
  96. if (params[OFFSET_PARAM].value < 1){
  97. osc3_saw = 0;
  98. } else{
  99. osc3_saw = oscillator3.saw();
  100. }
  101. float mix_percent = params[MIX_PARAM].value * clamp(inputs[MIX_CV_INPUT].normalize(10.0f) / 10.0f, 0.0f, 1.0f);
  102. outputs[SAW_OUTPUT].value = 5.0 * (( oscillator.saw() + (oscillator2.saw() * mix_percent) + (osc3_saw * mix_percent) / 3));
  103. lights[PHASE_POS_LIGHT].setBrightnessSmooth(fmaxf(0.0, oscillator.light()));
  104. lights[PHASE_NEG_LIGHT].setBrightnessSmooth(fmaxf(0.0, -oscillator.light()));
  105. }
  106. struct SupersawWidget: ModuleWidget {
  107. SupersawWidget(Supersaw *module);
  108. };
  109. SupersawWidget::SupersawWidget(Supersaw *module) : ModuleWidget(module) {
  110. box.size = Vec(15*10, 380);
  111. {
  112. SVGPanel *panel = new SVGPanel();
  113. panel->box.size = box.size;
  114. panel->setBackground(SVG::load(assetPlugin(plugin, "res/Supersaw.svg")));
  115. addChild(panel);
  116. }
  117. addChild(Widget::create<ScrewSilver>(Vec(15, 0)));
  118. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 0)));
  119. addChild(Widget::create<ScrewSilver>(Vec(15, 365)));
  120. addChild(Widget::create<ScrewSilver>(Vec(box.size.x-30, 365)));
  121. addParam(ParamWidget::create<CKSS>(Vec(119, 100), module, Supersaw::OFFSET_PARAM, 0.0, 1.0, 1.0));
  122. addParam(ParamWidget::create<CKSS>(Vec(119, 180), module, Supersaw::INVERT_PARAM, 0.0, 1.0, 1.0));
  123. addParam(ParamWidget::create<CKSS>(Vec(119, 260), module, Supersaw::THREE_OSC_PARAM, 0.0, 1.0, 1.0));
  124. addParam(ParamWidget::create<RoundHugeBlackKnob>(Vec(47, 61), module, Supersaw::FREQ_PARAM, 0.0, 8.0, 5.0));
  125. addParam(ParamWidget::create<RoundHugeBlackKnob>(Vec(47, 143), module, Supersaw::DETUNE_PARAM, 0.0, 1.0, 0.1));
  126. addParam(ParamWidget::create<RoundHugeBlackKnob>(Vec(47, 228), module, Supersaw::MIX_PARAM, 0.0, 1.0, 1.0));
  127. addInput(Port::create<PJ301MPort>(Vec(22, 100), Port::INPUT, module, Supersaw::FREQ_CV_INPUT));
  128. addInput(Port::create<PJ301MPort>(Vec(22, 190), Port::INPUT, module, Supersaw::DETUNE_CV_INPUT));
  129. addInput(Port::create<PJ301MPort>(Vec(22, 270), Port::INPUT, module, Supersaw::MIX_CV_INPUT));
  130. addInput(Port::create<PJ301MPort>(Vec(38, 310), Port::INPUT, module, Supersaw::RESET_INPUT));
  131. addOutput(Port::create<PJ301MPort>(Vec(100, 310), Port::OUTPUT, module, Supersaw::SAW_OUTPUT));
  132. addChild(ModuleLightWidget::create<SmallLight<GreenRedLight>>(Vec(99, 60), module, Supersaw::PHASE_POS_LIGHT));
  133. }
  134. } // namespace rack_plugin_RJModules
  135. using namespace rack_plugin_RJModules;
  136. RACK_PLUGIN_MODEL_INIT(RJModules, Supersaw) {
  137. Model *modelSupersaw = Model::create<Supersaw, SupersawWidget>("RJModules", "Supersaw", "[GEN] Supersaw", LFO_TAG);
  138. return modelSupersaw;
  139. }