#include "dcb.h"
#include "rnd.h"

extern Model *modelPad2;
#define MAX_PATS 100

struct SlewLimiter {
  float delta = 0.f;
  float last = 0.f;
  float target = 0.f;

  void setParams(float sampleTime, float seconds, float to) {
    if(seconds == 0)
      delta = 0;
    else if(target != to) {
      delta = (to - last) * sampleTime / seconds;
    }
    target = to;
  }

  float process() {
    if(delta == 0.f) return target;
    if(delta < 0.f)
      return last = std::max(last + delta, target);
    return last = std::min(last + delta, target);
  }
};

#define MAX_KNOBS 3

struct FadersPreset {
  float faderValues[48] = {};
  float knobValues[MAX_KNOBS] = {};

  int maxChannels[3] = {16, 16, 16};
  float min[3] = {-10, -10, -10};
  float max[3] = {10, 10, 10};
  unsigned int snaps[3] = {0, 0, 0};

  void reset() {
    for(int k = 0;k < 48;k++) {
      faderValues[k] = 0.f;
    }
    for(int k = 0;k < 3;k++) {
      maxChannels[k] = 16;
      min[k] = -10;
      max[k] = 10;
      snaps[k] = 0;
    }
    for(int k = 0;k < MAX_KNOBS;k++) {
      knobValues[k] = 0.f;
    }
  }

  void setValue(int row, int nr, float value) {
    faderValues[row * 16 + nr] = value;
  }

  void set(const FadersPreset &other) {
    for(int k = 0;k < 48;k++) {
      faderValues[k] = other.faderValues[k];
    }
    for(int k = 0;k < MAX_KNOBS;k++) {
      knobValues[k] = other.knobValues[k];
    }
    for(int k = 0;k < 3;k++) {
      min[k] = other.min[k];
      max[k] = other.max[k];
      maxChannels[k] = other.maxChannels[k];
    }
  }

  std::string getValuesAsString() {
    std::string s;
    for(int j = 0;j < 48;j++) {
      if(j > 0) s.append(",");
      int n = j / 16;
      s.append(std::to_string(rescale(faderValues[j], min[n], max[n], 0.f, 1.f)));
    }
    return s;
  }

  json_t *toJson() {
    json_t *data = json_object();
    json_t *valueList = json_array();
    for(int k = 0;k < 48;k++) {
      json_array_append_new(valueList, json_real(faderValues[k]));
    }
    json_object_set_new(data, "values", valueList);
    json_t *minList = json_array();
    for(int k = 0;k < 3;k++) {
      json_array_append_new(minList, json_real(min[k]));
    }
    json_object_set_new(data, "min", minList);

    json_t *maxList = json_array();
    for(int k = 0;k < 3;k++) {
      json_array_append_new(maxList, json_real(max[k]));
    }
    json_object_set_new(data, "max", maxList);

    json_t *maxChannelList = json_array();
    for(int k = 0;k < 3;k++) {
      json_array_append_new(maxChannelList, json_integer(maxChannels[k]));
    }
    json_object_set_new(data, "maxChannels", maxChannelList);
    json_t *snapList = json_array();
    for(int k = 0;k < 3;k++) {
      json_array_append_new(snapList, json_integer(snaps[k]));
    }
    json_object_set_new(data, "snaps", snapList);

    json_t *knobValueList = json_array();
    for(int k = 0;k < MAX_KNOBS;k++) {
      json_array_append_new(knobValueList, json_real(knobValues[k]));
    }
    json_object_set_new(data, "knobValues", knobValueList);
    return data;
  }

  void fromJson(json_t *data) {
    json_t *valueList = json_object_get(data, "values");
    for(int k = 0;k < 48;k++) {
      json_t *jValue = json_array_get(valueList, k);
      faderValues[k] = json_real_value(jValue);
    }
    json_t *minList = json_object_get(data, "min");
    for(int k = 0;k < 3;k++) {
      json_t *jValue = json_array_get(minList, k);
      min[k] = json_real_value(jValue);
    }
    json_t *maxList = json_object_get(data, "max");
    for(int k = 0;k < 3;k++) {
      json_t *jValue = json_array_get(maxList, k);
      max[k] = json_real_value(jValue);
    }
    json_t *maxChannelList = json_object_get(data, "maxChannels");
    for(int k = 0;k < 3;k++) {
      json_t *jValue = json_array_get(maxChannelList, k);
      maxChannels[k] = json_integer_value(jValue);
    }
    json_t *snapList = json_object_get(data, "snaps");
    for(int k = 0;k < 3;k++) {
      json_t *jValue = json_array_get(snapList, k);
      snaps[k] = json_integer_value(jValue);
    }
    json_t *knobValueList = json_object_get(data, "knobValues");
    for(int k = 0;k < MAX_KNOBS;k++) {
      json_t *jValue = json_array_get(knobValueList, k);
      if(jValue)
        knobValues[k] = json_real_value(jValue);
    }
    json_t *jKnob1 = json_object_get(data, "knob1");
    if(jKnob1) {
      knobValues[0] = json_real_value(jKnob1);
    }
    json_t *jKnob2 = json_object_get(data, "knob2");
    if(jKnob2) {
      knobValues[1] = json_real_value(jKnob2);
    }
  }
};

struct Faders : Module {
  enum ParamId {
    FADERS_A, FADERS_B = 16, FADERS_C = 32, MOD_CV_A = 48, MOD_CV_B, MOD_CV_C, PAT_PARAM, EDIT_PARAM, COPY_PARAM,
    PASTE_PARAM, GLIDE_PARAM, KNOB_PARAMS, INSERT_PARAM = KNOB_PARAMS + MAX_KNOBS, DEL_PARAM, PARAMS_LEN
  };

  enum InputId {
    MOD_A_INPUT, MOD_B_INPUT, MOD_C_INPUT, PAT_INPUT, INPUTS_LEN
  };

  enum OutputId {
    OUT_A_OUTPUT, MOD_B_OUTPUT, MOD_C_OUTPUT, KNOB_OUTPUTS, OUTPUTS_LEN = KNOB_OUTPUTS + MAX_KNOBS
  };

  enum LightId {
    LIGHTS_LEN
  };

  FadersPreset presets[MAX_PATS] = {};
  FadersPreset clipBoard = {};
  int dirty = 0;
  float currentVoltages[3][16] = {};
  std::string rowLabels[3] = {"A", "B", "C"};
  RND rnd;
  float snaps[8] = {0, 1.f / 12.f, 0.1, 10.f / 32.f, 0.5, 0.625, 1, 10.f / 8.f};
  dsp::ClockDivider divider;
  Module *padModule = nullptr;
  SlewLimiter faderSlews[48] = {};
  SlewLimiter knobSlews[MAX_KNOBS] = {};
  bool enabled[16]={}; // dummy used in FadersOne
  bool use_cv_pad_input_for_prog=false; // dummy
  Faders() {
    config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
    divider.setDivision(32);

    for(int i = 0;i < 3;i++) {
      for(int k = 0;k < 16;k++) {
        configParam(i * 16 + k, -10, 10, 0, rowLabels[i] + " chn " + std::to_string(k + 1));
      }
      configParam(MOD_CV_A + i, 0, 1, 0, "Mod CV Amount " + rowLabels[i]);
      configOutput(i, "CV " + rowLabels[i]);
      configInput(i, "Mod CV " + rowLabels[i]);
    }
    configButton(COPY_PARAM, "Copy Pattern");
    configButton(PASTE_PARAM, "Paste Pattern");
    configButton(EDIT_PARAM, "LOCK");
    configParam(PAT_PARAM, 0,MAX_PATS - 1, 0, "Preset Selection");
    configInput(PAT_INPUT, "Preset Select (0.1V per step)");
    configParam(GLIDE_PARAM, 0.f, 10.f, 0, "Glide");
    for(int k = 0;k < MAX_KNOBS;k++) {
      configParam(KNOB_PARAMS + k, -10.f, 10, 0, "Knob " + std::to_string(k + 1));
      configOutput(KNOB_OUTPUTS + k, "Knob " + std::to_string(k + 1));
    }
  }
  int getAdr() { return 0;} //dummy,not used
  bool progEnabled(int k) { return false;}
  void setValue(int row, int nr, float value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].setValue(row, nr, value);
  }

  void copy() {
    int pat = params[PAT_PARAM].getValue();
    clipBoard.set(presets[pat]);
  }

  void paste() {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].set(clipBoard);
    setCurrentPattern();
  }

  void insert() {
    int pat = params[PAT_PARAM].getValue();
    for(int k = MAX_PATS - 1;k > pat;k--) {
      presets[k] = presets[k - 1];
    }
    presets[pat] = {};
    setCurrentPattern();
  }

  void del() {
    int pat = params[PAT_PARAM].getValue();
    for(int k = pat;k < MAX_PATS - 1;k++) {
      presets[k] = presets[k + 1];
    }
    setCurrentPattern();
  }

  void setCurrentPattern() {
    int pat = params[PAT_PARAM].getValue();
    for(int nr = 0;nr < 3;nr++) {
      for(int k = 0;k < 16;k++) {
        configParam(nr * 16 + k, presets[pat].min[nr], presets[pat].max[nr], 0,
                    rowLabels[nr] + " chn " + std::to_string(k + 1));
      }
    }
    for(int k = 0;k < 48;k++) {
      setImmediateValue(getParamQuantity(k), presets[pat].faderValues[k]);
    }
    for(int k = 0;k < MAX_KNOBS;k++) {
      setImmediateValue(getParamQuantity(KNOB_PARAMS + k), presets[pat].knobValues[k]);
    }
  }

  float getSnapped(float value, int row, int c) {
    int pat = params[PAT_PARAM].getValue();
    if(presets[pat].snaps[row] > 0) {
      float snapInv = 1.f / snaps[presets[pat].snaps[row]];
      return roundf(value * snapInv) / snapInv;
    }
    return value;
  }

  void copyFromPad2() {
    int pat = params[PAT_PARAM].getValue();
    for(int k = 0;k < 3;k++) {
      presets[pat].min[k] = 0;
      presets[pat].max[k] = 10;
      presets[pat].maxChannels[k] = 16;
      presets[pat].snaps[k] = 0;
    }
    for(int k = 0;k < 48;k++) {
      presets[pat].faderValues[k] = padModule->params[k + 8].getValue() * 10.f;
    }
    presets[pat].knobValues[0] = rescale(padModule->params[0].getValue(), 0.5f, 60.f, 0.f, 10.f);
    presets[pat].knobValues[1] = rescale(padModule->params[2].getValue(), 0.5f, 4.f, 0.f, 10.f);
    setCurrentPattern();
  }

  float getMin(int nr) {
    int pat = params[PAT_PARAM].getValue();
    return presets[pat].min[nr];
  }

  float getMax(int nr) {
    int pat = params[PAT_PARAM].getValue();
    return presets[pat].max[nr];
  }

  int getMaxChannels(int nr) {
    int pat = params[PAT_PARAM].getValue();
    return presets[pat].maxChannels[nr];
  }

  int getSnap(int nr) {
    int pat = params[PAT_PARAM].getValue();
    return (int)presets[pat].snaps[nr];
  }
  int getProgChannels() {
    return 0;
  }
  void setMin(int nr, float value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].min[nr] = value;
  }

  void setMax(int nr, float value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].max[nr] = value;
  }

  void setMaxChannels(int nr, int value) {
    int pat = params[PAT_PARAM].getValue();
    INFO("set maxchannels %d %d %d", pat, nr, value);
    presets[pat].maxChannels[nr] = value;
  }

  void setSnap(int nr, float value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].snaps[nr] = value;
  }

  void setKnob(int nr) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].knobValues[nr] = params[KNOB_PARAMS + nr].getValue();
  }

  float getValue(int pat, int nr) {
    return faderSlews[nr].process();
  }

  float getKnobValue(int pat, int nr) {
    return knobSlews[nr].process();
  }

  void process(const ProcessArgs &args) override {
    if(leftExpander.module) {
      if(leftExpander.module->model == modelPad2) {
        padModule = leftExpander.module;
      } else {
        padModule = nullptr;
      }
    }

    if(divider.process()) {
      _process(args);
    }
  }

  void _process(const ProcessArgs &args) {
    if(inputs[PAT_INPUT].isConnected() && params[EDIT_PARAM].getValue() == 0) {
      int c = clamp(inputs[PAT_INPUT].getVoltage(), 0.f, 9.99f) * float(MAX_PATS) / 10.f;
      setImmediateValue(getParamQuantity(PAT_PARAM), c);
    }
    int pat = params[PAT_PARAM].getValue();
    float glide = params[GLIDE_PARAM].getValue();
    for(int k = 0;k < 48;k++) {
      faderSlews[k].setParams(args.sampleTime, glide / 32.f, presets[pat].faderValues[k]);
    }
    for(int k = 0;k < MAX_KNOBS;k++) {
      knobSlews[k].setParams(args.sampleTime, glide / 32.f, presets[pat].knobValues[k]);
    }
    for(int i = 0;i < 3;i++) {
      if(outputs[i].isConnected()) {
        for(int c = 0;c < presets[pat].maxChannels[i];c++) {
          currentVoltages[i][c] = clamp(
            getValue(pat, i * 16 + c) + inputs[i].getVoltage(c) * params[MOD_CV_A + i].getValue(), presets[pat].min[i],
            presets[pat].max[i]);
          currentVoltages[i][c] = getSnapped(currentVoltages[i][c], i, c);
          outputs[i].setVoltage(currentVoltages[i][c], c);
        }
        outputs[i].setChannels(presets[pat].maxChannels[i]);
      }
    }
    for(int k = 0;k < MAX_KNOBS;k++) {
      outputs[KNOB_OUTPUTS + k].setVoltage(knobSlews[k].process());
    }
  }

  float getCurrentVoltagePct(int row, int c) {
    int pat = params[PAT_PARAM].getValue();
    return math::rescale(currentVoltages[row][c], presets[pat].min[row], presets[pat].max[row], 0.f, 1.f);
  }

  void reconfig(int nr) {
    int pat = params[PAT_PARAM].getValue();
    for(int k = 0;k < 16;k++) {
      float value = getParamQuantity(nr * 16 + k)->getValue();
      if(value > presets[pat].max[nr])
        value = presets[pat].max[nr];
      if(value < presets[pat].min[nr])
        value = presets[pat].min[nr];
      configParam(nr * 16 + k, presets[pat].min[nr], presets[pat].max[nr], 0,
                  rowLabels[nr] + " chn " + std::to_string(k + 1));
      setImmediateValue(getParamQuantity(nr * 16 + k), value);
    }
  }

  json_t *dataToJson() override {
    json_t *data = json_object();
    json_t *presetList = json_array();
    for(int k = 0;k < MAX_PATS;k++) {
      json_array_append_new(presetList, presets[k].toJson());
    }
    json_object_set_new(data, "presets", presetList);
    return data;
  }

  void dataFromJson(json_t *root) override {
    json_t *data = json_object_get(root, "presets");
    if(data) {
      for(int k = 0;k < MAX_PATS;k++) {
        json_t *jPreset = json_array_get(data, k);
        presets[k].fromJson(jPreset);
      }
      setCurrentPattern();
    }
  }

  void randomizeValues(int nr) {
    int pat = params[PAT_PARAM].getValue();
    for(int k = 0;k < 16;k++) {
      int id = nr * 16 + k;
      setImmediateValue(getParamQuantity(id),
                        math::rescale(float(rnd.nextDouble()), 0.f, 1.f, presets[pat].min[nr], presets[pat].max[nr]));
    }
  }

  void onRandomize(const RandomizeEvent &e) override {
    for(int k = 0;k < 3;k++) {
      randomizeValues(k);
      setImmediateValue(getParamQuantity(KNOB_PARAMS + k),
                        math::rescale(float(rnd.nextDouble()), 0.f, 1.f, -10.f, 10.f));
    }
  }

  void onAdd(const AddEvent &e) override {
  }

  void onReset(const ResetEvent &e) override {
    for(int k = 0;k < MAX_PATS;k++) {
      presets[k].reset();
    }
    setCurrentPattern();
  }
};


struct FadersOnePreset {
  float faderValues[16] = {};

  int maxChannels = 16;
  float min = -10;
  float max = 10;
  unsigned int snap = 0;

  void reset() {
    for(int k = 0;k < 16;k++) {
      faderValues[k] = 0.f;
    }
    for(int k = 0;k < 3;k++) {
      maxChannels = 16;
      min = -10;
      max = 10;
      snap = 0;
    }
  }

  void setValue(int nr, float value) {
    faderValues[nr] = value;
  }

  void set(const FadersOnePreset &other) {
    for(int k = 0;k < 16;k++) {
      faderValues[k] = other.faderValues[k];
    }
    min = other.min;
    max = other.max;
    maxChannels = other.maxChannels;
  }

  std::string getValuesAsString() {
    std::string s;
    for(int j = 0;j < 16;j++) {
      if(j > 0) s.append(",");
      s.append(std::to_string(rescale(faderValues[j], min, max, 0.f, 1.f)));
    }
    return s;
  }

  json_t *toJson() {
    json_t *data = json_object();
    json_t *valueList = json_array();
    for(int k = 0;k < 16;k++) {
      json_array_append_new(valueList, json_real(faderValues[k]));
    }
    json_object_set_new(data, "values", valueList);
    json_object_set_new(data, "min", json_real(min));
    json_object_set_new(data, "max", json_real(max));
    json_object_set_new(data, "maxChannels", json_integer(maxChannels));
    json_object_set_new(data, "snaps", json_integer(snap));

    return data;
  }

  void fromJson(json_t *data) {
    json_t *valueList = json_object_get(data, "values");
    for(int k = 0;k < 16;k++) {
      json_t *jValue = json_array_get(valueList, k);
      faderValues[k] = json_real_value(jValue);
    }
    json_t *minJ = json_object_get(data, "min");
    if(minJ) {
      min = json_real_value(minJ);
    }
    json_t *maxJ = json_object_get(data, "max");
    if(maxJ) {
      max = json_real_value(maxJ);
    }
    json_t *maxChannelsJ = json_object_get(data, "maxChannels");
    if(maxChannelsJ) {
      maxChannels = json_integer_value(maxChannelsJ);
    }

    json_t *snapJ = json_object_get(data, "snap");
    if(snapJ) {
      snap = json_integer_value(snapJ);
    }
  }
};

struct FadersOne : Module {
  enum ParamId {
    FADERS, MOD_CV = 16, PAT_PARAM, EDIT_PARAM, COPY_PARAM, PASTE_PARAM, GLIDE_PARAM, INSERT_PARAM, DEL_PARAM,
    ENABLE_PARAM, CHAN_PARAM, PARAMS_LEN
  };

  enum InputId {
    MOD_INPUT, PAT_INPUT, PROG_INPUT, ENABLE_INPUT, CV_PAT_INPUT, INPUTS_LEN
  };

  enum OutputId {
    CV_OUTPUT, CV_PAT_OUTPUT, OUTPUTS_LEN
  };

  enum LightId {
    LIGHTS_LEN
  };

  FadersOnePreset presets[MAX_PATS] = {};
  FadersOnePreset clipBoard = {};
  int dirty = 0;
  float currentVoltages[16] = {};

  RND rnd;
  float snaps[8] = {0, 1.f / 12.f, 0.1, 10.f / 32.f, 0.5, 0.625, 1, 10.f / 8.f};
  dsp::ClockDivider divider;
  Module *padModule = nullptr;
  SlewLimiter faderSlews[16] = {};
  bool enabled[16]={};
  bool use_cv_pad_input_for_prog=false;

  FadersOne() {
    config(PARAMS_LEN, INPUTS_LEN, OUTPUTS_LEN, LIGHTS_LEN);
    divider.setDivision(32);

    for(int k = 0;k < 16;k++) {
      configParam(k, -10, 10, 0, " chn " + std::to_string(k + 1));
    }
    configParam(MOD_CV, 0, 1, 0, "Mod CV Amount");
    configOutput(CV_OUTPUT, "CV ");
    configInput(MOD_INPUT, "Mod CV ");
    configOutput(CV_PAT_OUTPUT, "Pattern CV");
    configInput(CV_PAT_INPUT, "Pattern Adr ");
    configButton(ENABLE_PARAM, "Enable");
    configButton(CHAN_PARAM, "Channels from Prg");
    configButton(COPY_PARAM, "Copy Pattern");
    configButton(PASTE_PARAM, "Paste Pattern");
    configButton(EDIT_PARAM, "LOCK");
    configParam(PAT_PARAM, 0,MAX_PATS - 1, 0, "Preset Selection");
    configInput(PAT_INPUT, "Preset Select (0.1V per step)");
    configInput(ENABLE_INPUT, "Enable");
    configInput(PROG_INPUT, "Program");
    configParam(GLIDE_PARAM, 0.f, 10.f, 0, "Glide");
  }

  void setValue(int dummy, int nr, float value) {
    int pat = params[PAT_PARAM].getValue();
    //INFO("pat %d set value %d = %f",pat, nr,value);
    presets[pat].setValue(nr, value);
  }

  void copy() {
    int pat = params[PAT_PARAM].getValue();
    clipBoard.set(presets[pat]);
  }

  void paste() {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].set(clipBoard);
    setCurrentPattern();
  }

  void insert() {
    int pat = params[PAT_PARAM].getValue();
    for(int k = MAX_PATS - 1;k > pat;k--) {
      presets[k] = presets[k - 1];
    }
    presets[pat] = {};
    setCurrentPattern();
  }

  void del() {
    int pat = params[PAT_PARAM].getValue();
    for(int k = pat;k < MAX_PATS - 1;k++) {
      presets[k] = presets[k + 1];
    }
    setCurrentPattern();
  }

  void setCurrentPattern() {
    int pat = params[PAT_PARAM].getValue();
    //INFO("set current pattern %d",pat);
    for(int k = 0;k < 16;k++) {
      configParam(k, presets[pat].min, presets[pat].max, 0, " chn " + std::to_string(k + 1));
    }

    for(int k = 0;k < 16;k++) {
      setImmediateValue(getParamQuantity(k), presets[pat].faderValues[k]);
    }
    ///INFO("end set current pattern %d",pat);
  }

  float getSnapped(float value, int c) {
    int pat = (int)params[PAT_PARAM].getValue();
    return getSnapped(value,snaps[presets[pat].snap],c);
  }

  static float getSnapped(float value, float snap, int c) {
    if(snap > 0) {
      float snapInv = 1.f / (float)snap;
      return roundf(value * snapInv) / snapInv;
    }
    return value;
  }

  float getMin(int dummy) {
    int pat = params[PAT_PARAM].getValue();
    return presets[pat].min;
  }

  float getMax(int dummy) {
    int pat = params[PAT_PARAM].getValue();
    return presets[pat].max;
  }

  int getMaxChannels(int dummy) {
    int pat = params[PAT_PARAM].getValue();
    return presets[pat].maxChannels;
  }

  int getSnap(int dummy) {
    int pat = params[PAT_PARAM].getValue();
    return presets[pat].snap;
  }

  int getProgChannels() {
    return inputs[PROG_INPUT].getChannels();
  }

  void setMin(int dummy, float value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].min = value;
  }

  void setMax(int dummy, float value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].max = value;
  }

  void setMaxChannels(int dummy, int value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].maxChannels = value;
  }

  void setSnap(int dummy, float value) {
    int pat = params[PAT_PARAM].getValue();
    presets[pat].snap = value;
  }

  float getValue(int pat, int nr) {
    return faderSlews[nr].process();
  }

  int getAdr() {
    return (int)(clamp(inputs[CV_PAT_INPUT].getVoltage(),0.f,9.99f)*float(MAX_PATS) / 10.f);
  }

  bool progEnabled(int k) {
    return inputs[PROG_INPUT].isConnected() && enabled[k] && getAdr()==(int)params[PAT_PARAM].getValue();
  }

  void process(const ProcessArgs &args) override {
    if(leftExpander.module) {
      if(leftExpander.module->model == modelPad2) {
        padModule = leftExpander.module;
      } else {
        padModule = nullptr;
      }
    }

    if(divider.process()) {
      _process(args);
    }
  }

  void _process(const ProcessArgs &args) {
    if(inputs[PAT_INPUT].isConnected() && params[EDIT_PARAM].getValue() == 0) {
      int c = clamp(inputs[PAT_INPUT].getVoltage(), 0.f, 9.99f) * float(MAX_PATS) / 10.f;
      setImmediateValue(getParamQuantity(PAT_PARAM), c);
    }
    if(inputs[ENABLE_INPUT].isConnected()) {
      int channels=inputs[ENABLE_INPUT].getChannels();
      if(channels==1) {
        bool on=inputs[ENABLE_INPUT].getVoltage()>1.f;
        getParamQuantity(ENABLE_PARAM)->setImmediateValue(on?1.f:0.f);
        for(int k=0;k<16;k++) enabled[k]=on;
      } else if(channels>1) {
        auto v= inputs[ENABLE_INPUT].getVoltages(0);
        bool on=false;
        for(int k=0;k<16;k++) {
          if(v[k]>1.f) {
            on=true;
            enabled[k]=true;
          } else {
            enabled[k]=false;
          }
        }
        getParamQuantity(ENABLE_PARAM)->setImmediateValue(on?1.f:0.f);
      }
    } else {
      for(int k=0;k<16;k++) enabled[k]=params[ENABLE_PARAM].getValue()>0.f;
    }
    int pat = params[PAT_PARAM].getValue();
    if(inputs[PROG_INPUT].isConnected()) {
      int cv_pat=(int)(clamp(inputs[CV_PAT_INPUT].getVoltage(),0.f,9.99f)*float(MAX_PATS) / 10.f);
      int prog_adr=use_cv_pad_input_for_prog?cv_pat:pat;
      int channels=inputs[PROG_INPUT].getChannels();
      for(int k=0;k<channels;k++) {
        if(enabled[k]) {
          float v= inputs[PROG_INPUT].getVoltage(k);
          if(!use_cv_pad_input_for_prog) getParamQuantity(k)->setImmediateValue(v);
          presets[prog_adr].faderValues[k] = v; // ensure saving in preset
        }
      }
      if(params[CHAN_PARAM].getValue()>0.f) {
        presets[pat].maxChannels=channels;
      }
    }

    float glide = params[GLIDE_PARAM].getValue();
    for(int k = 0;k < 16;k++) {
      faderSlews[k].setParams(args.sampleTime, glide / 32.f, presets[pat].faderValues[k]);
    }

    if(outputs[CV_PAT_OUTPUT].isConnected()) {
      int cv_pat=(int)(clamp(inputs[CV_PAT_INPUT].getVoltage(),0.f,9.99f)*float(MAX_PATS) / 10.f);
      for(int c = 0;c < presets[cv_pat].maxChannels;c++) {
        float v = clamp(presets[cv_pat].faderValues[c] + inputs[MOD_INPUT].getVoltage(c) * params[MOD_CV].getValue(),
                                   presets[cv_pat].min, presets[cv_pat].max);
        v = getSnapped(v,snaps[presets[pat].snap], c);
        outputs[CV_PAT_OUTPUT].setVoltage(v, c);
      }
      outputs[CV_PAT_OUTPUT].setChannels(presets[cv_pat].maxChannels);
    }

    if(outputs[CV_OUTPUT].isConnected()) {
      for(int c = 0;c < presets[pat].maxChannels;c++) {
        currentVoltages[c] = clamp(getValue(pat, c) + inputs[MOD_INPUT].getVoltage(c) * params[MOD_CV].getValue(),
                                   presets[pat].min, presets[pat].max);
        currentVoltages[c] = getSnapped(currentVoltages[c], c);
        outputs[CV_OUTPUT].setVoltage(currentVoltages[c], c);
      }
      outputs[CV_OUTPUT].setChannels(presets[pat].maxChannels);
    }
  }

  float getCurrentVoltagePct(int dummy, int c) {
    int pat = params[PAT_PARAM].getValue();
    return math::rescale(currentVoltages[c], presets[pat].min, presets[pat].max, 0.f, 1.f);
  }

  void reconfig(int dummy) {
    int pat = params[PAT_PARAM].getValue();
    for(int k = 0;k < 16;k++) {
      float value = getParamQuantity(k)->getValue();
      if(value > presets[pat].max)
        value = presets[pat].max;
      if(value < presets[pat].min)
        value = presets[pat].min;
      configParam(k, presets[pat].min, presets[pat].max, 0, " chn " + std::to_string(k + 1));
      setImmediateValue(getParamQuantity(k), value);
    }
  }

  json_t *dataToJson() override {
    json_t *data = json_object();
    json_object_set_new(data, "use_cv_pad_input_for_prog", json_boolean(use_cv_pad_input_for_prog));
    json_t *presetList = json_array();
    for(int k = 0;k < MAX_PATS;k++) {
      json_array_append_new(presetList, presets[k].toJson());
    }
    json_object_set_new(data, "presets", presetList);
    return data;
  }

  void dataFromJson(json_t *root) override {
    json_t *j=json_object_get(root,"use_cv_pad_input_for_prog");
    if(j) {
      use_cv_pad_input_for_prog = json_boolean_value(j);
    }
    json_t *data = json_object_get(root, "presets");
    if(data) {
      for(int k = 0;k < MAX_PATS;k++) {
        json_t *jPreset = json_array_get(data, k);
        presets[k].fromJson(jPreset);
      }
      setCurrentPattern();
    } else {
      INFO("no data in faders one");
    }
  }

  void randomizeValues() {
    int pat = params[PAT_PARAM].getValue();
    for(int k = 0;k < 16;k++) {
      setImmediateValue(getParamQuantity(k),
                        math::rescale(float(rnd.nextDouble()), 0.f, 1.f, presets[pat].min, presets[pat].max));
    }
  }

  void onRandomize(const RandomizeEvent &e) override {
    randomizeValues();
  }

  void onAdd(const AddEvent &e) override {
  }

  void onReset(const ResetEvent &e) override {
    for(int k = 0;k < MAX_PATS;k++) {
      presets[k].reset();
    }
    setCurrentPattern();
  }

  void setKnob(float f) {
  }
};

template<typename M>
struct Fader : SliderKnob {
  M *module = nullptr;
  float pos = 0;
  int nr=0;
  Fader() : SliderKnob() {
    box.size = mm2px(Vec(4.5f, 33.f));
    forceLinear = true;
    speed = 2.0;
  }

  void step() override {
    if(module && module->use_cv_pad_input_for_prog && module->progEnabled(nr)) {
      int pat=(int)module->params[M::PAT_PARAM].getValue();
      if(module->getAdr()==pat)
        getParamQuantity()->setImmediateValue(module->presets[pat].faderValues[nr]);
    }
    SliderKnob::step();
  }

  void drawLayer(const DrawArgs &args, int layer) override {
    if(layer == 1) {
      _draw(args);
    }
    Widget::drawLayer(args, layer);
  }

  void _draw(const DrawArgs &args) {

    int c = 0;
    int id = -1;
    int row = 0;
    ParamQuantity *pq = getParamQuantity();
    if(pq) {
      id = pq->paramId;
      c = id % 16;
      row = id / 16;
    }
    if(module) {
      int pat=(int)module->params[M::PAT_PARAM].getValue();
      nvgBeginPath(args.vg);
      nvgRect(args.vg, 0, 0, box.size.x, box.size.y);
      if(id >= 0 &&  c < module->getMaxChannels(row)) {
        if(module->enabled[c] && c < module->getProgChannels() && (!module->use_cv_pad_input_for_prog || module->getAdr()==pat))
          nvgFillColor(args.vg, nvgRGB(0x33, 0x55, 0x88));
        else
          nvgFillColor(args.vg, nvgRGB(0x33, 0x33, 0x33));
      } else {
        nvgFillColor(args.vg, nvgRGB(0x55, 0x55, 0x55));

      }
    }
    nvgStrokeColor(args.vg, nvgRGB(0x88, 0x88, 0x88));
    nvgFill(args.vg);
    nvgStroke(args.vg);

    if(pq) {
      pos = math::rescale(pq->getSmoothValue(), pq->getMinValue(), pq->getMaxValue(), 0.f, 1.f);
    }
    if(id >= 0 && module && c < module->getMaxChannels(row)) {
      nvgFillColor(args.vg, nvgRGB(0x81, 0x7c, 0xac));
    } else {
      nvgFillColor(args.vg, nvgRGB(0x77, 0x77, 0x77));
    }
    nvgBeginPath(args.vg);

    if(pq != nullptr && pq->getMinValue() < 0) {
      if(pos >= 0.5) {
        nvgRect(args.vg, 1, box.size.y / 2 - (box.size.y) * (pos - 0.5), box.size.x - 2, (box.size.y) * (pos - 0.5));
      } else {
        nvgRect(args.vg, 1, box.size.y / 2, box.size.x - 2, -(box.size.y) * (pos - 0.5));
      }
    } else {
      nvgRect(args.vg, 1, box.size.y - box.size.y * pos, box.size.x - 2, box.size.y * pos);
    }
    nvgFill(args.vg);
    if(pq && module) {
      float vpos = module->getCurrentVoltagePct(row, c);
      nvgBeginPath(args.vg);
      if(id >= 0 && module && c < module->getMaxChannels(row)) {
        nvgFillColor(args.vg, nvgRGB(0x0, 0xee, 0x88));
      } else {
        nvgFillColor(args.vg, nvgRGB(0x99, 0x99, 0x99));
      }

      nvgRect(args.vg, 1, box.size.y - box.size.y * vpos - 1, box.size.x - 2, 2);
      nvgFill(args.vg);
    }
  }

  void onButton(const event::Button &e) override {
    if(e.button == GLFW_MOUSE_BUTTON_LEFT && e.action == GLFW_PRESS && (e.mods & RACK_MOD_MASK) == 0) {
      float pct = (box.size.y - e.pos.y) / box.size.y;
      ParamQuantity *pq = getParamQuantity();
      if(pq) {
        float newValue = math::rescale(pct, 0.f, 1.f, pq->getMinValue(), pq->getMaxValue());
        pq->setValue(newValue);
      }
      e.consume(this);
    }
    SliderKnob::onButton(e);
  }

  void onChange(const ChangeEvent &e) override {
    if(module) {
      ParamQuantity *pq = getParamQuantity();
      if(pq) {
        int id = pq->paramId;
        int c = id % 16;
        int row = id / 16;
        module->setValue(row, c, pq->getValue());
      }
    }
    ParamWidget::onChange(e);
  }
};

struct FaderRange {
  float min;
  float max;
};

template<typename M>
struct FaderRangeSelectItem : MenuItem {
  M *module = nullptr;
  std::vector<FaderRange> ranges;
  int nr;

  FaderRangeSelectItem(M *_module, std::vector<FaderRange> _ranges, int _nr) : module(_module),
                                                                               ranges(std::move(_ranges)), nr(_nr) {
  }

  Menu *createChildMenu() override {
    Menu *menu = new Menu;
    for(unsigned int k = 0;k < ranges.size();k++) {
      menu->addChild(createCheckMenuItem(string::f("%d/%dV", (int)ranges[k].min, (int)ranges[k].max), "", [=]() {
                                           return module->getMin(nr) == ranges[k].min && module->getMax(nr) == ranges[k]
                                                  .max;
                                         }, [=]() {
                                           module->setMin(nr, ranges[k].min);
                                           module->setMax(nr, ranges[k].max);
                                           module->reconfig(nr);
                                         }));
    }
    return menu;
  }
};

template<typename M>
struct SnapSelectItem : MenuItem {
  M *module = nullptr;
  std::vector<std::string> labels;
  int nr;

  SnapSelectItem(M *_module, std::vector<std::string> _labels, int _nr) : module(_module), labels(std::move(_labels)),
                                                                          nr(_nr) {
  }

  Menu *createChildMenu() override {
    Menu *menu = new Menu;
    for(unsigned int k = 0;k < labels.size();k++) {
      menu->addChild(createCheckMenuItem(labels[k], "", [=]() {
                                           return module->getSnap(nr) == k;
                                         }, [=]() {
                                           module->setSnap(nr, k);
                                         }));
    }
    return menu;
  }
};

template<typename M>
struct ChannelSelectItem : MenuItem {
  M *module = nullptr;
  int nr;
  int min;
  int max;

  ChannelSelectItem(M *m, int n, int _min, int _max) : module(m), nr(n), min(_min), max(_max) {
  }

  Menu *createChildMenu() override {
    Menu *menu = new Menu;
    for(int c = min;c <= max;c++) {
      menu->addChild(createCheckMenuItem(string::f("%d", c), "", [=]() {
                                           return module->getMaxChannels(nr) == c;
                                         }, [=]() {
                                           module->setMaxChannels(nr, c);
                                         }));
    }
    return menu;
  }
};

template<typename M>
struct FadersPresetSelect : SpinParamWidget {
  M *module = nullptr;

  FadersPresetSelect() {
    init();
  }

  void onChange(const ChangeEvent &e) override {
    if(module) {
      INFO("on Change");
      module->setCurrentPattern();
    }
  }
};

template<typename M>
struct SingleKnob : TrimbotWhite {
  M *module = nullptr;
  int nr = 0;

  void onChange(const ChangeEvent &e) override {
    if(module) {
      module->setKnob(nr);
    }
    TrimbotWhite::onChange(e);
  }
};

template<typename M>
struct MInsertButton : SmallButtonWithLabel {
  M *module = nullptr;

  MInsertButton() : SmallButtonWithLabel() {
    momentary = true;
  }

  void onChange(const ChangeEvent &e) override {
    SvgSwitch::onChange(e);
    if(module) {
      if(module->params[Faders::INSERT_PARAM].getValue() > 0)
        module->insert();
    }
  }
};

template<typename M>
struct MDelButton : SmallButtonWithLabel {
  M *module = nullptr;

  MDelButton() : SmallButtonWithLabel() {
    momentary = true;
  }

  void onChange(const ChangeEvent &e) override {
    SvgSwitch::onChange(e);
    if(module) {
      if(module->params[Faders::DEL_PARAM].getValue() > 0)
        module->del();
    }
  }
};

template<typename M>
struct FMCopyButton : SmallButtonWithLabel {
  M *module = nullptr;

  FMCopyButton() : SmallButtonWithLabel() {
    momentary = true;
  }

  void onChange(const ChangeEvent &e) override {
    SvgSwitch::onChange(e);
    if(module) {
      if(module->params[Faders::COPY_PARAM].getValue() > 0) {
        module->copy();
        std::string values = module->clipBoard.getValuesAsString();
        glfwSetClipboardString(APP->window->win, values.c_str());
      }
    }
  }
};

struct FadersWidget : ModuleWidget {
  FadersWidget(Faders *module) {
    setModule(module);
    setPanel(createPanel(asset::plugin(pluginInstance, "res/Faders.svg")));
    addChild(createWidget<ScrewSilver>(Vec(0, 0)));
    addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 0)));
    addChild(createWidget<ScrewSilver>(Vec(0, 365)));
    addChild(createWidget<ScrewSilver>(Vec(box.size.x - 15, 365)));

    for(int i = 0;i < 3;i++) {
      for(int k = 0;k < 16;k++) {
        auto faderParam = createParam<Fader<Faders> >(mm2px(Vec(3 + k * 4.5,MHEIGHT - (9 + 38 * (2 - i)) - 33)), module,
                                                      i * 16 + k);
        faderParam->module = module;
        addParam(faderParam);
      }
      addParam(createParam<TrimbotWhite>(mm2px(Vec(78,MHEIGHT - (23 + 38 * (2 - i) + 6.273f))), module,
                                         Faders::MOD_CV_A + i));
      addInput(createInput<SmallPort>(mm2px(Vec(78,MHEIGHT - (32.75f + 38 * (2 - i) + 6.273f))), module, i));
      addOutput(createOutput<SmallPort>(mm2px(Vec(78,MHEIGHT - (9 + 38 * (2 - i) + 6.273f))), module, i));
    }
    float x = 88;
    float y = 13.463;
    auto patParam = createParam<FadersPresetSelect<Faders> >(mm2px(Vec(x, y)), module, Faders::PAT_PARAM);
    patParam->module = module;
    addParam(patParam);
    y += 10;
    auto editButton = createParam<SmallButtonWithLabel>(mm2px(Vec(x, y)), module, Faders::EDIT_PARAM);
    editButton->setLabel("Lock");
    addParam(editButton);
    y += 4;
    addInput(createInput<SmallPort>(mm2px(Vec(x + 0.4, y)), module, Faders::PAT_INPUT));
    y += 8;
    auto copyButton = createParam<FMCopyButton<Faders> >(mm2px(Vec(x, y)), module, Faders::COPY_PARAM);
    copyButton->label = "Cpy";
    copyButton->module = module;
    addParam(copyButton);
    y += 4;
    auto pasteButton = createParam<MPasteButton<Faders> >(mm2px(Vec(x, y)), module, Faders::PASTE_PARAM);
    pasteButton->label = "Pst";
    pasteButton->module = module;
    addParam(pasteButton);
    y += 4;
    auto insertButton = createParam<MInsertButton<Faders> >(mm2px(Vec(x, y)), module, Faders::INSERT_PARAM);
    insertButton->label = "+";
    insertButton->module = module;
    addParam(insertButton);
    y += 4;
    auto delButton = createParam<MDelButton<Faders> >(mm2px(Vec(x, y)), module, Faders::DEL_PARAM);
    delButton->label = "-";
    delButton->module = module;
    addParam(delButton);
    y = 58;
    for(int k = 0;k < MAX_KNOBS;k++) {
      auto knob = createParam<SingleKnob<Faders> >(mm2px(Vec(x, y)), module, Faders::KNOB_PARAMS + k);
      knob->module = module;
      knob->nr = k;
      addParam(knob);
      y += 7;
      addOutput(createOutput<SmallPort>(mm2px(Vec(x, y)), module, Faders::KNOB_OUTPUTS + k));
      y += 11;
    }
    addParam(createParam<TrimbotWhite>(mm2px(Vec(x, 113.213f)), module, Faders::GLIDE_PARAM));
  }

  void appendContextMenu(Menu *menu) override {
    auto *module = dynamic_cast<Faders *>(this->module);
    assert(module);
    for(int i = 0;i < 3;i++) {
      menu->addChild(new MenuSeparator);
      auto channelSelect = new ChannelSelectItem<Faders>(module, i, 1, PORT_MAX_CHANNELS);
      channelSelect->text = "Polyphonic Channels";
      channelSelect->rightText = string::f("%d", module->getMaxChannels(i)) + "  " + RIGHT_ARROW;
      menu->addChild(channelSelect);
      std::vector<FaderRange> ranges = {
        {-10, 10},
        {-5, 5},
        {-3, 3},
        {-2, 2},
        {-1, 1},
        {0, 10},
        {0, 5},
        {0, 3},
        {0, 2},
        {0, 1}
      };
      auto rangeSelectItem = new FaderRangeSelectItem<Faders>(module, ranges, i);
      rangeSelectItem->text = "Range";
      rangeSelectItem->rightText = string::f("%d/%dV", (int)module->getMin(i), (int)module->getMax(i)) + "  " +
                                   RIGHT_ARROW;
      menu->addChild(rangeSelectItem);
      struct RandomizeRow : ui::MenuItem {
        Faders *module;
        int nr;

        RandomizeRow(Faders *m, int n) : module(m), nr(n) {
        }

        void onAction(const ActionEvent &e) override {
          if(!module)
            return;
          module->randomizeValues(nr);
        }
      };

      std::vector<std::string> labels = {"None", "Semi", "0.1", "Pat 32", "0.5", "Pat 16", "1", "Pat 8"};
      auto snapSelectItem = new SnapSelectItem<Faders>(module, labels, i);
      snapSelectItem->text = "Snap Size";
      snapSelectItem->rightText = labels[module->getSnap(i)] + "  " + RIGHT_ARROW;
      menu->addChild(snapSelectItem);

      auto rpMenu = new RandomizeRow(module, i);
      rpMenu->text = "Randomize";
      menu->addChild(rpMenu);
    }
  }

  void onHoverKey(const event::HoverKey &e) override {
    if(e.action == GLFW_PRESS) {
      if(e.keyName == "f") {
        auto *fadersModule = dynamic_cast<Faders *>(this->module);
        fadersModule->copyFromPad2();
      }
    }
  }
};

struct FadersOneWidget : ModuleWidget {
  FadersOneWidget(FadersOne *module) {
    setModule(module);
    setPanel(createPanel(asset::plugin(pluginInstance, "res/FadersOne.svg")));

    for(int k = 0;k < 16;k++) {
      auto faderParam = createParam<Fader<FadersOne> >(mm2px(Vec(4.f + k * 5.5f, 19)), module, k);
      faderParam->box.size = mm2px(Vec(5.5f, 74.f));
      faderParam->module = module;
      faderParam->nr=k;
      addParam(faderParam);
    }

    addInput(createInput<SmallPort>(mm2px(Vec(33, 104)), module, FadersOne::PROG_INPUT));
    addInput(createInput<SmallPort>(mm2px(Vec(45, 104)), module, FadersOne::ENABLE_INPUT));


    addParam(createParam<TrimbotWhite>(mm2px(Vec(60, 116)), module, FadersOne::MOD_CV));
    addInput(createInput<SmallPort>(mm2px(Vec(60, 104)), module, FadersOne::MOD_INPUT));

    addInput(createInput<SmallPort>(mm2px(Vec(72, 104)), module, FadersOne::CV_PAT_INPUT));
    addOutput(createOutput<SmallPort>(mm2px(Vec(72, 116)), module, FadersOne::CV_PAT_OUTPUT));


    addOutput(createOutput<SmallPort>(mm2px(Vec(84, 116)), module, FadersOne::CV_OUTPUT));
    addParam(createParam<MLED>(mm2px(Vec(45,116)), module, FadersOne::ENABLE_PARAM));
    addParam(createParam<MLED>(mm2px(Vec(33,116)), module, FadersOne::CHAN_PARAM));
    addParam(createParam<TrimbotWhite>(mm2px(Vec(84, 104.f)), module, FadersOne::GLIDE_PARAM));

    float x = 4;
    float y = 104;
    auto patParam = createParam<FadersPresetSelect<FadersOne> >(mm2px(Vec(x, y)), module, FadersOne::PAT_PARAM);
    patParam->module = module;
    addParam(patParam);
    y += 10;
    addInput(createInput<SmallPort>(mm2px(Vec(x, 116)), module, FadersOne::PAT_INPUT));
    y = 104;
    x=12;
    auto editButton = createParam<SmallButtonWithLabel>(mm2px(Vec(x, y)), module, FadersOne::EDIT_PARAM);
    editButton->setLabel("Lock");
    addParam(editButton);
    y += 4;
    auto copyButton = createParam<FMCopyButton<FadersOne> >(mm2px(Vec(x, y)), module, FadersOne::COPY_PARAM);
    copyButton->label = "Cpy";
    copyButton->module = module;
    addParam(copyButton);
    y += 4;
    auto pasteButton = createParam<MPasteButton<FadersOne> >(mm2px(Vec(x, y)), module, FadersOne::PASTE_PARAM);
    pasteButton->label = "Pst";
    pasteButton->module = module;
    addParam(pasteButton);
    y += 4;
    auto insertButton = createParam<MInsertButton<FadersOne> >(mm2px(Vec(x, y)), module, FadersOne::INSERT_PARAM);
    insertButton->label = "+";
    insertButton->module = module;
    addParam(insertButton);
    y += 4;
    auto delButton = createParam<MDelButton<FadersOne> >(mm2px(Vec(x, y)), module, FadersOne::DEL_PARAM);
    delButton->label = "-";
    delButton->module = module;
    addParam(delButton);


  }

  void appendContextMenu(Menu *menu) override {
    auto module = dynamic_cast<FadersOne *>(this->module);
    assert(module);
     menu->addChild(new MenuSeparator);
      auto channelSelect = new ChannelSelectItem<FadersOne>(module, 0, 1, PORT_MAX_CHANNELS);
      channelSelect->text = "Polyphonic Channels";
      channelSelect->rightText = string::f("%d", module->getMaxChannels(0)) + "  " + RIGHT_ARROW;
      menu->addChild(channelSelect);
      std::vector<FaderRange> ranges = {
        {-10, 10},
        {-5, 5},
        {-3, 3},
        {-2, 2},
        {-1, 1},
        {0, 10},
        {0, 5},
        {0, 3},
        {0, 2},
        {0, 1}
      };
      auto rangeSelectItem = new FaderRangeSelectItem<FadersOne>(module, ranges, 0);
      rangeSelectItem->text = "Range";
      rangeSelectItem->rightText = string::f("%d/%dV", (int)module->getMin(0), (int)module->getMax(0)) + "  " +
                                   RIGHT_ARROW;
      menu->addChild(rangeSelectItem);
      struct RandomizeRow : ui::MenuItem {
        FadersOne *module;
        int nr;

        RandomizeRow(FadersOne *m, int n) : module(m), nr(n) {
        }

        void onAction(const ActionEvent &e) override {
          if(!module)
            return;
          module->randomizeValues();
        }
      };

      std::vector<std::string> labels = {"None", "Semi", "0.1", "Pat 32", "0.5", "Pat 16", "1", "Pat 8"};
      auto snapSelectItem = new SnapSelectItem<FadersOne>(module, labels, 0);
      snapSelectItem->text = "Snap Size";
      snapSelectItem->rightText = labels[module->getSnap(0)] + "  " + RIGHT_ARROW;
      menu->addChild(snapSelectItem);

      auto rpMenu = new RandomizeRow(module, 0);
      rpMenu->text = "Randomize";
      menu->addChild(rpMenu);
      menu->addChild(createBoolPtrMenuItem("Use Adr for Prog","",&module->use_cv_pad_input_for_prog));

  }

  void onHoverKey(const event::HoverKey &e) override {
    if(e.action == GLFW_PRESS) {
      if(e.keyName == "f") {
        auto *fadersModule = dynamic_cast<Faders *>(this->module);
        fadersModule->copyFromPad2();
      }
    }
  }
};

Model *modelFaders = createModel<Faders, FadersWidget>("Faders");
Model *modelFadersOne = createModel<FadersOne, FadersOneWidget>("FadersOne");
