#include "FrozenWasteland.hpp" #include "dsp/decimator.hpp" #include "dsp/digital.hpp" #include "StateVariableFilter.h" using namespace std; #define BANDS 5 namespace rack_plugin_FrozenWasteland { struct VoxInhumana : Module { typedef float T; enum ParamIds { VOWEL_1_PARAM, VOWEL_2_PARAM, VOWEL_BALANCE_PARAM, VOICE_TYPE_PARAM, FC_MAIN_CUTOFF_PARAM, FREQ_1_CUTOFF_PARAM, FREQ_2_CUTOFF_PARAM, FREQ_3_CUTOFF_PARAM, FREQ_4_CUTOFF_PARAM, FREQ_5_CUTOFF_PARAM, AMP_1_PARAM, AMP_2_PARAM, AMP_3_PARAM, AMP_4_PARAM, AMP_5_PARAM, VOWEL_1_ATTENUVERTER_PARAM, VOWEL_2_ATTENUVERTER_PARAM, VOWEL_BALANCE_ATTENUVERTER_PARAM, VOICE_TYPE_ATTENUVERTER_PARAM, FC_MAIN_ATTENUVERTER_PARAM, FREQ_1_CV_ATTENUVERTER_PARAM, FREQ_2_CV_ATTENUVERTER_PARAM, FREQ_3_CV_ATTENUVERTER_PARAM, FREQ_4_CV_ATTENUVERTER_PARAM, FREQ_5_CV_ATTENUVERTER_PARAM, AMP_1_CV_ATTENUVERTER_PARAM, AMP_2_CV_ATTENUVERTER_PARAM, AMP_3_CV_ATTENUVERTER_PARAM, AMP_4_CV_ATTENUVERTER_PARAM, AMP_5_CV_ATTENUVERTER_PARAM, NUM_PARAMS }; enum InputIds { SIGNAL_IN, VOWEL_1_CV_IN, VOWEL_2_CV_IN, VOWEL_BALANCE_CV_IN, VOICE_TYPE_CV_IN, FC_MAIN_CV_IN, FREQ_1_CUTOFF_INPUT, FREQ_2_CUTOFF_INPUT, FREQ_3_CUTOFF_INPUT, FREQ_4_CUTOFF_INPUT, FREQ_5_CUTOFF_INPUT, AMP_1_INPUT, AMP_2_INPUT, AMP_3_INPUT, AMP_4_INPUT, AMP_5_INPUT, NUM_INPUTS }; enum OutputIds { VOX_OUTPUT, NUM_OUTPUTS }; enum LightIds { VOWEL_1_LIGHT, VOWEL_2_LIGHT, NUM_LIGHTS }; StateVariableFilterState filterStates[BANDS]; StateVariableFilterParams filterParams[BANDS]; float freq[BANDS] = {0}; float lastFreq[BANDS] = {0}; float Q[BANDS] = {0}; float lastQ[BANDS] = {0}; float peak[BANDS] = {0}; int vowel1 = 0; int vowel2 = 0; float vowelBalance = 0; int voiceType = 0; float fcShift = 0; float lerp(float v0, float v1, float t) { return (1 - t) * v0 + t * v1; } // First Index is Voice Type (soprano,alto,counter-tenor,tenor,bass) // Second Index is Vowel (a,e,i,o,u) // should find more // Third if filter/formant index // Fourth: Cutoff,Q,Peak db) const float formantParameters[5][5][BANDS][3] = { //Bass { {{600,10,0},{1040,15,-7},{2250,21,-9},{2450,20,-9},{2750,21,-20}}, //a {{400,10,0},{1620,20,-12},{2400,24,-9},{2800,23,-12},{3100,26,-18}}, //e {{250,4,0},{1750,19,-30},{2600,26,-16},{3050,25,-22},{3340,28,-28}}, //i {{400,10,0},{750,9,-11},{2400,24,-21},{2600,22,-20},{2900,24,-40}}, //o {{350,9,0},{600,8,-20},{2400,24,-32},{2675,22,-28},{2950,25,-36}}, //u }, //Tenor - in progress { {{650,8,0},{1080,12,-6},{2650,22,-7},{2900,22,-8},{3250,23,-22}}, //a {{400,6,0},{1700,21,-14},{2600,26,-12},{3200,27,-14},{3580,30,-20}}, //e {{290,7,0},{1870,21,-15},{2800,28,-18},{3250,27,-20},{3540,30,-30}}, //i {{400,10,0},{800,10,-10},{2600,26,-12},{2800,23,-12},{3000,25,-26}}, //o {{350,9,0},{600,10,-20},{2700,27,-17},{2900,24,-14},{3300,28,-26}}, //u }, //Counter-Tenor - in progress { {{660,8,0},{1120,12,-6},{2750,23,-23},{3000,23,-24},{3350,24,-38}}, //a {{440,6,0},{1800,22,-14},{2700,27,-18},{3000,25,-20},{3300,28,-20}}, //e {{270,7,0},{1850,21,-24},{2900,29,-24},{3350,28,-36},{3590,30,-36}}, //i {{430,11,0},{820,10,-10},{2700,27,-26},{3000,25,-22},{3300,28,-34}}, //o {{370,9,0},{630,11,-20},{2750,28,-23},{3000,25,-30},{3400,28,-34}}, //u }, //Alto { {{800,10,0},{1150,13,-4},{2800,27,-20},{3500,27,-36},{4950,35,-60}}, //a {{400,7,0},{1600,20,-24},{2700,23,-30},{3300,22,-35},{4950,25,-60}}, //e {{350,7,0},{1700,17,-20},{2700,23,-30},{3700,25,-36},{4950,25,-60}}, //i {{450,6,0},{800,10,-9},{2830,28,-16},{3500,27,-28},{4950,37,-55}}, //o {{325,7,0},{700,12,-12},{2530,15,-30},{3500,19,-40},{4950,25,-64}}, //u }, //Soprano { {{800,10,0},{1150,12,-6},{2900,24,-32},{3900,30,-20},{4950,35,-50}}, //a {{350,6,0},{2000,20,-20},{2800,23,-15},{3600,24,-40},{4950,25,-56}}, //e {{270,5,0},{2140,24,-12},{2950,30,-26},{3900,33,-26},{4950,41,-44}}, //i {{450,6,0},{800,10,-11},{2830,28,-22},{3800,29,-22},{4950,37,-50}}, //o {{325,7,0},{700,12,-16},{2700,16,-35},{3800,21,-40},{4950,25,-60}}, //u } }; VoxInhumana() : Module(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS) { for (int i = 0; i < BANDS; ++i) { filterParams[i].setMode(StateVariableFilterParams::Mode::BandPass); filterParams[i].setQ(5); filterParams[i].setFreq(T(.1)); } } void reset() override; void step() override; }; void VoxInhumana::reset() { params[FC_MAIN_CUTOFF_PARAM].value = 1.0f; params[VOWEL_BALANCE_PARAM].value = 0.0f; for(int i = 0;i::run(signalIn, filterStates[i], filterParams[i]);; float attenuation = powf(10,peak[i] / 20.0f); float manualAttenuation = params[AMP_1_PARAM+i].value + inputs[AMP_1_INPUT+i].value * params[AMP_1_CV_ATTENUVERTER_PARAM+i].value; attenuation = clamp(attenuation * manualAttenuation, 0.0f, 1.0f); out += filterOut * attenuation * 5.0f; } outputs[VOX_OUTPUT].value = out / 5.0f; } struct VoxInhumanaBandDisplay : TransparentWidget { VoxInhumana *module; int frame = 0; std::shared_ptr font; VoxInhumanaBandDisplay() { font = Font::load(assetPlugin(plugin, "res/fonts/DejaVuSansMono.ttf")); } void drawVowel(NVGcontext *vg, Vec pos, int vowel) { nvgFontSize(vg, 22); nvgFontFaceId(vg, font->handle); nvgTextLetterSpacing(vg, -2); const char* vowelNames[5] = {"A","E","I","O","U"}; nvgFillColor(vg, nvgRGBA(0x00, 0xaf, 0x00, 0xff)); char text[128]; snprintf(text, sizeof(text), "%s", vowelNames[vowel]); nvgText(vg, pos.x + 8, pos.y, text, NULL); } void drawVoiceType(NVGcontext *vg, Vec pos, int voiceType) { nvgFontSize(vg, 14); nvgFontFaceId(vg, font->handle); nvgTextLetterSpacing(vg, -2); const char* voiceNames[5] = {"Bass","Tenor","Counter-Tenor","Alto","Soprano"}; nvgFillColor(vg, nvgRGBA(0x00, 0x8f, 0xff, 0xff)); char text[128]; snprintf(text, sizeof(text), "%s", voiceNames[voiceType]); nvgText(vg, pos.x + 8, pos.y, text, NULL); } void draw(NVGcontext *vg) override { drawVowel(vg, Vec(24.0, box.size.y - 99),module->vowel1); drawVowel(vg, Vec(156.0, box.size.y - 99),module->vowel2); drawVoiceType(vg, Vec(20.0, box.size.y - 27),module->voiceType); } }; struct VoxInhumanaWidget : ModuleWidget { VoxInhumanaWidget(VoxInhumana *module); }; VoxInhumanaWidget::VoxInhumanaWidget(VoxInhumana *module) : ModuleWidget(module) { box.size = Vec(15*14, 380); { SVGPanel *panel = new SVGPanel(); panel->box.size = box.size; panel->setBackground(SVG::load(assetPlugin(plugin, "res/VoxInhumana.svg"))); addChild(panel); } { VoxInhumanaBandDisplay *offsetDisplay = new VoxInhumanaBandDisplay(); offsetDisplay->module = module; offsetDisplay->box.pos = Vec(15, 10); offsetDisplay->box.size = Vec(box.size.x, 140); addChild(offsetDisplay); } addParam(ParamWidget::create(Vec(10, 30), module, VoxInhumana::VOWEL_1_PARAM, 0, 4.6, 0)); addParam(ParamWidget::create(Vec(140, 30), module, VoxInhumana::VOWEL_2_PARAM, 0, 4.6, 0)); addParam(ParamWidget::create(Vec(89, 30), module, VoxInhumana::VOWEL_BALANCE_PARAM, 0.0, 1.0, 0)); addParam(ParamWidget::create(Vec(7, 103), module, VoxInhumana::VOICE_TYPE_PARAM, 0, 4.6, 0)); addParam(ParamWidget::create(Vec(140, 103), module, VoxInhumana::FC_MAIN_CUTOFF_PARAM, 0.0, 2.0, 1)); addParam(ParamWidget::create(Vec(40, 62), module, VoxInhumana::VOWEL_1_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(170, 62), module, VoxInhumana::VOWEL_2_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(107, 62), module, VoxInhumana::VOWEL_BALANCE_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(31, 134), module, VoxInhumana::VOICE_TYPE_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(169, 125), module, VoxInhumana::FC_MAIN_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(15, 160), module, VoxInhumana::FREQ_1_CUTOFF_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(15, 195), module, VoxInhumana::FREQ_2_CUTOFF_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(15, 230), module, VoxInhumana::FREQ_3_CUTOFF_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(15, 265), module, VoxInhumana::FREQ_4_CUTOFF_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(15, 300), module, VoxInhumana::FREQ_5_CUTOFF_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(80, 162), module, VoxInhumana::FREQ_1_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(80, 197), module, VoxInhumana::FREQ_2_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(80, 232), module, VoxInhumana::FREQ_3_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(80, 267), module, VoxInhumana::FREQ_4_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(80, 302), module, VoxInhumana::FREQ_5_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(110, 160), module, VoxInhumana::AMP_1_PARAM, 0.0, 2.0, 1)); addParam(ParamWidget::create(Vec(110, 195), module, VoxInhumana::AMP_2_PARAM, 0.0, 2.0, 1)); addParam(ParamWidget::create(Vec(110, 230), module, VoxInhumana::AMP_3_PARAM, 0.0, 2.0, 1)); addParam(ParamWidget::create(Vec(110, 265), module, VoxInhumana::AMP_4_PARAM, 0.0, 2.0, 1)); addParam(ParamWidget::create(Vec(110, 300), module, VoxInhumana::AMP_5_PARAM, 0.0, 2.0, 1)); addParam(ParamWidget::create(Vec(175, 162), module, VoxInhumana::AMP_1_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(175, 197), module, VoxInhumana::AMP_2_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(175, 232), module, VoxInhumana::AMP_3_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(175, 267), module, VoxInhumana::AMP_4_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addParam(ParamWidget::create(Vec(175, 302), module, VoxInhumana::AMP_5_CV_ATTENUVERTER_PARAM, -1.0, 1.0, 0)); addInput(Port::create(Vec(13, 62), Port::INPUT, module, VoxInhumana::VOWEL_1_CV_IN)); addInput(Port::create(Vec(143, 62), Port::INPUT, module, VoxInhumana::VOWEL_2_CV_IN)); addInput(Port::create(Vec(80, 62), Port::INPUT, module, VoxInhumana::VOWEL_BALANCE_CV_IN)); addInput(Port::create(Vec(5, 134), Port::INPUT, module, VoxInhumana::VOICE_TYPE_CV_IN)); addInput(Port::create(Vec(170, 97), Port::INPUT, module, VoxInhumana::FC_MAIN_CV_IN)); addInput(Port::create(Vec(50, 162), Port::INPUT, module, VoxInhumana::FREQ_1_CUTOFF_INPUT)); addInput(Port::create(Vec(50, 197), Port::INPUT, module, VoxInhumana::FREQ_2_CUTOFF_INPUT)); addInput(Port::create(Vec(50, 232), Port::INPUT, module, VoxInhumana::FREQ_3_CUTOFF_INPUT)); addInput(Port::create(Vec(50, 267), Port::INPUT, module, VoxInhumana::FREQ_4_CUTOFF_INPUT)); addInput(Port::create(Vec(50, 302), Port::INPUT, module, VoxInhumana::FREQ_5_CUTOFF_INPUT)); addInput(Port::create(Vec(145, 162), Port::INPUT, module, VoxInhumana::AMP_1_INPUT) ); addInput(Port::create(Vec(145, 197), Port::INPUT, module, VoxInhumana::AMP_2_INPUT)); addInput(Port::create(Vec(145, 232), Port::INPUT, module, VoxInhumana::AMP_3_INPUT)); addInput(Port::create(Vec(145, 267), Port::INPUT, module, VoxInhumana::AMP_4_INPUT)); addInput(Port::create(Vec(145, 302), Port::INPUT, module, VoxInhumana::AMP_5_INPUT)); addInput(Port::create(Vec(56, 338), Port::INPUT, module, VoxInhumana::SIGNAL_IN)); addOutput(Port::create(Vec(130, 338), Port::OUTPUT, module, VoxInhumana::VOX_OUTPUT)); addChild(ModuleLightWidget::create>(Vec(72, 37), module, VoxInhumana::VOWEL_1_LIGHT)); addChild(ModuleLightWidget::create>(Vec(120, 37), module, VoxInhumana::VOWEL_2_LIGHT)); addChild(Widget::create(Vec(RACK_GRID_WIDTH-12, 0))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, 0))); addChild(Widget::create(Vec(RACK_GRID_WIDTH-12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); addChild(Widget::create(Vec(box.size.x - 2 * RACK_GRID_WIDTH + 12, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); } } // namespace rack_plugin_FrozenWasteland using namespace rack_plugin_FrozenWasteland; RACK_PLUGIN_MODEL_INIT(FrozenWasteland, VoxInhumana) { Model *modelVoxInhumana = Model::create("Frozen Wasteland", "VoxInhumana", "Vox Inhumana", FILTER_TAG); return modelVoxInhumana; }