computerscare-vcv-modules

ComputerScare modules for VCV Rack
Log | Files | Refs

ComputerscareOhPeas.cpp (14764B)


      1 #include "Computerscare.hpp"
      2 
      3 #include "dtpulse.hpp"
      4 
      5 
      6 #include <string>
      7 #include <sstream>
      8 #include <iomanip>
      9 
     10 struct ComputerscareOhPeas;
     11 
     12 const int numChannels = 4;
     13 
     14 struct ComputerscareOhPeas;
     15 
     16 struct ComputerscareOhPeas : Module
     17 {
     18     enum ParamIds
     19     {
     20         GLOBAL_TRANSPOSE,
     21         NUM_DIVISIONS,
     22         SCALE_TRIM,
     23         SCALE_VAL = SCALE_TRIM + numChannels,
     24         OFFSET_TRIM = SCALE_VAL + numChannels,
     25         OFFSET_VAL = OFFSET_TRIM + numChannels,
     26         NUM_PARAMS = OFFSET_VAL + numChannels
     27 
     28     };
     29     enum InputIds
     30     {
     31         CHANNEL_INPUT,
     32         SCALE_CV = CHANNEL_INPUT + numChannels,
     33         OFFSET_CV = SCALE_CV + numChannels,
     34         NUM_INPUTS = OFFSET_CV + numChannels
     35     };
     36     enum OutputIds
     37     {
     38         SCALED_OUTPUT,
     39         QUANTIZED_OUTPUT = SCALED_OUTPUT + numChannels,
     40         NUM_OUTPUTS = QUANTIZED_OUTPUT + numChannels
     41     };
     42     enum LightIds
     43     {
     44         BLINK_LIGHT,
     45         NUM_LIGHTS
     46     };
     47 
     48 
     49 
     50     int numDivisions = 12;
     51     int globalTranspose = 0;
     52     bool evenQuantizeMode = true;
     53     bool manualSet = true;
     54 
     55     int checkCounter = 9999;
     56     int checkPeriod = 1000;
     57     std::string currentFormula = "221222";
     58     std::string lastFormula = "52";
     59 
     60 
     61     std::string numDivisionsString = "";
     62     SmallLetterDisplay *numDivisionsDisplay;
     63     SmallLetterDisplay *globalTransposeDisplay;
     64 
     65     Quantizer quant;
     66 
     67     ComputerscareOhPeas()
     68     {
     69 
     70         enum InputIds
     71         {
     72             CHANNEL_INPUT,
     73             SCALE_CV = CHANNEL_INPUT + numChannels,
     74             OFFSET_CV = SCALE_CV + numChannels,
     75             NUM_INPUTS = OFFSET_CV + numChannels
     76         };
     77         enum OutputIds
     78         {
     79             SCALED_OUTPUT,
     80             QUANTIZED_OUTPUT = SCALED_OUTPUT + numChannels,
     81             NUM_OUTPUTS = QUANTIZED_OUTPUT + numChannels
     82         };
     83 
     84 
     85         config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
     86         configParam(GLOBAL_TRANSPOSE, -1.f, 1.f, 0.0f, "Global Transpose");
     87         configParam(NUM_DIVISIONS, 1.f, 24.f, 12.0f, "Number of Divisions");
     88         for (int i = 0; i < numChannels; i++)
     89         {
     90             std::string chi = "Column " + std::to_string(i + 1);
     91             configParam( SCALE_TRIM + i, -1.f, 1.f, 0.0f, chi + " Scale CV Amount");
     92             configParam( SCALE_VAL + i, -2.f, 2.f, 1.0f, chi + " Scale Value");
     93             configParam( OFFSET_TRIM + i, -1.f, 1.f, 0.0f, chi + " Offset CV Amount");
     94             configParam( OFFSET_VAL + i, -10.f, 10.f, 0.0f, chi + " Offset Value");
     95 
     96             configInput(CHANNEL_INPUT + i, chi);
     97             configInput(SCALE_CV + i, chi + " Scale");
     98             configInput(OFFSET_CV + i, chi + " Offset");
     99 
    100             configOutput(SCALED_OUTPUT + i, chi + " Non-Quantized");
    101             configOutput(QUANTIZED_OUTPUT + i, chi + " Quantized");
    102 
    103         }
    104 
    105     }
    106     void process(const ProcessArgs &args) override;
    107 
    108     json_t *dataToJson() override {
    109         json_t *rootJ = json_object();
    110 
    111         json_t *sequenceJ = json_string(currentFormula.c_str());
    112 
    113         json_object_set_new(rootJ, "sequences", sequenceJ);
    114 
    115         return rootJ;
    116     }
    117 
    118     void dataFromJson(json_t *rootJ) override {
    119         std::string val;
    120         json_t *textJ = json_object_get(rootJ, "sequences");
    121         if (textJ) {
    122             currentFormula = json_string_value(textJ);
    123             manualSet = true;
    124         }
    125 
    126     }
    127 
    128     void setQuant()
    129     {
    130         this->quant = Quantizer(this->currentFormula.c_str(), this->numDivisions, this->globalTranspose);
    131     }
    132     void checkForChange() {
    133         if (lastFormula != currentFormula) {
    134             setQuant();
    135         }
    136         lastFormula = currentFormula;
    137     }
    138     // For more advanced Module features, read Rack's engine.hpp header file
    139     // - toJson, fromJson: serialization of internal data
    140     // - onSampleRateChange: event triggered by a change of sample rate
    141     // - onReset, onRandomize, onCreate, onDelete: implements special behavior when user clicks these from the context menu
    142 };
    143 
    144 
    145 void ComputerscareOhPeas::process(const ProcessArgs &args)
    146 {
    147     if (checkCounter > checkPeriod) {
    148         checkForChange();
    149         checkCounter = 0;
    150     }
    151     checkCounter++;
    152 
    153     float A, B, C, D, Q, a, b, c, d;
    154 
    155     int numDivisionsKnobValue = floor(params[NUM_DIVISIONS].getValue());
    156     int iTranspose = floor(numDivisionsKnobValue * params[GLOBAL_TRANSPOSE].getValue());
    157     int numInputChannels;
    158     int numScaleCVChannels;
    159     int numOffsetCVChannels;
    160 
    161     if (numDivisionsKnobValue != numDivisions)
    162     {
    163         //what a hack!!!
    164         if (numDivisionsKnobValue != 0)
    165         {
    166             numDivisions = numDivisionsKnobValue;
    167             setQuant();
    168         }
    169 
    170     }
    171     if (iTranspose != globalTranspose)
    172     {
    173         globalTranspose = iTranspose;
    174         setQuant();
    175     }
    176     for (int i = 0; i < numChannels; i++)
    177     {
    178         if (outputs[SCALED_OUTPUT + i].isConnected() || outputs[QUANTIZED_OUTPUT + i].isConnected()) {
    179             numInputChannels = inputs[CHANNEL_INPUT + i].getChannels();
    180             numScaleCVChannels = inputs[SCALE_CV + i].getChannels();
    181             numOffsetCVChannels = inputs[OFFSET_CV + i].getChannels();
    182             outputs[SCALED_OUTPUT + i].setChannels(numInputChannels);
    183             outputs[QUANTIZED_OUTPUT + i].setChannels(numInputChannels);
    184             for (int ch = 0; ch < std::max(numInputChannels, 1); ch++) {
    185 
    186 
    187                 a = params[SCALE_VAL + i].getValue();
    188 
    189                 b = params[SCALE_TRIM + i].getValue();
    190                 B = inputs[SCALE_CV + i].getVoltage(numScaleCVChannels == 1 ? 0 : ch);
    191                 A = inputs[CHANNEL_INPUT + i].getVoltage(ch);
    192 
    193                 c = params[OFFSET_TRIM + i].getValue();
    194                 C = inputs[OFFSET_CV + i].getVoltage(numOffsetCVChannels == 1 ? 0 : ch);
    195                 d = params[OFFSET_VAL + i].getValue();
    196 
    197                 D = (b * B + a) * A + (c * C + d);
    198 
    199                 Q = quant.quantizeEven(D, iTranspose);
    200 
    201                 outputs[SCALED_OUTPUT + i].setVoltage(D, ch);
    202                 outputs[QUANTIZED_OUTPUT + i].setVoltage(Q, ch);
    203             }
    204         }
    205     }
    206 }
    207 
    208 struct SetQuantizationModeMenuItem : MenuItem
    209 {
    210     ComputerscareOhPeas *peas;
    211 
    212     bool mode = true;
    213     SetQuantizationModeMenuItem(bool evenMode)
    214     {
    215         mode = evenMode;
    216     }
    217     void doAction()
    218     {
    219         peas->evenQuantizeMode = mode;
    220     }
    221     void step() override
    222     {
    223         rightText = CHECKMARK(peas->evenQuantizeMode == mode);
    224         MenuItem::step();
    225     }
    226 };
    227 struct PeasTF2 : ComputerscareTextField
    228 {
    229     ComputerscareOhPeas *module;
    230     int fontSize = 16;
    231     int rowIndex = 0;
    232     bool inError = false;
    233 
    234     PeasTF2()
    235     {
    236         ComputerscareTextField();
    237     };
    238     void draw(const DrawArgs &args) override
    239     {
    240         if (module)
    241         {
    242             if (module->manualSet) {
    243                 text = module->currentFormula;
    244                 module->manualSet = false;
    245             }
    246             if (text.c_str() != module->currentFormula)
    247             {
    248                 module->currentFormula = text.c_str();
    249             }
    250 
    251         }
    252         else {
    253             text = "2212221";
    254         }
    255         ComputerscareTextField::draw(args);
    256     }
    257 
    258     //void draw(const DrawArgs &args) override;
    259     //int getTextPosition(math::Vec mousePos) override;
    260 };
    261 struct PeasSmallDisplay : SmallLetterDisplay
    262 {
    263     ComputerscareOhPeas *module;
    264     int type;
    265     PeasSmallDisplay(int t)
    266     {
    267         type = t;
    268         SmallLetterDisplay();
    269     };
    270     void draw(const DrawArgs &args)
    271     {
    272         //this->setNumDivisionsString();
    273         if (module)
    274         {
    275             if (type == 0)
    276             {
    277 
    278                 std::string transposeString =  (module->globalTranspose > 0 ? "+" : "" ) + std::to_string(module->globalTranspose);
    279                 value = transposeString;
    280             }
    281             else
    282             {
    283                 std::string numDivisionsDisplay = std::to_string(module->numDivisions);
    284                 value = numDivisionsDisplay;
    285             }
    286 
    287         }
    288         else {
    289             value = std::to_string((random::u32() % 24) + 1);
    290         }
    291         SmallLetterDisplay::draw(args);
    292     }
    293 
    294 };
    295 
    296 
    297 
    298 void quantizationModeMenuItemAdd(ComputerscareOhPeas *peas, Menu *menu, bool evenMode, std::string label)
    299 {
    300     SetQuantizationModeMenuItem *menuItem = new SetQuantizationModeMenuItem(evenMode);
    301     menuItem->text = label;
    302     menuItem->peas = peas;
    303     menu->addChild(menuItem);
    304 }
    305 //this->numDivisions,this->globalTranspose
    306 struct ComputerscareOhPeasWidget : ModuleWidget
    307 {
    308     float randAmt = 0.f;
    309 
    310     ComputerscareOhPeasWidget(ComputerscareOhPeas *module)
    311     {
    312         setModule(module);
    313         //setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareOhPeasPanel.svg")));
    314         box.size = Vec(9 * 15, 380);
    315         {
    316             ComputerscareSVGPanel *panel = new ComputerscareSVGPanel();
    317             panel->box.size = box.size;
    318             panel->setBackground(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareOhPeasPanel.svg")));
    319 
    320             //module->panelRef = panel;
    321 
    322             addChild(panel);
    323 
    324         }
    325         double x = 1;
    326         double y = 7;
    327         //double dy = 18.4;
    328         double dx = 9.95;
    329         double xx;
    330         double yy = 18;
    331         addParam(createParam<MediumSnapKnob>(mm2px(Vec(11, yy - 2)), module, ComputerscareOhPeas::NUM_DIVISIONS ));
    332 
    333         addParam(createParam<SmoothKnob>(mm2px(Vec(21, yy - 2)), module, ComputerscareOhPeas::GLOBAL_TRANSPOSE));
    334 
    335         textFieldTemp = createWidget<PeasTF2>(mm2px(Vec(x, y + 24)));
    336         textFieldTemp->module = module;
    337         textFieldTemp->box.size = mm2px(Vec(44, 7));
    338         textFieldTemp->multiline = false;
    339         textFieldTemp->color = nvgRGB(0xC0, 0xE7, 0xDE);
    340         addChild(textFieldTemp);
    341 
    342         ndd = new PeasSmallDisplay(1);
    343         ndd->module = module;
    344         ndd->box.pos = mm2px(Vec(2, yy));
    345         ndd->box.size = mm2px(Vec(9, 7));
    346         ndd->value = "";
    347         ndd->baseColor = COLOR_COMPUTERSCARE_LIGHT_GREEN;
    348         addChild(ndd);
    349 
    350         transposeDisplay = new PeasSmallDisplay(0);
    351         transposeDisplay->module = module;
    352 
    353         transposeDisplay->box.pos = mm2px(Vec(30, yy));
    354         transposeDisplay->box.size = mm2px(Vec(11, 7));
    355         transposeDisplay->letterSpacing = 2.f;
    356         transposeDisplay->value = "";
    357         transposeDisplay->baseColor = COLOR_COMPUTERSCARE_LIGHT_GREEN;
    358         addChild(transposeDisplay);
    359 
    360         for (int i = 0; i < numChannels; i++)
    361         {
    362 
    363             xx = x + dx * i + randAmt * (2 * random::uniform() - .5);
    364             y += randAmt * (random::uniform() - .5);
    365             addInput(createInput<InPort>(mm2px(Vec(xx, y - 0.8)), module, ComputerscareOhPeas::CHANNEL_INPUT + i));
    366 
    367             addParam(createParam<SmallKnob>(mm2px(Vec(xx + 2, y + 34)), module, ComputerscareOhPeas::SCALE_TRIM + i));
    368 
    369             addInput(createInput<InPort>(mm2px(Vec(xx, y + 40)),  module, ComputerscareOhPeas::SCALE_CV + i));
    370 
    371             addParam(createParam<SmoothKnob>(mm2px(Vec(xx, y + 50)), module, ComputerscareOhPeas::SCALE_VAL + i));
    372 
    373             addParam(createParam<ComputerscareDotKnob>(mm2px(Vec(xx + 2, y + 64)), module, ComputerscareOhPeas::OFFSET_TRIM + i));
    374 
    375             addInput(createInput<InPort>(mm2px(Vec(xx, y + 70)),  module, ComputerscareOhPeas::OFFSET_CV + i));
    376 
    377 
    378             addParam(createParam<SmoothKnob>(mm2px(Vec(xx, y + 80)), module, ComputerscareOhPeas::OFFSET_VAL + i));
    379 
    380             addOutput(createOutput<OutPort>(mm2px(Vec(xx, y + 93)), module, ComputerscareOhPeas::SCALED_OUTPUT + i));
    381 
    382             addOutput(createOutput<InPort>(mm2px(Vec(xx + 1, y + 108)),  module, ComputerscareOhPeas::QUANTIZED_OUTPUT + i));
    383 
    384         }
    385         peas = module;
    386     }
    387 
    388     /*void fromJson(json_t *rootJ) override
    389     {
    390         std::string val;
    391         ModuleWidget::fromJson(rootJ);
    392 
    393         // legacy
    394         json_t *textJ = json_object_get(rootJ, "sequences");
    395         if (textJ) {
    396             peas->currentFormula = json_string_value(textJ);
    397             peas->manualSet = true;
    398         }
    399     }*/
    400 
    401     ComputerscareOhPeas *peas;
    402     PeasTF2 *textFieldTemp;
    403     SmallLetterDisplay *trimPlusMinus;
    404     PeasSmallDisplay *ndd;
    405     PeasSmallDisplay *transposeDisplay;
    406     void scaleItemAdd(ComputerscareOhPeas *peas, Menu *menu, std::string scale, std::string label);
    407     void appendContextMenu(Menu *menu) override;
    408 
    409 };
    410 struct SetScaleMenuItem : MenuItem
    411 {
    412     ComputerscareOhPeas *peas;
    413     ComputerscareOhPeasWidget *peasWidget;
    414     std::string scale = "221222";
    415     SetScaleMenuItem(std::string scaleInput)
    416     {
    417         scale = scaleInput;
    418     }
    419 
    420     void onAction(const event::Action &e) override
    421     {
    422         peasWidget->textFieldTemp->text = scale;
    423         peas->setQuant();
    424     }
    425 };
    426 void ComputerscareOhPeasWidget::scaleItemAdd(ComputerscareOhPeas *peas, Menu *menu, std::string scale, std::string label)
    427 {
    428     SetScaleMenuItem *menuItem = new SetScaleMenuItem(scale);
    429     menuItem->text = label;
    430     menuItem->peas = peas;
    431     menuItem->peasWidget = this;
    432     menu->addChild(menuItem);
    433 }
    434 void ComputerscareOhPeasWidget::appendContextMenu(Menu *menu)
    435 {
    436     ComputerscareOhPeas *peas = dynamic_cast<ComputerscareOhPeas *>(this->module);
    437 
    438     MenuLabel *spacerLabel = new MenuLabel();
    439     menu->addChild(spacerLabel);
    440 
    441 
    442     MenuLabel *modeLabel = new MenuLabel();
    443     modeLabel->text = "Scale Presets";
    444     menu->addChild(modeLabel);
    445 
    446     scaleItemAdd(peas, menu, "221222", "Major");
    447     scaleItemAdd(peas, menu, "212212", "Natural Minor");
    448     scaleItemAdd(peas, menu, "2232", "Major Pentatonic");
    449     scaleItemAdd(peas, menu, "3223", "Minor Pentatonic");
    450     scaleItemAdd(peas, menu, "32113", "Blues");
    451     scaleItemAdd(peas, menu, "11111111111", "Chromatic");
    452     scaleItemAdd(peas, menu, "212213", "Harmonic Minor");
    453     scaleItemAdd(peas, menu, "22222", "Whole-Tone");
    454     scaleItemAdd(peas, menu, "2121212", "Whole-Half Diminished");
    455 
    456     scaleItemAdd(peas, menu, "43", "Major Triad");
    457     scaleItemAdd(peas, menu, "34", "Minor Triad");
    458     scaleItemAdd(peas, menu, "33", "Diminished Triad");
    459     scaleItemAdd(peas, menu, "434", "Major 7 Tetrachord");
    460     scaleItemAdd(peas, menu, "433", "Dominant 7 Tetrachord");
    461     scaleItemAdd(peas, menu, "343", "Minor 7 Tetrachord");
    462     scaleItemAdd(peas, menu, "334", "Minor 7 b5 Tetrachord");
    463 }
    464 
    465 // Specify the Module and ModuleWidget subclass, human-readable
    466 // author name for categorization per plugin, module slug (should never
    467 // change), human-readable module name, and any number of tags
    468 // (found in `include/tags.hpp`) separated by commas.
    469 
    470 
    471 //Model *modelComputerscareDebug = createModel<ComputerscareDebug, ComputerscareDebugWidget>("computerscare-debug");
    472 
    473 Model *modelComputerscareOhPeas = createModel<ComputerscareOhPeas, ComputerscareOhPeasWidget>("computerscare-ohpeas");