   DUINO_SaveWidget<ParameterValues> * widget_save_;
   DUINO_DisplayWidget * widgets_adsr_[8];
 
-  volatile bool gate, retrigger;
-  volatile uint8_t selected_env;
-  volatile unsigned long debounce;
-  DU_ADSR_Values adsr_values[2];
-  uint8_t env;
-  unsigned long gate_time, release_time;
-  float cv_current, cv_released;
-  uint8_t last_selected_env;
-  bool last_gate;
+  volatile bool gate_, retrigger_;
+  volatile uint8_t selected_env_;
+  volatile unsigned long debounce_;
+  DU_ADSR_Values adsr_values_[2];
+  uint8_t env_;
+  unsigned long gate_time_, release_time_;
+  float cv_current_, cv_released_;
+  uint8_t last_selected_env_;
+  bool last_gate_;
 };
 
 DU_ADSR_Function * function;


  virtual void function_loop()
  {
    if (retrigger)
    {
      env = selected_env;
      gate_time = 0;
      release_time = 0;
      retrigger = false;
    }

    if (gate_time)
    {
      if (gate)
      {
        uint16_t elapsed = millis() - gate_time;
        if (elapsed < adsr_values[env].A)
        {
          // attack
          cv_current = ENV_PEAK * ENV_SATURATION * (1.0 - exp(-(float(elapsed) / float(adsr_values[env].A))));
        }
        else
        {
          // decay/sustain
          cv_current = adsr_values[env].S + (ENV_PEAK - adsr_values[env].S)
              * exp(ENV_DECAY_COEFF * (float(elapsed - adsr_values[env].A) / float(adsr_values[env].D)));
        }
      }
      else
      {
        if (release_time)
        {
          uint16_t elapsed = millis() - release_time;
          if(elapsed < adsr_values[env].R * ENV_RELEASE_HOLD)
          {
            // release
            cv_current = exp(ENV_RELEASE_COEFF * (float(elapsed) / float(adsr_values[env].R))) * cv_released;
          }
          else
          {
            cv_current = 0.0;
            release_time = 0;
            gate_time = 0;
          }
        }
        else
        {
          release_time = millis();
          cv_released = cv_current;
        }
      }

      cv_out(CO1, cv_current);
      cv_out(CO3, cv_current / 2.0);
    }
    else if (gate)
    {
      gate_time = millis()
          - (unsigned long)(-float(adsr_values[env].A) * log(1 - (cv_current / (ENV_PEAK * ENV_SATURATION))));
    }

    widget_loop();

    // display selected envelope
    if (selected_env != last_selected_env)
    {
      last_selected_env = selected_env;
      Display.fill_rect(53, 55, 7, 9, DUINO_SH1106::Inverse);
      Display.fill_rect(68, 55, 7, 9, DUINO_SH1106::Inverse);
      Display.display(53, 74, 6, 7);
    }

    // display gate
    if (gate != last_gate)
    {
      last_gate = gate;
      if (gate)
      {
        Display.draw_bitmap_7(60, 25, icons, 0, DUINO_SH1106::White);
      }


  void gate_callback()
  {
    gate_ = gt_read_debounce(DUINO_Function::GT3);
    if (gate_)
    {
      retrigger_ = true;
    }
  }

  void switch_callback()
  {
    if (millis() - debounce_ > DEBOUNCE_MS)
    {
      selected_env_++;
      selected_env_ %= 2;
      debounce_ = millis();
    }
  }

