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.

216 lines
7.4KB

  1. #include "FrozenWasteland.hpp"
  2. #include "dsp/decimator.hpp"
  3. #include "dsp/digital.hpp"
  4. #include "StateVariableFilter.h"
  5. using namespace std;
  6. #define BANDS 4
  7. #define FREQUENCIES 3
  8. #define numFilters 6
  9. namespace rack_plugin_FrozenWasteland {
  10. struct DamianLillard : Module {
  11. typedef float T;
  12. enum ParamIds {
  13. FREQ_1_CUTOFF_PARAM,
  14. FREQ_2_CUTOFF_PARAM,
  15. FREQ_3_CUTOFF_PARAM,
  16. FREQ_1_CV_ATTENUVERTER_PARAM,
  17. FREQ_2_CV_ATTENUVERTER_PARAM,
  18. FREQ_3_CV_ATTENUVERTER_PARAM,
  19. NUM_PARAMS
  20. };
  21. enum InputIds {
  22. SIGNAL_IN,
  23. FREQ_1_CUTOFF_INPUT,
  24. FREQ_2_CUTOFF_INPUT,
  25. FREQ_3_CUTOFF_INPUT,
  26. BAND_1_RETURN_INPUT,
  27. BAND_2_RETURN_INPUT,
  28. BAND_3_RETURN_INPUT,
  29. BAND_4_RETURN_INPUT,
  30. NUM_INPUTS
  31. };
  32. enum OutputIds {
  33. BAND_1_OUTPUT,
  34. BAND_2_OUTPUT,
  35. BAND_3_OUTPUT,
  36. BAND_4_OUTPUT,
  37. MIX_OUTPUT,
  38. NUM_OUTPUTS
  39. };
  40. enum LightIds {
  41. LEARN_LIGHT,
  42. NUM_LIGHTS
  43. };
  44. float freq[FREQUENCIES] = {0};
  45. float lastFreq[FREQUENCIES] = {0};
  46. float output[BANDS] = {0};
  47. StateVariableFilterState<T> filterStates[numFilters];
  48. StateVariableFilterParams<T> filterParams[numFilters];
  49. int bandOffset = 0;
  50. DamianLillard() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  51. filterParams[0].setMode(StateVariableFilterParams<T>::Mode::LowPass);
  52. filterParams[1].setMode(StateVariableFilterParams<T>::Mode::HiPass);
  53. filterParams[2].setMode(StateVariableFilterParams<T>::Mode::LowPass);
  54. filterParams[3].setMode(StateVariableFilterParams<T>::Mode::HiPass);
  55. filterParams[4].setMode(StateVariableFilterParams<T>::Mode::LowPass);
  56. filterParams[5].setMode(StateVariableFilterParams<T>::Mode::HiPass);
  57. for (int i = 0; i < numFilters; ++i) {
  58. filterParams[i].setQ(5);
  59. filterParams[i].setFreq(T(.1));
  60. }
  61. }
  62. void step() override;
  63. };
  64. void DamianLillard::step() {
  65. float signalIn = inputs[SIGNAL_IN].value/5;
  66. float out = 0.0;
  67. const float minCutoff = 15.0;
  68. const float maxCutoff = 8400.0;
  69. for (int i=0; i<FREQUENCIES;i++) {
  70. float cutoffExp = params[FREQ_1_CUTOFF_PARAM+i].value + inputs[FREQ_1_CUTOFF_INPUT+i].value * params[FREQ_1_CV_ATTENUVERTER_PARAM+i].value / 10.0f; //I'm reducing range of CV to make it more useful
  71. cutoffExp = clamp(cutoffExp, 0.0f, 1.0f);
  72. freq[i] = minCutoff * powf(maxCutoff / minCutoff, cutoffExp);
  73. //Prevent band overlap
  74. if(i>0 && freq[i] < lastFreq[i-1]) {
  75. freq[i] = lastFreq[i-1]+1;
  76. }
  77. if(i<FREQUENCIES-1 && freq[i] > lastFreq[i+1]) {
  78. freq[i] = lastFreq[i+1]-1;
  79. }
  80. if(freq[i] != lastFreq[i]) {
  81. float Fc = freq[i] / engineGetSampleRate();
  82. filterParams[i*2].setFreq(T(Fc));
  83. filterParams[i*2 + 1].setFreq(T(Fc));
  84. lastFreq[i] = freq[i];
  85. }
  86. }
  87. output[0] = StateVariableFilter<T>::run(signalIn, filterStates[0], filterParams[0]) * 5;
  88. output[1] = StateVariableFilter<T>::run(StateVariableFilter<T>::run(signalIn, filterStates[1], filterParams[1]), filterStates[2], filterParams[2]) * 5;
  89. output[2] = StateVariableFilter<T>::run(StateVariableFilter<T>::run(signalIn, filterStates[3], filterParams[3]), filterStates[4], filterParams[4]) * 5;
  90. output[3] = StateVariableFilter<T>::run(signalIn, filterStates[5], filterParams[5]) * 5;
  91. for(int i=0; i<BANDS; i++) {
  92. outputs[BAND_1_OUTPUT+i].value = output[i];
  93. if(inputs[BAND_1_RETURN_INPUT+i].active) {
  94. out += inputs[BAND_1_RETURN_INPUT+i].value;
  95. } else {
  96. out += output[i];
  97. }
  98. }
  99. outputs[MIX_OUTPUT].value = out / 2.0;
  100. }
  101. struct DamianLillardBandDisplay : TransparentWidget {
  102. DamianLillard *module;
  103. int frame = 0;
  104. std::shared_ptr<Font> font;
  105. DamianLillardBandDisplay() {
  106. font = Font::load(assetPlugin(plugin, "res/fonts/01 Digit.ttf"));
  107. }
  108. void drawFrequency(NVGcontext *vg, Vec pos, float cutoffFrequency) {
  109. nvgFontSize(vg, 12);
  110. nvgFontFaceId(vg, font->handle);
  111. nvgTextLetterSpacing(vg, -2);
  112. nvgFillColor(vg, nvgRGBA(0x00, 0xff, 0x00, 0xff));
  113. char text[128];
  114. snprintf(text, sizeof(text), " % 4.0f", cutoffFrequency);
  115. nvgText(vg, pos.x + 8, pos.y, text, NULL);
  116. }
  117. void draw(NVGcontext *vg) override {
  118. for(int i=0;i<FREQUENCIES;i++) {
  119. drawFrequency(vg, Vec(i * 46.0, box.size.y - 75), module->freq[i]);
  120. }
  121. }
  122. };
  123. struct DamianLillardWidget : ModuleWidget {
  124. DamianLillardWidget(DamianLillard *module);
  125. };
  126. DamianLillardWidget::DamianLillardWidget(DamianLillard *module) : ModuleWidget(module) {
  127. box.size = Vec(15*11, 380);
  128. {
  129. SVGPanel *panel = new SVGPanel();
  130. panel->box.size = box.size;
  131. panel->setBackground(SVG::load(assetPlugin(plugin, "res/DamianLillard.svg")));
  132. addChild(panel);
  133. }
  134. {
  135. DamianLillardBandDisplay *offsetDisplay = new DamianLillardBandDisplay();
  136. offsetDisplay->module = module;
  137. offsetDisplay->box.pos = Vec(15, 10);
  138. offsetDisplay->box.size = Vec(box.size.x, 140);
  139. addChild(offsetDisplay);
  140. }
  141. addParam(ParamWidget::create<RoundBlackKnob>(Vec(15, 84), module, DamianLillard::FREQ_1_CUTOFF_PARAM, 0, 1.0, .25));
  142. addParam(ParamWidget::create<RoundBlackKnob>(Vec(66, 84), module, DamianLillard::FREQ_2_CUTOFF_PARAM, 0, 1.0, .5));
  143. addParam(ParamWidget::create<RoundBlackKnob>(Vec(117, 84), module, DamianLillard::FREQ_3_CUTOFF_PARAM, 0, 1.0, .75));
  144. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(19, 146), module, DamianLillard::FREQ_1_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  145. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(70, 146), module, DamianLillard::FREQ_2_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  146. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(121, 146), module, DamianLillard::FREQ_3_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  147. addInput(Port::create<PJ301MPort>(Vec(18, 117), Port::INPUT, module, DamianLillard::FREQ_1_CUTOFF_INPUT));
  148. addInput(Port::create<PJ301MPort>(Vec(69, 117), Port::INPUT, module, DamianLillard::FREQ_2_CUTOFF_INPUT));
  149. addInput(Port::create<PJ301MPort>(Vec(120, 117), Port::INPUT, module, DamianLillard::FREQ_3_CUTOFF_INPUT));
  150. addInput(Port::create<PJ301MPort>(Vec(10, 317), Port::INPUT, module, DamianLillard::SIGNAL_IN));
  151. addInput(Port::create<PJ301MPort>(Vec(10, 255), Port::INPUT, module, DamianLillard::BAND_1_RETURN_INPUT));
  152. addInput(Port::create<PJ301MPort>(Vec(50, 255), Port::INPUT, module, DamianLillard::BAND_2_RETURN_INPUT));
  153. addInput(Port::create<PJ301MPort>(Vec(90, 255), Port::INPUT, module, DamianLillard::BAND_3_RETURN_INPUT));
  154. addInput(Port::create<PJ301MPort>(Vec(130, 255), Port::INPUT, module, DamianLillard::BAND_4_RETURN_INPUT));
  155. addOutput(Port::create<PJ301MPort>(Vec(10, 215), Port::OUTPUT, module, DamianLillard::BAND_1_OUTPUT));
  156. addOutput(Port::create<PJ301MPort>(Vec(50, 215), Port::OUTPUT, module, DamianLillard::BAND_2_OUTPUT));
  157. addOutput(Port::create<PJ301MPort>(Vec(90, 215), Port::OUTPUT, module, DamianLillard::BAND_3_OUTPUT));
  158. addOutput(Port::create<PJ301MPort>(Vec(130, 215), Port::OUTPUT, module, DamianLillard::BAND_4_OUTPUT));
  159. addOutput(Port::create<PJ301MPort>(Vec(90, 317), Port::OUTPUT, module, DamianLillard::MIX_OUTPUT));
  160. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH-12, 0)));
  161. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0)));
  162. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH-12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  163. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  164. }
  165. } // namespace rack_plugin_FrozenWasteland
  166. using namespace rack_plugin_FrozenWasteland;
  167. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, DamianLillard) {
  168. Model *modelDamianLillard = Model::create<DamianLillard, DamianLillardWidget>("Frozen Wasteland", "DamianLillard", "Damian Lillard", FILTER_TAG);
  169. return modelDamianLillard;
  170. }