|
| 1 | +#include "plugin.hpp" |
| 2 | +#include "musiclib.hpp" |
| 3 | + |
| 4 | +struct ScaleCV : Module { |
| 5 | + enum ParamIds { |
| 6 | + ROOT_PARAM, |
| 7 | + MODE_PARAM, |
| 8 | + NUM_PARAMS |
| 9 | + }; |
| 10 | + enum InputIds { |
| 11 | + ROOT_INPUT, |
| 12 | + MODE_INPUT, |
| 13 | + NUM_INPUTS |
| 14 | + }; |
| 15 | + enum OutputIds { |
| 16 | + POLY_OUTPUT, |
| 17 | + NUM_OUTPUTS |
| 18 | + }; |
| 19 | + enum LightIds { |
| 20 | + NUM_LIGHTS |
| 21 | + }; |
| 22 | + |
| 23 | + int root_semi = 0; |
| 24 | + int mode = 0; |
| 25 | + |
| 26 | + ScaleCV() { |
| 27 | + config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS); |
| 28 | + configParam(ROOT_PARAM, -4.0, 4.0, 0.0, "Root Note"); |
| 29 | + configParam(MODE_PARAM, -4.0, 4.0, -4.0, "Mode"); |
| 30 | + |
| 31 | + configInput(ROOT_INPUT, "1V/oct pitch"); |
| 32 | + configInput(MODE_INPUT, "Mode"); |
| 33 | + |
| 34 | + configOutput(POLY_OUTPUT, "Polyphonic"); |
| 35 | + } |
| 36 | + |
| 37 | + void process(const ProcessArgs& args) override; |
| 38 | +}; |
| 39 | + |
| 40 | +void ScaleCV::process(const ProcessArgs &args){ |
| 41 | + float value = params[ROOT_PARAM].getValue(); |
| 42 | + if(inputs[ROOT_INPUT].isConnected()){ |
| 43 | + value = inputs[ROOT_INPUT].getVoltage(); |
| 44 | + } |
| 45 | + float mode_val = params[MODE_PARAM].getValue(); |
| 46 | + if(inputs[MODE_INPUT].isConnected()){ |
| 47 | + mode_val = inputs[MODE_INPUT].getVoltage(); |
| 48 | + } |
| 49 | + mode_val = clamp(mode_val,-4.0f, 2.0f); |
| 50 | + mode = (int)floor(mode_val + 4.0f); |
| 51 | + |
| 52 | + //quantize root note |
| 53 | + float octave = round(value); |
| 54 | + float semi = voltage_to_note(value); |
| 55 | + root_semi = voltage_to_note_int(value); |
| 56 | + int root_note = (octave + 4) * 12 + (int)semi; |
| 57 | + |
| 58 | + //Make the scale |
| 59 | + struct scale s = get_scale(root_note, mode); |
| 60 | + |
| 61 | + outputs[POLY_OUTPUT].setChannels(8); |
| 62 | + for(int t=0; t<8; t++){ |
| 63 | + outputs[POLY_OUTPUT].setVoltage(note_to_voltage(s.notes[t]),t); |
| 64 | + } |
| 65 | +} |
| 66 | + |
| 67 | + |
| 68 | +struct ScaleCVWidget : ModuleWidget { |
| 69 | + struct ChordDisplayWidget : TransparentWidget { |
| 70 | + ScaleCV* module; |
| 71 | + std::shared_ptr<Font> font; |
| 72 | + char text[13]; |
| 73 | + |
| 74 | + ChordDisplayWidget(Vec _pos, Vec _size, ScaleCV* _module) { |
| 75 | + box.size = _size; |
| 76 | + box.pos = _pos.minus(_size.div(2)); |
| 77 | + module = _module; |
| 78 | + font = APP->window->loadFont(asset::plugin(pluginInstance, "res/fonts/PixelOperator.ttf")); |
| 79 | + } |
| 80 | + |
| 81 | + void draw(const DrawArgs &args) override { |
| 82 | + NVGcolor textColor = prepareDisplay(args.vg, &box, 22); |
| 83 | + nvgFontFaceId(args.vg, font->handle); |
| 84 | + nvgTextLetterSpacing(args.vg, -1.5); |
| 85 | + nvgTextAlign(args.vg, NVG_ALIGN_CENTER); |
| 86 | + |
| 87 | + Vec textPos = Vec(box.size.x/2, 21.0f); |
| 88 | + nvgFillColor(args.vg, textColor); |
| 89 | + |
| 90 | + if (module != NULL){ |
| 91 | + get_scale_name(module->root_semi,module->mode,text); |
| 92 | + }else{ |
| 93 | + snprintf(text, 13, " "); |
| 94 | + } |
| 95 | + |
| 96 | + nvgText(args.vg, textPos.x, textPos.y, text, NULL); |
| 97 | + } |
| 98 | + |
| 99 | + }; |
| 100 | + |
| 101 | + ScaleCVWidget(ScaleCV* module) { |
| 102 | + setModule(module); |
| 103 | + setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ScaleCV.svg"))); |
| 104 | + |
| 105 | + addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, 0))); |
| 106 | + addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, 0))); |
| 107 | + addChild(createWidget<ScrewSilver>(Vec(RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); |
| 108 | + addChild(createWidget<ScrewSilver>(Vec(box.size.x - 2 * RACK_GRID_WIDTH, RACK_GRID_HEIGHT - RACK_GRID_WIDTH))); |
| 109 | + |
| 110 | + const int centerX = box.size.x / 2; |
| 111 | + |
| 112 | + ChordDisplayWidget* display = new ChordDisplayWidget(Vec(centerX, 55), Vec(box.size.x - 5, 29), module); |
| 113 | + addChild(display); |
| 114 | + |
| 115 | + const int offsetXL = 40; |
| 116 | + |
| 117 | + |
| 118 | + addParam(createParamCentered<Rogan2PWhite>(Vec(centerX,95), module, ScaleCV::ROOT_PARAM)); |
| 119 | + addInput(createInputCentered<PJ301MPort>(Vec(centerX - offsetXL, 95), module, ScaleCV::ROOT_INPUT)); |
| 120 | + |
| 121 | + addParam(createParamCentered<Rogan2PWhite>(Vec(centerX,140), module, ScaleCV::MODE_PARAM)); |
| 122 | + addInput(createInputCentered<PJ301MPort>(Vec(centerX - offsetXL, 140), module, ScaleCV::MODE_INPUT)); |
| 123 | + |
| 124 | + addOutput(createOutputCentered<PJ301MPort>(Vec(centerX, 330), module, ScaleCV::POLY_OUTPUT)); |
| 125 | + } |
| 126 | +}; |
| 127 | + |
| 128 | + |
| 129 | +Model* modelScaleCV = createModel<ScaleCV, ScaleCVWidget>("ScaleCV"); |
0 commit comments