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.

219 lines
6.1KB

  1. #include "dBiz.hpp"
  2. #include "dsp/functions.hpp"
  3. #include "dsp/decimator.hpp"
  4. #include "dsp/filter.hpp"
  5. namespace rack_plugin_dBiz {
  6. extern float sawTable[2048];
  7. template <int OVERSAMPLE, int QUALITY>
  8. struct subBank
  9. {
  10. float phase = 0.0;
  11. float freq;
  12. float pitch;
  13. Decimator<OVERSAMPLE, QUALITY> sawDecimator;
  14. // For analog detuning effect
  15. float pitchSlew = 0.0f;
  16. int pitchSlewIndex = 0;
  17. float sawBuffer[OVERSAMPLE] = {};
  18. //void setPitch(float pitchKnob, float pitchCv)
  19. void setPitch(float pitchKnob, float pitchCv)
  20. {
  21. // Compute frequency
  22. pitch = pitchKnob;
  23. const float pitchSlewAmount = 3.0f;
  24. pitch += pitchSlew * pitchSlewAmount;
  25. pitch += pitchCv;
  26. // Note C3
  27. freq = 261.626f * powf(2.0, pitch / 12.0);
  28. // Accumulate the phase
  29. }
  30. void process(float deltaTime) {
  31. // Adjust pitch slew
  32. if (++pitchSlewIndex > 32) {
  33. const float pitchSlewTau = 100.0f; // Time constant for leaky integrator in seconds
  34. pitchSlew += (randomNormal() - pitchSlew / pitchSlewTau) * engineGetSampleTime();
  35. pitchSlewIndex = 0;
  36. }
  37. // Advance phase
  38. float deltaPhase = clamp(freq * deltaTime, 1e-6, 0.5f);
  39. for (int i = 0; i < OVERSAMPLE; i++) {
  40. sawBuffer[i] = 1.66f * interpolateLinear(sawTable, phase * 2047.f);
  41. // Advance phase
  42. phase += deltaPhase / OVERSAMPLE;
  43. phase = eucmod(phase, 1.0f);
  44. }
  45. }
  46. float saw() {
  47. return sawDecimator.process(sawBuffer);
  48. }
  49. };
  50. struct SuHa : Module {
  51. enum ParamIds
  52. {
  53. SUM_VOL_PARAM,
  54. VCO_PARAM,
  55. SUB1_PARAM = VCO_PARAM + 2,
  56. SUB2_PARAM = SUB1_PARAM + 2,
  57. VCO_VOL_PARAM = SUB2_PARAM + 2,
  58. SUB1_VOL_PARAM = VCO_VOL_PARAM + 2,
  59. SUB2_VOL_PARAM = SUB1_VOL_PARAM + 2,
  60. NUM_PARAMS = SUB2_VOL_PARAM + 2
  61. };
  62. enum InputIds
  63. {
  64. VCO_INPUT,
  65. SUB1_INPUT = VCO_INPUT + 2,
  66. SUB2_INPUT = SUB1_INPUT + 2,
  67. NUM_INPUTS = SUB2_INPUT + 2
  68. };
  69. enum OutputIds
  70. {
  71. SUM_OUTPUT,
  72. VCO_OUTPUT,
  73. SUB1_OUTPUT = VCO_OUTPUT + 2,
  74. SUB2_OUTPUT = SUB1_OUTPUT + 2,
  75. NUM_OUTPUTS = SUB2_OUTPUT + 2
  76. };
  77. enum LightIds {
  78. NUM_LIGHTS
  79. };
  80. subBank <16,16> VCO[2]={};
  81. subBank <16,16> SUB1[2]={};
  82. subBank <16,16> SUB2[2]={};
  83. SuHa() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {}
  84. void step() override;
  85. };
  86. void SuHa::step() {
  87. int s1[2]={};
  88. int s2[2] = {};
  89. float sum=0.0f;
  90. for (int i=0;i<2;i++)
  91. {
  92. s1[i] = round(params[SUB1_PARAM+i].value + clamp(inputs[SUB1_INPUT+i].value, -15.0f, 15.0f));
  93. if (s1[i]>15) s1[i]=15;
  94. if (s1[i]<=1) s1[i]=1;
  95. s2[i] = round(params[SUB2_PARAM+i].value + clamp(inputs[SUB2_INPUT+i].value, -15.0f, 15.0f));
  96. if (s2[i]>15) s2[i]=15;
  97. if (s2[i]<=1) s2[i]=1;
  98. VCO[i].setPitch(params[VCO_PARAM+i].value,12*inputs[VCO_INPUT+i].value);
  99. SUB1[i].freq=VCO[i].freq/s1[i];
  100. SUB2[i].freq=VCO[i].freq/s2[i];
  101. VCO[i].process(engineGetSampleTime());
  102. SUB1[i].process(engineGetSampleTime());
  103. SUB2[i].process(engineGetSampleTime());
  104. outputs[VCO_OUTPUT + i].value = 2.0f * VCO[i].saw()*params[VCO_VOL_PARAM+i].value;
  105. outputs[SUB1_OUTPUT + i].value = 2.0f * SUB1[i].saw()*params[SUB1_VOL_PARAM+i].value;
  106. outputs[SUB2_OUTPUT + i].value = 2.0f * SUB2[i].saw()*params[SUB2_VOL_PARAM+i].value;
  107. }
  108. for (int i = 0; i < 2; i++)
  109. {
  110. sum += clamp(outputs[VCO_OUTPUT + i].value + outputs[SUB1_OUTPUT + i].value + outputs[SUB2_OUTPUT + i].value,-5.0f,5.0f);
  111. }
  112. outputs[SUM_OUTPUT].value=sum*params[SUM_VOL_PARAM].value;
  113. }
  114. struct SuHaWidget : ModuleWidget {
  115. SuHaWidget(SuHa *module) : ModuleWidget(module) {
  116. setPanel(SVG::load(assetPlugin(plugin, "res/SuHa.svg")));
  117. int KS=50;
  118. int JS = 37;
  119. float Side=7.5;
  120. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0)));
  121. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0)));
  122. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  123. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  124. ///////////////////////////////////////////////////////////////////////////////////
  125. for (int i = 0; i < 2; i++)
  126. {
  127. addParam(ParamWidget::create<DKnob>(Vec(Side + 6, 87 + i * KS), module, SuHa::VCO_PARAM + i, -54.0, 54.0, 0.0));
  128. addParam(ParamWidget::create<DKnob>(Vec(Side + 6 + KS, 87 +i*KS), module, SuHa::SUB1_PARAM +i, 1.0, 15.0, 1.0));
  129. addParam(ParamWidget::create<DKnob>(Vec(Side + 6 + 2 * KS, 87 +i*KS), module, SuHa::SUB2_PARAM +i, 1.0, 15.0, 1.0));
  130. addParam(ParamWidget::create<Trimpot>(Vec(Side + 15, 25 + i*30), module, SuHa::VCO_VOL_PARAM +i, 0.0, 1.0, 0.0));
  131. addParam(ParamWidget::create<Trimpot>(Vec(Side + 15 + KS, 25 + i*30), module, SuHa::SUB1_VOL_PARAM +i, 0.0, 1.0, 0.0));
  132. addParam(ParamWidget::create<Trimpot>(Vec(Side + 15 + 2 * KS, 25 + i*30), module, SuHa::SUB2_VOL_PARAM +i, 0.0, 1.0, 0.0));
  133. addInput(Port::create<PJ301MVAPort>(Vec(Side + 11, 215+i*JS), Port::INPUT, module, SuHa::VCO_INPUT +i));
  134. addInput(Port::create<PJ301MVAPort>(Vec(Side + 11 + KS, 215+i*JS), Port::INPUT, module, SuHa::SUB1_INPUT +i));
  135. addInput(Port::create<PJ301MVAPort>(Vec(Side + 11 + 2 * KS, 215+i*JS), Port::INPUT, module, SuHa::SUB2_INPUT +i));
  136. addOutput(Port::create<PJ301MVAPort>(Vec(Side + 11, 215 + 2 * JS+i*JS), Port::OUTPUT, module, SuHa::VCO_OUTPUT +i));
  137. addOutput(Port::create<PJ301MVAPort>(Vec(Side + 11 + KS, 215 + 2 * JS+i*JS), Port::OUTPUT, module, SuHa::SUB1_OUTPUT +i));
  138. addOutput(Port::create<PJ301MVAPort>(Vec(Side + 11 + 2 * KS, 215 + 2 * JS+i*JS), Port::OUTPUT, module, SuHa::SUB2_OUTPUT +i));
  139. }
  140. addParam(ParamWidget::create<SDKnob>(Vec(Side + 40, 180), module, SuHa::SUM_VOL_PARAM, 0.0, 1.0, 0.0));
  141. addOutput(Port::create<PJ301MVAPort>(Vec(Side + 80, 185), Port::OUTPUT, module, SuHa::SUM_OUTPUT));
  142. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  143. }
  144. };
  145. } // namespace rack_plugin_dBiz
  146. using namespace rack_plugin_dBiz;
  147. RACK_PLUGIN_MODEL_INIT(dBiz, SuHa) {
  148. // Specify the Module and ModuleWidget subclass, human-readable
  149. // author name for categorization per plugin, module slug (should never
  150. // change), human-readable module name, and any number of tags
  151. // (found in `include/tags.hpp`) separated by commas.
  152. Model *modelSuHa = Model::create<SuHa, SuHaWidget>("dBiz", "SuHa", "SuHa", OSCILLATOR_TAG);
  153. return modelSuHa;
  154. }