computerscare-vcv-modules

ComputerScare modules for VCV Rack
Log | Files | Refs

ComputerscareILoveCookies.cpp (25334B)


      1 #include "Computerscare.hpp"
      2 #include "dtpulse.hpp"
      3 
      4 #include <string>
      5 #include <sstream>
      6 #include <iomanip>
      7 
      8 struct ComputerscareILoveCookies;
      9 struct CookiesTF2;
     10 struct CookiesSmallDisplay;
     11 struct CookiesCurrentStepDisplay;
     12 
     13 const int numFields = 6;
     14 const int numKnobRows = 13;
     15 const int numKnobColumns = 2;
     16 const int numInputRows = 13;
     17 const int numInputColumns = 2;
     18 
     19 const int numKnobs = numKnobRows * numKnobColumns;
     20 const int numInputs = numInputRows * numInputColumns;
     21 const std::vector<NVGcolor> outlineColorMap = {COLOR_COMPUTERSCARE_RED, COLOR_COMPUTERSCARE_YELLOW, COLOR_COMPUTERSCARE_BLUE};
     22 
     23 const std::string uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     24 
     25 struct ComputerscareILoveCookies : Module {
     26   enum ParamIds {
     27     KNOB_PARAM,
     28     MANUAL_CLOCK_PARAM = KNOB_PARAM + numKnobs,
     29     MANUAL_RESET_PARAM,
     30     INDIVIDUAL_RESET_PARAM,
     31     NUM_PARAMS = INDIVIDUAL_RESET_PARAM + numFields
     32   };
     33   enum InputIds {
     34     GLOBAL_CLOCK_INPUT,
     35     GLOBAL_RESET_INPUT,
     36     CLOCK_INPUT,
     37     RESET_INPUT = CLOCK_INPUT + numFields,
     38     SIGNAL_INPUT = RESET_INPUT + numFields ,
     39     NUM_INPUTS = SIGNAL_INPUT + numInputs
     40   };
     41   enum OutputIds {
     42     TRG_OUTPUT,
     43     FIRST_STEP_OUTPUT = TRG_OUTPUT + numFields,
     44     NUM_OUTPUTS = FIRST_STEP_OUTPUT + numFields
     45   };
     46   enum LightIds {
     47     SWITCH_LIGHTS,
     48     NUM_LIGHTS = SWITCH_LIGHTS + (numKnobs + numInputs) * numFields
     49   };
     50 
     51   rack::dsp::SchmittTrigger globalClockTrigger;
     52   rack::dsp::SchmittTrigger globalResetTriggerInput;
     53 
     54   rack::dsp::SchmittTrigger globalManualClockTrigger;
     55   rack::dsp::SchmittTrigger globalManualResetTrigger;
     56 
     57   rack::dsp::SchmittTrigger clockTriggers[numFields];
     58   rack::dsp::SchmittTrigger resetTriggers[numFields];
     59 
     60   rack::dsp::SchmittTrigger manualResetTriggers[numFields];
     61 
     62   SmallLetterDisplay* smallLetterDisplays[numFields];
     63   SmallLetterDisplay* currentWorkingStepDisplays[numFields];
     64 
     65   AbsoluteSequence newABS[numFields];
     66   AbsoluteSequence newABSQueue[numFields];
     67 
     68   std::string currentFormula[numFields];
     69   std::string currentTextFieldValue[numFields];
     70 
     71   std::string upcomingFormula[numFields];
     72   std::string lastValue[numFields];
     73 
     74 
     75   bool manualSet[numFields];
     76   bool inError[numFields];
     77   bool shouldChange[numFields] = {false};
     78   bool changeImminent[numFields] = {false};
     79 
     80   int activeKnobIndex[numFields] = {0};
     81 
     82   int knobRangeEnum = 0;
     83 
     84   int checkCounter = 0;
     85   int checkCounterLimit = 10000;
     86 
     87   bool jsonLoaded = false;
     88 
     89   std::vector<ParamWidget*> smallLetterKnobs;
     90 
     91   ComputerscareILoveCookies() {
     92     config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
     93     for (int i = 0; i < numFields; i++) {
     94       manualSet[i] = false;
     95       inError[i] = false;
     96 
     97       currentFormula[i] = "";
     98       lastValue[i] = "";
     99       setNextAbsoluteSequence(i);
    100       checkIfShouldChange(i);
    101       resetOneOfThem(i);
    102 
    103       std::string rowi = std::to_string(i + 1);
    104 
    105       configButton(INDIVIDUAL_RESET_PARAM + i, "Reset Row " + rowi );
    106 
    107       configInput(CLOCK_INPUT + i, "Row " + rowi + " Clock");
    108       configInput(RESET_INPUT + i, "Row " + rowi + " Reset");
    109 
    110       configOutput(TRG_OUTPUT + i, "Row " + rowi + " CV");
    111       configOutput(FIRST_STEP_OUTPUT + i, "Row " + rowi + " End of Cycle");
    112     }
    113     for (int k = 0; k < numKnobs; k++) {
    114       configParam( KNOB_PARAM + k, 0.f, 10.f, 0.0f, string::f("knob %c", knoblookup[k]));
    115 
    116       configInput(SIGNAL_INPUT + k, string::f("%c", uppercaseLetters.at(k)));
    117     }
    118 
    119     configButton(MANUAL_CLOCK_PARAM, "Manual Clock Advance");
    120     configButton(MANUAL_RESET_PARAM, "Manual Reset");
    121 
    122     configInput(GLOBAL_CLOCK_INPUT, "Global Clock");
    123     configInput(GLOBAL_RESET_INPUT, "Global Reset");
    124 
    125   }
    126   json_t *dataToJson() override {
    127     json_t *rootJ = json_object();
    128 
    129     json_t *sequencesJ = json_array();
    130     json_t *knobRangeJ = json_integer(knobRangeEnum);
    131 
    132     for (int i = 0; i < numFields; i++) {
    133       json_t *sequenceJ = json_string(currentTextFieldValue[i].c_str());
    134       json_array_append_new(sequencesJ, sequenceJ);
    135 
    136 
    137     }
    138     json_object_set_new(rootJ, "sequences", sequencesJ);
    139     json_object_set_new(rootJ, "knobRange", knobRangeJ);
    140 
    141     return rootJ;
    142   }
    143 
    144   void dataFromJson(json_t *rootJ) override {
    145     std::string val;
    146     int count;
    147     json_t *sequencesJ = json_object_get(rootJ, "sequences");
    148     if (sequencesJ) {
    149       for (int i = 0; i < numFields; i++) {
    150 
    151         json_t *sequenceJ = json_array_get(sequencesJ, i);
    152         if (sequenceJ) {
    153           val = json_string_value(sequenceJ);
    154 
    155           // currentFormula[i] = val;
    156           //currentTextFieldValue[i] = val;
    157           currentTextFieldValue[i] = val;
    158 
    159           manualSet[i] = true;
    160         }
    161       }
    162       jsonLoaded = true;
    163     }
    164     else {
    165       json_t *textJLegacy = json_object_get(rootJ, "data");
    166       if (textJLegacy) {
    167         json_t *seqJLegacy = json_object_get(textJLegacy, "sequences");
    168 
    169         if (seqJLegacy) {
    170           for (int i = 0; i < numFields; i++) {
    171             json_t *sequenceJ = json_array_get(seqJLegacy, i);
    172             if (sequenceJ)
    173               val = json_string_value(sequenceJ);
    174             // currentFormula[i] = val;
    175             //lastValue[i] = val;
    176             currentTextFieldValue[i] = val;
    177             //upcomingFormula[i]=val;
    178             manualSet[i] = true;
    179 
    180           }
    181         }
    182       }
    183     }
    184     json_t *knobRangeJ = json_object_get(rootJ, "knobRange");
    185     if (knobRangeJ) {
    186       knobRangeEnum = json_integer_value(knobRangeJ);
    187     }
    188 
    189 
    190   }
    191   void process(const ProcessArgs &args) override;
    192 
    193 
    194 
    195   void onRandomize() override {
    196   }
    197   void randomizeShuffle() {
    198 
    199   }
    200   void wiggleKnobs() {
    201     for (int i = 0; i < numKnobs; i++) {
    202       float prev = params[KNOB_PARAM + i].getValue();
    203       if (random::uniform() < 0.7) {
    204         float rv = (10 * random::uniform() + 2 * prev) / 3;
    205         params[KNOB_PARAM + i].setValue(rv);
    206       }
    207     }
    208   }
    209   std::string randomCookieFormula() {
    210     std::string mainlookup = knoblookup;
    211     std::string str = "";
    212     std::string randchar = "";
    213 
    214     float ru;
    215     int length = 0;
    216     length = floor(random::uniform() * 12) + 2;
    217     str = "";
    218     for (int j = 0; j < length; j++) {
    219       randchar = mainlookup[floor(random::uniform() * mainlookup.size())];
    220       str = str + randchar;
    221       ru = random::uniform();
    222       if (ru < 0.1) {
    223         str = "(" + str + ")";
    224       }
    225     }
    226     return str;
    227   }
    228 
    229   void randomizeAllFields() {
    230     for (int i = 0; i < numFields; i++) {
    231       randomizeAField(i);
    232     }
    233   }
    234 
    235   void randomizeAField(int i) {
    236     currentTextFieldValue[i] = randomCookieFormula();
    237     manualSet[i] = true;
    238     setNextAbsoluteSequence(i);
    239 
    240 
    241   }
    242   void setNextAbsoluteSequence(int index) {
    243     newABSQueue[index] = AbsoluteSequence(upcomingFormula[index], knobandinputlookup);
    244     shouldChange[index] = true;
    245   }
    246   void setAbsoluteSequenceFromQueue(int index) {
    247     newABS[index] = newABSQueue[index];
    248     currentFormula[index] = upcomingFormula[index];
    249     newABS[index].incrementAndCheck();
    250   }
    251   void checkIfShouldChange(int index) {
    252     if (shouldChange[index]) {
    253       setAbsoluteSequenceFromQueue(index);
    254       shouldChange[index] = false;
    255     }
    256   }
    257 
    258   void incrementInternalStep(int i) {
    259     newABS[i].incrementAndCheck();
    260     if (newABS[i].readHead == 0) {
    261       setChangeImminent(i, false);
    262       checkIfShouldChange(i);
    263     }
    264   }
    265 
    266   void resetOneOfThem(int i) {
    267     newABS[i].readHead = -1;
    268   }
    269   void setChangeImminent(int i, bool value) {
    270     changeImminent[i] = value;
    271   }
    272   std::string getDisplayString(int index) {
    273     std::string lhs = std::to_string(this->newABS[index].readHead + 1);
    274     std::string rhs =  std::to_string(this->newABS[index].numTokens);
    275 
    276     padTo(lhs, 3, ' ');
    277     padTo(rhs, 3, ' ');
    278 
    279     std::string val = lhs + "\n" + rhs;
    280 
    281     return val;
    282   }
    283   float mapKnobValue(float rawValue, int rowIndex) {
    284     // raw value is between 0 and +10
    285 
    286     float mappedValue = 0.f;
    287     int mapEnum = knobRangeEnum;
    288     switch (mapEnum) {
    289     case 0: mappedValue = rawValue; break;//0..10
    290     case 1: mappedValue = mapValue(rawValue, 0.f, 0.5); break;//0..5
    291     case 2: mappedValue = mapValue(rawValue, 0.f, 0.2); break;//0..2
    292     case 3: mappedValue = mapValue(rawValue, 0.f, 0.1); break;//0..1
    293 
    294 
    295     case 4: mappedValue = mapValue(rawValue, -5, 2.f); break;//-10..10
    296     case 5: mappedValue = mapValue(rawValue, -5, 1.f); break;//-5..5
    297 
    298     case 6: mappedValue = mapValue(rawValue, -5, 0.4); break;//-2..2
    299 
    300     case 7: mappedValue = mapValue(rawValue, -5, 0.2); break;//-1..1
    301     }
    302     return mappedValue;
    303   }
    304   float mapValue(float input, float offset, float multiplier) {
    305     return (input + offset) * multiplier;
    306   }
    307   void checkTextField(int channel) {
    308     std::string textFieldValue = currentTextFieldValue[channel];
    309 
    310     if (textFieldValue != currentFormula[channel] && textFieldValue != upcomingFormula[channel]) {
    311 
    312       AbsoluteSequence pendingSequence = AbsoluteSequence(textFieldValue, knobandinputlookup);
    313       if (!pendingSequence.inError && matchParens(textFieldValue)) {
    314         upcomingFormula[channel] = textFieldValue;
    315         setNextAbsoluteSequence(channel);
    316         inError[channel] = false;
    317       }
    318       else {
    319         inError[channel] = true;
    320       }
    321     }
    322 
    323   }
    324 
    325 };
    326 
    327 
    328 void ComputerscareILoveCookies::process(const ProcessArgs &args) {
    329 
    330 
    331   bool globalGateIn = globalClockTrigger.isHigh();
    332   bool activeStep = 0;
    333   bool atFirstStep = false;
    334   bool globalTriggerClocked = globalClockTrigger.process(inputs[GLOBAL_CLOCK_INPUT].getVoltage());
    335   bool globalManualResetClicked = globalManualResetTrigger.process(params[MANUAL_RESET_PARAM].getValue());
    336   bool globalManualClockClicked = globalManualClockTrigger.process(params[MANUAL_CLOCK_PARAM].getValue());
    337   bool currentTriggerIsHigh;
    338   bool currentTriggerClocked;
    339   bool globalResetTriggered = globalResetTriggerInput.process(inputs[GLOBAL_RESET_INPUT].getVoltage() / 2.f);
    340   bool currentResetActive;
    341   bool currentResetTriggered;
    342   bool currentManualResetClicked;
    343   bool outputConnected;
    344   float knobRawValue = 0.f;
    345   float inV[16] = {10.f};
    346 
    347   if (checkCounter > checkCounterLimit) {
    348     if (!jsonLoaded) {
    349       for (int i = 0; i < numFields; i++) {
    350         //currentTextFieldValue[i] = i < numFields - 1 ? std::to_string(i + 1) : "abcd";
    351         manualSet[i] = true;
    352       }
    353       for (int i = 0; i < numFields; i++) {
    354         checkTextField(i);
    355         checkIfShouldChange(i);
    356       }
    357       jsonLoaded = true;
    358     }
    359     else {
    360       for (int i = 0; i < numFields; i++) {
    361         checkTextField(i);
    362       }
    363     }
    364     checkCounter = 0;
    365   }
    366   checkCounter++;
    367 
    368   for (int i = 0; i < numFields; i++) {
    369     activeStep = false;
    370     currentResetActive = inputs[RESET_INPUT + i].isConnected();
    371     outputConnected = outputs[TRG_OUTPUT + i].isConnected();
    372 
    373     currentResetTriggered = resetTriggers[i].process(inputs[RESET_INPUT + i].getVoltage() / 2.f);
    374     currentManualResetClicked = manualResetTriggers[i].process(params[INDIVIDUAL_RESET_PARAM + i].getValue());
    375 
    376     currentTriggerIsHigh = clockTriggers[i].isHigh();
    377     currentTriggerClocked = clockTriggers[i].process(inputs[CLOCK_INPUT + i].getVoltage());
    378 
    379     if (true) {
    380       if (inputs[CLOCK_INPUT + i].isConnected()) {
    381         if (currentTriggerClocked) {
    382           incrementInternalStep(i);
    383           activeKnobIndex[i] = newABS[i].peekWorkingStep();
    384         }
    385       }
    386       else {
    387         if ((inputs[GLOBAL_CLOCK_INPUT].isConnected() && globalTriggerClocked) || globalManualClockClicked) {
    388           incrementInternalStep(i);
    389           activeKnobIndex[i] = newABS[i].peekWorkingStep();
    390         }
    391       }
    392       if ((currentResetActive && currentResetTriggered) || (!currentResetActive && globalResetTriggered)) {
    393         resetOneOfThem(i);
    394       }
    395 
    396       atFirstStep = (newABS[i].readHead == 0);
    397 
    398       if (globalManualResetClicked || currentManualResetClicked) {
    399         setChangeImminent(i, true);
    400         resetOneOfThem(i);
    401       }
    402       if ( (currentResetActive && currentResetTriggered) || (!currentResetActive && globalResetTriggered)) {
    403         resetOneOfThem(i);
    404         setChangeImminent(i, false);
    405       }
    406       else {
    407         if (atFirstStep && !currentResetActive && !inputs[GLOBAL_RESET_INPUT].isConnected()) {
    408           //checkIfShouldChange(i);
    409         }
    410       }
    411     }
    412     if (outputConnected) {
    413       if (activeKnobIndex[i] < 0) {
    414         outputs[TRG_OUTPUT + i].setChannels(1);
    415         outputs[TRG_OUTPUT + i].setVoltage(0.f);
    416       }
    417       else if (activeKnobIndex[i] < 26) {
    418         outputs[TRG_OUTPUT + i].setChannels(1);
    419         knobRawValue = params[activeKnobIndex[i]].getValue();
    420         outputs[TRG_OUTPUT + i].setVoltage(mapKnobValue(knobRawValue, i));
    421       }
    422       else if (activeKnobIndex[i] < 52) {
    423         outputs[TRG_OUTPUT + i].setChannels(inputs[SIGNAL_INPUT + activeKnobIndex[i] - 26].getChannels());
    424         inputs[SIGNAL_INPUT + activeKnobIndex[i] - 26].readVoltages(inV);
    425         outputs[TRG_OUTPUT + i].writeVoltages(inV);
    426       }
    427       else if (activeKnobIndex[i] < 78) {
    428         outputs[TRG_OUTPUT + i].setChannels(1);
    429         outputs[TRG_OUTPUT + i].setVoltage(newABS[i].exactFloats[activeKnobIndex[i] - 52]);
    430       }
    431       else if (activeKnobIndex[i] < 104) {
    432         outputs[TRG_OUTPUT + i].setChannels(1);
    433         outputs[TRG_OUTPUT + i].setVoltage(2.22);
    434       }
    435       else {
    436         outputs[TRG_OUTPUT + i].setChannels(1);
    437         outputs[TRG_OUTPUT + i].setVoltage(0.f);
    438       }
    439     }
    440     if (inputs[CLOCK_INPUT + i].isConnected()) {
    441       outputs[FIRST_STEP_OUTPUT + i].setVoltage((currentTriggerIsHigh && atFirstStep) ? 10.f : 0.0f);
    442     }
    443     else {
    444       outputs[FIRST_STEP_OUTPUT + i].setVoltage((globalGateIn && atFirstStep) ? 10.f : 0.0f);
    445     }
    446   }
    447 }
    448 
    449 
    450 struct WiggleKnobsMenuItem : MenuItem {
    451   ComputerscareILoveCookies *cookies;
    452   void onAction(const event::Action &e) override {
    453     cookies->wiggleKnobs();
    454   }
    455 };
    456 struct RandomizeTextFieldsMenuItem : MenuItem {
    457   ComputerscareILoveCookies *cookies;
    458   void onAction(const event::Action &e) override {
    459     srand(time(0));
    460     cookies->randomizeAllFields();
    461 
    462   }
    463 };
    464 struct CookiesKnobRangeItem : MenuItem {
    465   ComputerscareILoveCookies *cookies;
    466   int knobRangeEnum;
    467   void onAction(const event::Action &e) override {
    468     cookies->knobRangeEnum = knobRangeEnum;
    469   }
    470   void step() override {
    471     rightText = CHECKMARK(cookies->knobRangeEnum == knobRangeEnum);
    472     MenuItem::step();
    473   }
    474 };
    475 struct CookiesTF2 : ComputerscareTextField
    476 {
    477   ComputerscareILoveCookies *module;
    478   int rowIndex = 0;
    479 
    480   CookiesTF2(int i)
    481   {
    482     rowIndex = i;
    483     dimWithRoom = false;
    484     ComputerscareTextField();
    485   };
    486   void draw(const DrawArgs &args) override
    487   {
    488     if (module)
    489     {
    490       if (module->manualSet[rowIndex]) {
    491         text = module->currentTextFieldValue[rowIndex];
    492         module->manualSet[rowIndex] = false;
    493       }
    494       std::string value = text.c_str();
    495       module->currentTextFieldValue[rowIndex] = value;
    496       inError = module->inError[rowIndex];
    497     }
    498     else {
    499       text = "we,love{}@9,cook(ies)";
    500     }
    501     ComputerscareTextField::draw(args);
    502   }
    503 };
    504 
    505 struct CookiesSmallDisplay : SmallLetterDisplay
    506 {
    507   ComputerscareILoveCookies *module;
    508   int type;
    509   int index;
    510   CookiesSmallDisplay(int i)
    511   {
    512     index = i;
    513     SmallLetterDisplay();
    514   };
    515   void draw(const DrawArgs &args)
    516   {
    517     //this->setNumDivisionsString();
    518     if (module)
    519     {
    520       value = module->getDisplayString(index);
    521       blink = module->shouldChange[index];
    522       doubleblink = module->changeImminent[index];
    523       SmallLetterDisplay::draw(args);
    524     }
    525     else {
    526       value = "4\n20";
    527       SmallLetterDisplay::draw(args);
    528     }
    529   }
    530 
    531 };
    532 struct CookiesCurrentStepDisplay : SmallLetterDisplay
    533 {
    534   ComputerscareILoveCookies *module;
    535   int type;
    536   int index;
    537   CookiesCurrentStepDisplay(int i)
    538   {
    539     index = i;
    540     fontSize = 26;
    541     textAlign = 4;
    542     SmallLetterDisplay();
    543   };
    544   void draw(const DrawArgs &args)
    545   {
    546     if (module)
    547     {
    548       value = module->newABS[index].getWorkingStepDisplay();
    549 
    550       SmallLetterDisplay::draw(args);
    551     }
    552   }
    553 
    554 };
    555 
    556 struct ComputerscareILoveCookiesWidget : ModuleWidget {
    557 
    558   double verticalSpacing = 18.4;
    559   int verticalStart = 24;
    560   double xStart = 41;
    561   int index = 0;
    562   int inputindex = 0;
    563   double knobPosX = 0.0;
    564   double knobPosY = 0.0;
    565   double knobXStart = 20;
    566   double knobYStart = 2;
    567   double knobRowWidth = 11;
    568   double knobColumnHeight = 9.58;
    569 
    570   double inputPosX = 0.0;
    571   double inputPosY = 0.0;
    572   double inputXStart = 0;
    573   double inputYStart = 0;
    574   double inputRowWidth = 9.4;
    575   double inputColumnHeight = 9.7;
    576 
    577   ComputerscareILoveCookiesWidget(ComputerscareILoveCookies *module) {
    578     setModule(module);
    579     setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/ComputerscareILoveCookiesPanel.svg")));
    580 
    581     for (int i = 0; i < numKnobRows; i++) {
    582       for (int j = 0; j < numKnobColumns; j++) {
    583         knobPosX = knobXStart + j * knobRowWidth;
    584         knobPosY = knobYStart + i * knobColumnHeight + j * 2.0;
    585         index = numKnobColumns * i + j;
    586 
    587 
    588         //label for knob a-z
    589         smallLetterDisplay = new SmallLetterDisplay();
    590         smallLetterDisplay->box.pos = mm2px(Vec(knobPosX + 6, knobPosY - 2));
    591         smallLetterDisplay->box.size = Vec(20, 20);
    592         smallLetterDisplay->value = knoblookup[index];
    593 
    594         addChild(smallLetterDisplay);
    595 
    596         ParamWidget* knob =  createParam<SmoothKnob>(mm2px(Vec(knobPosX, knobPosY)), module, ComputerscareILoveCookies::KNOB_PARAM + index);
    597 
    598         //module->smallLetterKnobs.push_back(knob);
    599         addParam(knob);
    600 
    601       }
    602     }
    603     for (int k = 0; k < numInputRows; k++) {
    604       for (int m = 0; m < numInputColumns; m++) {
    605         inputPosX = inputXStart + m * inputRowWidth;
    606         inputPosY = inputYStart + k * inputColumnHeight + m * 2.0;
    607         inputindex = numInputColumns * k + m;
    608 
    609         if (m % 2) {
    610           addInput(createInput<InPort>(mm2px(Vec(inputPosX , inputPosY)),  module, ComputerscareILoveCookies::SIGNAL_INPUT + inputindex));
    611         }
    612         else {
    613           addInput(createInput<PointingUpPentagonPort>(mm2px(Vec(inputPosX , inputPosY)),  module, ComputerscareILoveCookies::SIGNAL_INPUT + inputindex));
    614         }
    615 
    616         //label for input A-Z
    617         smallLetterDisplay = new SmallLetterDisplay();
    618         smallLetterDisplay->box.pos = mm2px(Vec(inputPosX + 6, inputPosY - 1));
    619         smallLetterDisplay->box.size = Vec(20, 20);
    620         smallLetterDisplay->value = inputlookup[inputindex];
    621 
    622         addChild(smallLetterDisplay);
    623         //module->smallLetterDisplays[i] = smallLetterDisplay;
    624       }
    625     }
    626 
    627     //global clock input
    628     addInput(createInput<InPort>(mm2px(Vec(2 + xStart , 0)), module, ComputerscareILoveCookies::GLOBAL_CLOCK_INPUT));
    629 
    630     //global reset input
    631     addInput(createInput<InPort>(mm2px(Vec(12 + xStart , 0)),  module, ComputerscareILoveCookies::GLOBAL_RESET_INPUT));
    632     addParam(createParam<ComputerscareResetButton>(mm2px(Vec(12 + xStart , 9)), module, ComputerscareILoveCookies::MANUAL_RESET_PARAM));
    633     addParam(createParam<ComputerscareClockButton>(mm2px(Vec(2 + xStart , 9)), module, ComputerscareILoveCookies::MANUAL_CLOCK_PARAM));
    634 
    635 
    636     for (int i = 0; i < numFields; i++) {
    637       //first-step output
    638       addOutput(createOutput<OutPort>(mm2px(Vec(42 + xStart , verticalStart + verticalSpacing * i - 11)), module, ComputerscareILoveCookies::FIRST_STEP_OUTPUT + i));
    639 
    640       //individual output
    641       addOutput(createOutput<OutPort>(mm2px(Vec(54 + xStart , verticalStart + verticalSpacing * i - 11)),  module, ComputerscareILoveCookies::TRG_OUTPUT + i));
    642 
    643       //individual clock input
    644       addInput(createInput<InPort>(mm2px(Vec(2 + xStart, verticalStart + verticalSpacing * i - 10)),  module, ComputerscareILoveCookies::CLOCK_INPUT + i));
    645 
    646       //individual reset input
    647       addInput(createInput<InPort>(mm2px(Vec(12 + xStart, verticalStart + verticalSpacing * i - 10)), module, ComputerscareILoveCookies::RESET_INPUT + i));
    648 
    649       //sequence input field
    650       textField = new CookiesTF2(i);
    651       textField->box.pos = mm2px(Vec(1 + xStart, verticalStart + verticalSpacing * i));
    652       textField->box.size = mm2px(Vec(63, 7));
    653       textField->multiline = false;
    654       textField->color = nvgRGB(0xC0, 0xE7, 0xDE);
    655       textField->module = module;
    656       addChild(textField);
    657       cookiesTextFields[i] = textField;
    658 
    659       //active/total steps display
    660       cookiesSmallDisplay = new CookiesSmallDisplay(i);
    661       cookiesSmallDisplay->box.pos = mm2px(Vec(21 + xStart, verticalStart - 9.2 + verticalSpacing * i));
    662       cookiesSmallDisplay->box.size = Vec(60, 30);
    663       cookiesSmallDisplay->baseColor = COLOR_COMPUTERSCARE_LIGHT_GREEN;
    664       cookiesSmallDisplay->value = "?\n?";
    665       addChild(cookiesSmallDisplay);
    666       cookiesSmallDisplay->module = module;
    667       cookiesSmallDisplays[i] = cookiesSmallDisplay;
    668 
    669       //active/total steps display
    670       currentWorkingStepDisplay = new CookiesCurrentStepDisplay(i);
    671       currentWorkingStepDisplay->box.pos = mm2px(Vec(11 + xStart, verticalStart - 7.0 + verticalSpacing * i));
    672       currentWorkingStepDisplay->box.size = mm2px(Vec(2, 10));
    673 
    674       currentWorkingStepDisplay->value = "?";
    675       currentWorkingStepDisplay->module = module;
    676       addChild(currentWorkingStepDisplay);
    677       currentWorkingStepDisplays[i] = currentWorkingStepDisplay;
    678 
    679       addParam(createParam<ComputerscareInvisibleButton>(mm2px(Vec(21 + xStart, verticalStart - 9.9 + verticalSpacing * i)), module, ComputerscareILoveCookies::INDIVIDUAL_RESET_PARAM + i));
    680     }
    681     cookies = module;
    682   }
    683 
    684 
    685   /*void fromJson(json_t *rootJ) override
    686   { std::string val;
    687     ModuleWidget::fromJson(rootJ);
    688     json_t *sequencesJ = json_object_get(rootJ, "sequences");//legacy
    689     if (sequencesJ) {
    690       for (int i = 0; i < numFields; i++) {
    691         json_t *sequenceJ = json_array_get(sequencesJ, i);
    692         if (sequenceJ) {
    693           val = json_string_value(sequenceJ);
    694           cookies->currentTextFieldValue[i] = val;
    695           cookies->manualSet[i] = true;
    696         }
    697       }
    698       cookies->jsonLoaded = true;
    699     }
    700     else {
    701       json_t *textJLegacy = json_object_get(rootJ, "data");
    702       if (textJLegacy) {
    703         json_t *seqJLegacy = json_object_get(textJLegacy, "sequences");
    704 
    705         if (seqJLegacy) {
    706           for (int i = 0; i < numFields; i++) {
    707             json_t *sequenceJ = json_array_get(seqJLegacy, i);
    708             if (sequenceJ)
    709               val = json_string_value(sequenceJ);
    710             cookiesTextFields[i]->text = val;
    711             cookies->currentFormula[i] = val;
    712           }
    713         }
    714       }
    715     }
    716   }*/
    717 
    718 
    719   ComputerscareILoveCookies *cookies;
    720 
    721   CookiesTF2 *textField;
    722   CookiesTF2 *cookiesTextFields[numFields];
    723 
    724 
    725 
    726   CookiesSmallDisplay* cookiesSmallDisplay;
    727   CookiesSmallDisplay* cookiesSmallDisplays[numFields];
    728 
    729   SmallLetterDisplay* smallLetterDisplay;
    730 
    731   CookiesCurrentStepDisplay* currentWorkingStepDisplay;
    732   CookiesCurrentStepDisplay* currentWorkingStepDisplays[numFields];
    733 
    734 
    735   void appendContextMenu(Menu *menu) override;
    736 };
    737 
    738 void ComputerscareILoveCookiesWidget::appendContextMenu(Menu *menu) {
    739   ComputerscareILoveCookies *cookiesModule = dynamic_cast<ComputerscareILoveCookies *>(this->module);
    740 
    741   MenuLabel *spacerLabel = new MenuLabel();
    742   menu->addChild(spacerLabel);
    743 
    744   MenuLabel *modeLabel = new MenuLabel();
    745   modeLabel->text = "Premium Randomizations";
    746   menu->addChild(modeLabel);
    747 
    748   WiggleKnobsMenuItem *wiggleKnobsMenuItem = new WiggleKnobsMenuItem();
    749   wiggleKnobsMenuItem->text = "Wiggle Knobs";
    750   wiggleKnobsMenuItem->cookies = cookiesModule;
    751   menu->addChild(wiggleKnobsMenuItem);
    752 
    753   RandomizeTextFieldsMenuItem *randomizeTextFieldsMenuItem = new RandomizeTextFieldsMenuItem();
    754   randomizeTextFieldsMenuItem->text = "Randomize Text Fields";
    755   randomizeTextFieldsMenuItem->cookies = cookiesModule;
    756   menu->addChild(randomizeTextFieldsMenuItem);
    757 
    758 
    759   menu->addChild(construct<MenuLabel>());
    760   menu->addChild(construct<MenuLabel>(&MenuLabel::text, "Knob Range"));
    761   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, "  0v ... +10v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 0));
    762   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, "  0v ...  +5v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 1));
    763   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, "  0v ...  +2v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 2));
    764   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, "  0v ...  +1v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 3));
    765   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, "-10v ... +10v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 4));
    766   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, " -5v ...  +5v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 5));
    767   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, " -2v ...  +2v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 6));
    768   menu->addChild(construct<CookiesKnobRangeItem>(&MenuItem::text, " -1v ...  +1v", &CookiesKnobRangeItem::cookies, cookiesModule, &CookiesKnobRangeItem::knobRangeEnum, 7));
    769 
    770 };
    771 
    772 
    773 
    774 
    775 
    776 
    777 Model *modelComputerscareILoveCookies = createModel<ComputerscareILoveCookies, ComputerscareILoveCookiesWidget>("computerscare-i-love-cookies");