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
6.2KB

  1. #include "FrozenWasteland.hpp"
  2. #include "dsp/digital.hpp"
  3. #include "dsp-noise/noise.hpp"
  4. #include "filters/biquad.h"
  5. using namespace frozenwasteland::dsp;
  6. namespace rack_plugin_FrozenWasteland {
  7. struct EverlastingGlottalStopper : Module {
  8. enum ParamIds {
  9. FREQUENCY_PARAM,
  10. TIME_OPEN_PARAM,
  11. TIME_CLOSED_PARAM,
  12. BREATHINESS_PARAM,
  13. FM_CV_ATTENUVERTER_PARAM,
  14. TIME_OPEN_CV_ATTENUVERTER_PARAM,
  15. TIME_CLOSED_CV_ATTENUVERTER_PARAM,
  16. BREATHINESS_CV_ATTENUVERTER_PARAM,
  17. DEEMPHASIS_FILTER_PARAM,
  18. NUM_PARAMS
  19. };
  20. enum InputIds {
  21. PITCH_INPUT,
  22. FM_INPUT,
  23. TIME_OPEN_INPUT,
  24. TIME_CLOSED_INPUT,
  25. BREATHINESS_INPUT,
  26. NUM_INPUTS
  27. };
  28. enum OutputIds {
  29. VOICE_OUTPUT,
  30. NUM_OUTPUTS
  31. };
  32. enum LightIds {
  33. LEARN_LIGHT,
  34. NUM_LIGHTS
  35. };
  36. Biquad* deemphasisFilter;
  37. GaussianNoiseGenerator _gauss;
  38. float phase = 0.0;
  39. EverlastingGlottalStopper() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) {
  40. deemphasisFilter = new Biquad(bq_type_lowpass, 2000 / engineGetSampleRate(), 1, 0);
  41. }
  42. void step() override;
  43. };
  44. float Rosenburg(float timeOpening, float timeOpen, float phase) {
  45. float out;
  46. if(phase < timeOpening) {
  47. out = 0.5f * (1.0f - cosf(M_PI * phase / timeOpening));
  48. } else if (phase < timeOpen) {
  49. out = cosf(M_PI * (phase - timeOpening) / (timeOpen - timeOpening) / 2);
  50. } else {
  51. out = 0.0f;
  52. }
  53. return out;
  54. }
  55. float HanningWindow(float phase) {
  56. return 0.5f * (1 - cosf(2 * M_PI * phase));
  57. }
  58. inline float quadraticBipolar(float x) {
  59. float x2 = x*x;
  60. return (x >= 0.f) ? x2 : -x2;
  61. }
  62. void EverlastingGlottalStopper::step() {
  63. float pitch = params[FREQUENCY_PARAM].value;
  64. float pitchCv = 12.0f * inputs[PITCH_INPUT].value;
  65. if (inputs[FM_INPUT].active) {
  66. pitchCv += quadraticBipolar(params[FM_CV_ATTENUVERTER_PARAM].value) * 12.0f * inputs[FM_INPUT].value;
  67. }
  68. pitch += pitchCv;
  69. // Note C4
  70. float freq = 261.626f * powf(2.0f, pitch / 12.0f);
  71. //float pitch = params[FREQUENCY_PARAM].value + inputs[FM_INPUT].value * params[FM_CV_ATTENUVERTER_PARAM].value;
  72. //float freq = powf(2.0, pitch);
  73. float timeOpening = clamp(params[TIME_OPEN_PARAM].value + inputs[TIME_OPEN_INPUT].value * params[TIME_OPEN_CV_ATTENUVERTER_PARAM].value,0.01f,1.0f);
  74. float timeClosed = clamp(params[TIME_CLOSED_PARAM].value + inputs[TIME_CLOSED_INPUT].value * params[TIME_CLOSED_CV_ATTENUVERTER_PARAM].value,0.0f,1.0f);
  75. float timeOpen = clamp(1.0-timeClosed,timeOpening,1.0);
  76. float dt = 1.0 / engineGetSampleRate();
  77. float deltaPhase = fminf(freq * dt, 0.5);
  78. phase += deltaPhase;
  79. if (phase >= 1.0) {
  80. phase -= 1.0;
  81. }
  82. float out = Rosenburg(timeOpening,timeOpen,phase);
  83. float noiseLevel = clamp(params[BREATHINESS_PARAM].value + inputs[BREATHINESS_INPUT].value * params[BREATHINESS_CV_ATTENUVERTER_PARAM].value,0.0f,1.0f);
  84. //Noise level follows glottal wave
  85. // noise = _gauss.next() * out * noiseLevel;
  86. float noise = _gauss.next() / 5.0 * noiseLevel * HanningWindow(phase);
  87. out = out + noise;
  88. if(params[DEEMPHASIS_FILTER_PARAM].value) {
  89. out = deemphasisFilter->process(out);
  90. }
  91. outputs[VOICE_OUTPUT].value = out * 10.0f - 5.0f;
  92. }
  93. struct EverlastingGlottalStopperWidget : ModuleWidget {
  94. EverlastingGlottalStopperWidget(EverlastingGlottalStopper *module);
  95. };
  96. EverlastingGlottalStopperWidget::EverlastingGlottalStopperWidget(EverlastingGlottalStopper *module) : ModuleWidget(module) {
  97. box.size = Vec(15*11, 380);
  98. {
  99. SVGPanel *panel = new SVGPanel();
  100. panel->box.size = box.size;
  101. panel->setBackground(SVG::load(assetPlugin(plugin, "res/EverlastingGlottalStopper.svg")));
  102. addChild(panel);
  103. }
  104. addParam(ParamWidget::create<RoundHugeBlackKnob>(Vec(54, 60), module, EverlastingGlottalStopper::FREQUENCY_PARAM, -54.0f, 54.0f, 0.0f));
  105. addParam(ParamWidget::create<RoundBlackKnob>(Vec(15, 215), module, EverlastingGlottalStopper::TIME_OPEN_PARAM, 0.01f, 1.0f, 0.5f));
  106. addParam(ParamWidget::create<RoundBlackKnob>(Vec(68, 215), module, EverlastingGlottalStopper::TIME_CLOSED_PARAM, 0.0f, 0.9f, 0.0f));
  107. addParam(ParamWidget::create<RoundBlackKnob>(Vec(120, 215), module, EverlastingGlottalStopper::BREATHINESS_PARAM, 0.0f, 0.9f, 0.0f));
  108. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(108, 162), module, EverlastingGlottalStopper::FM_CV_ATTENUVERTER_PARAM, 0.0, 1.0, 0));
  109. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(17, 275), module, EverlastingGlottalStopper::TIME_OPEN_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  110. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(70, 275), module, EverlastingGlottalStopper::TIME_CLOSED_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  111. addParam(ParamWidget::create<RoundSmallBlackKnob>(Vec(123, 275), module, EverlastingGlottalStopper::BREATHINESS_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0));
  112. //addParam(ParamWidget::create<CKSS>(Vec(123, 300), module, EverlastingGlottalStopper::DEEMPHASIS_FILTER_PARAM, 0.0, 1.0, 0));
  113. addInput(Port::create<PJ301MPort>(Vec(30, 134), Port::INPUT, module, EverlastingGlottalStopper::PITCH_INPUT));
  114. addInput(Port::create<PJ301MPort>(Vec(108, 134), Port::INPUT, module, EverlastingGlottalStopper::FM_INPUT));
  115. addInput(Port::create<PJ301MPort>(Vec(17, 247), Port::INPUT, module, EverlastingGlottalStopper::TIME_OPEN_INPUT));
  116. addInput(Port::create<PJ301MPort>(Vec(70, 247), Port::INPUT, module, EverlastingGlottalStopper::TIME_CLOSED_INPUT));
  117. addInput(Port::create<PJ301MPort>(Vec(123, 247), Port::INPUT, module, EverlastingGlottalStopper::BREATHINESS_INPUT));
  118. addOutput(Port::create<PJ301MPort>(Vec(71, 325), Port::OUTPUT, module, EverlastingGlottalStopper::VOICE_OUTPUT));
  119. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH-12, 0)));
  120. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0)));
  121. addChild(Widget::create<ScrewSilver>(Vec(RACK_GRID_WIDTH-12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  122. addChild(Widget::create<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH)));
  123. }
  124. } // namespace rack_plugin_FrozenWasteland
  125. using namespace rack_plugin_FrozenWasteland;
  126. RACK_PLUGIN_MODEL_INIT(FrozenWasteland, EverlastingGlottalStopper) {
  127. Model *modelEverlastingGlottalStopper = Model::create<EverlastingGlottalStopper, EverlastingGlottalStopperWidget>("Frozen Wasteland", "EverlastingGlottalStopper", "Everlasting Glottal Stopper", FILTER_TAG);
  128. return modelEverlastingGlottalStopper;
  129. }