/*
   MicroDexed

   MicroDexed is a port of the Dexed sound engine
   (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6/4.x with audio shield.
   Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android

   (c)2018-2021 H. Wirtz <wirtz@parasitstudio.de>

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
*/

#ifdef ENABLE_LCD_UI
#ifndef _UI_HPP_
#define _UI_HPP_

#include <LCDMenuLib2.h>
#include <MD_REncoder.h>
#include "config.h"
#include "disp_plus.h"
#include "synth_dexed.h"
#include "effect_modulated_delay.h"
#include "effect_stereo_mono.h"
#ifdef USE_PLATEREVERB
#include "effect_platervbstereo.h"
#else
#include "effect_freeverbf.h"
#endif
#include "drumset.h"

#define _LCDML_DISP_cols  LCD_cols
#define _LCDML_DISP_rows  LCD_rows

#ifdef I2C_DISPLAY
#define _LCDML_DISP_cfg_cursor                     0x7E   // cursor Symbol
#else
#define _LCDML_DISP_cfg_cursor                     0x8d   // cursor Symbol
#endif
#define _LCDML_DISP_cfg_scrollbar                  1      // enable a scrollbar

extern PeriodicTimer timer1;
extern void sequencer(void);
extern bool check_sd_seq_exists(uint8_t);

extern config_t configuration;
extern void set_volume(uint8_t v, uint8_t m);
extern bool load_sysex(uint8_t b, uint8_t v);
extern void generate_version_string(char* buffer, uint8_t len);
extern void initial_values_from_eeprom(bool init);
extern void _softRestart(void);
extern void eeprom_update_sys(void);
extern void eeprom_update_performance(void);
extern void eeprom_update_fx(void);
extern void eeprom_update_dexed(uint8_t instance_id);
//extern float pseudo_log_curve(float value);
extern float midi_volume_transform(uint8_t midi_amp);
extern float volume_transform(float amp);
extern uint8_t selected_instance_id;
extern char receive_bank_filename[FILENAME_LEN];

#if NUM_DRUMS > 0
#include "drums.h"
extern void get_sd_seq_name_json(uint8_t number);
extern drum_config_t drum_config[NUM_DRUMSET_CONFIG];
extern char seq_name[FILENAME_LEN];
extern char seq_name_temp[FILENAME_LEN];
extern uint8_t seq_vel[NUM_SEQ_PATTERN][16];
extern uint8_t seq_patternchain[NUM_SEQ_TRACKS][4];
extern uint8_t seq_content_type[NUM_SEQ_PATTERN];
extern uint8_t seq_track_type[NUM_SEQ_TRACKS];
extern uint8_t seq_step;
extern uint8_t seq_chord_key_ammount;
extern int seq_tempo_ms;
extern uint8_t seq_bpm;
extern uint8_t seq_chain_lenght;
extern bool seq_running;
extern bool seq_recording;
extern bool smartfilter;
extern uint8_t seq_state_last_loadsave;
extern uint8_t seq_active_track;
extern uint8_t seq_menu;
extern uint8_t seq_temp_select_menu;
extern uint8_t seq_temp_active_menu;
extern uint8_t seq_chain_active_chainstep; //for editor
extern uint8_t seq_chain_active_step;
extern int seq_transpose;
extern uint8_t seq_inst_dexed[NUM_SEQ_TRACKS];
extern uint8_t arp_step;
extern uint8_t arp_note;
extern uint8_t arp_chord;
extern uint8_t arp_octave;
extern uint8_t arp_lenght;
extern uint8_t arp_style;
extern uint8_t arp_speed;
extern uint8_t seq_element_shift;
extern int seq_oct_shift;
extern char arp_style_names[4][3];
extern char seq_chord_names[7][4];
extern uint8_t seq_data_buffer[16];
extern float drums_volume;
extern uint8_t drum_midi_channel;
uint8_t seq_active_function = 99;
uint8_t activesample; extern uint8_t seq_data[10][16];
#endif

#ifdef DISPLAY_LCD_SPI
extern void change_disp_sd(bool d);
#endif

#ifdef SGTL5000_AUDIO_ENHANCE
#include "control_sgtl5000plus.h"
extern AudioControlSGTL5000Plus sgtl5000;
#else
extern AudioControlSGTL5000 sgtl5000;
#endif

#if defined(USE_FX)
extern AudioSynthWaveform*        chorus_modulator[NUM_DEXED];
extern AudioEffectModulatedDelay* modchorus[NUM_DEXED];
extern AudioMixer4*               chorus_mixer[NUM_DEXED];
extern AudioMixer4*               delay_fb_mixer[NUM_DEXED];
extern AudioEffectDelay*          delay_fx[NUM_DEXED];
extern AudioMixer4*               delay_mixer[NUM_DEXED];
#endif
extern AudioEffectMonoStereo*     mono2stereo[NUM_DEXED];

extern AudioMixer4                microdexed_peak_mixer;
extern AudioAnalyzePeak           microdexed_peak;
#if defined(USE_FX)
extern AudioMixer4                reverb_mixer_r;
extern AudioMixer4                reverb_mixer_l;
#ifdef USE_PLATEREVERB
extern AudioEffectPlateReverb     reverb;
#else
extern AudioEffectFreeverbStereoFloat        freeverb;
#endif
#endif
extern AudioMixer4                master_mixer_r;
extern AudioMixer4                master_mixer_l;
extern AudioEffectStereoMono      stereo2mono;
extern AudioAnalyzePeak           master_peak_r;
extern AudioAnalyzePeak           master_peak_l;

extern char sd_string[LCD_cols + 1];
extern char g_voice_name[NUM_DEXED][VOICE_NAME_LEN];
extern char g_bank_name[NUM_DEXED][BANK_NAME_LEN];
extern int perform_attack_mod[NUM_DEXED];
extern int perform_release_mod[NUM_DEXED];
extern float midi_ticks_factor[10];
extern uint8_t midi_bpm;

/***********************************************************************
   GLOBAL
************************************************************************/
elapsedMillis back_from_volume;
uint8_t instance_num[8][8];
const char accepted_chars[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-abcdefghijklmnopqrstuvwxyz";
const char noteNames[12][3] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
uint8_t active_perform_page = 1;
uint8_t orig_attack_values[2][7];
uint8_t orig_release_values[2][7];
int temp_int;
bool menu_select_toggle;
float temp_float;


#ifdef I2C_DISPLAY
#include <LiquidCrystal_I2C.h>
Disp_Plus<LiquidCrystal_I2C> lcd(LCD_I2C_ADDRESS, _LCDML_DISP_cols, _LCDML_DISP_rows);
#endif

#ifdef U8X8_DISPLAY
#include <U8x8lib.h>
#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif
Disp_Plus<U8X8_DISPLAY_CLASS> lcd(/* cs=*/ U8X8_CS_PIN, /* dc=*/ U8X8_DC_PIN, /* reset=*/ U8X8_RESET_PIN);
//Disp_Plus<U8X8_DISPLAY_CLASS> lcd(U8X8_PIN_NONE);
#endif

const uint8_t scroll_bar[5][8] = {
  {B10001, B10001, B10001, B10001, B10001, B10001, B10001, B10001}, // scrollbar top
  {B11111, B11111, B10001, B10001, B10001, B10001, B10001, B10001}, // scroll state 1
  {B10001, B10001, B11111, B11111, B10001, B10001, B10001, B10001}, // scroll state 2
  {B10001, B10001, B10001, B10001, B11111, B11111, B10001, B10001}, // scroll state 3
  {B10001, B10001, B10001, B10001, B10001, B10001, B11111, B11111}  // scrollbar bottom
};

const uint8_t block_bar[5][8] = {
  {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000},
  {B11000, B11000, B11000, B11000, B11000, B11000, B11000, B11000},
  {B11100, B11100, B11100, B11100, B11100, B11100, B11100, B11100},
  {B11110, B11110, B11110, B11110, B11110, B11110, B11110, B11110},
  {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111}
};

const uint8_t meter_bar[5][8] = {
  {B10000, B10000, B10000, B10000, B10000, B10000, B10000, B10000},
  {B01000, B01000, B01000, B01000, B01000, B01000, B01000, B01000},
  {B00100, B00100, B00100, B00100, B00100, B00100, B00100, B00100},
  {B00010, B00010, B00010, B00010, B00010, B00010, B00010, B00010},
  {B00001, B00001, B00001, B00001, B00001, B00001, B00001, B00001}
};

const uint8_t special_chars[22][8] = {
  {B11111, B11011, B10011, B11011, B11011, B11011, B11011, B11111}, //  [0] 1 small invers
  {B11111, B11011, B10101, B11101, B11011, B10111, B10001, B11111}, //  [1] 2 small invers
  {B11111, B11011, B10011, B11011, B11011, B11011, B11011, B11111}, //  [2] 1 OP invers
  {B11111, B11011, B10101, B11101, B11011, B10111, B10001, B11111}, //  [3] 2 OP invers
  {B11111, B10001, B11101, B11011, B11101, B10101, B11011, B11111}, //  [4] 3 OP invers
  {B11111, B10111, B10111, B10101, B10001, B11101, B11101, B11111}, //  [5] 4 OP invers
  {B11111, B10001, B10111, B10011, B11101, B11101, B10011, B11111}, //  [6] 5 OP invers
  {B11111, B11001, B10111, B10011, B10101, B10101, B11011, B11111}, //  [7] 6 OP invers
  {B00000, B00000, B00000, B00000, B00000, B00000, B00000, B11111}, //  [8] Level 1
  {B00000, B00000, B00000, B00000, B00000, B00000, B11111, B11111}, //  [9] Level 2
  {B00000, B00000, B00000, B00000, B00000, B11111, B11111, B11111}, // [10] Level 3
  {B00000, B00000, B00000, B00000, B11111, B11111, B11111, B11111}, // [11] Level 4
  {B00000, B00000, B00000, B11111, B11111, B11111, B11111, B11111}, // [12] Level 5
  {B00000, B00000, B11111, B11111, B11111, B11111, B11111, B11111}, // [13] Level 6
  {B00000, B11111, B11111, B11111, B11111, B11111, B11111, B11111}, // [14] Level 7
  {B11111, B11111, B11111, B11111, B11111, B11111, B11111, B11111}, // [15] Level 8
  {B00100, B00110, B00101, B00101, B01101, B11101, B11100, B11000}, // [16] Note
  {B01110, B10001, B10001, B01110, B00100, B00100, B00110, B00110}, // [17] Disabled 2nd instance symbol
  {B11111, B10001, B10111, B10001, B10111, B10111, B10111, B11111}, // [18] Favorites Icon
  {B01000, B01100, B01110, B01111, B01110, B01100, B01000, B00000}, // [19] Play Symbol
  {B00000, B01110, B11111, B11111, B11111, B01110, B00000, B00000}, // [20] Record Symbol
  {B00000, B00000, B01110, B01110, B01110, B00000, B00000, B00000}  // [21] Stop Symbol
};

enum { SCROLLBAR, BLOCKBAR, METERBAR };
enum { ENC_R, ENC_L };
enum {MENU_VOICE_BANK, MENU_VOICE_SOUND};

void lcdml_menu_display(void);
void lcdml_menu_clear(void);
void lcdml_menu_control(void);
#ifdef USE_FX
void UI_func_reverb_roomsize(uint8_t param);
void UI_func_reverb_damping(uint8_t param);
void UI_func_reverb_lowpass(uint8_t param);
void UI_func_reverb_lodamp(uint8_t param);
void UI_func_reverb_hidamp(uint8_t param);
void UI_func_reverb_diffusion(uint8_t param);
void UI_func_reverb_level(uint8_t param);
void UI_func_chorus_frequency(uint8_t param);
void UI_func_chorus_waveform(uint8_t param);
void UI_func_chorus_depth(uint8_t param);
void UI_func_chorus_level(uint8_t param);
void UI_func_delay_time(uint8_t param);
void UI_func_delay_feedback(uint8_t param);
void UI_func_delay_level(uint8_t param);
void UI_func_reverb_send(uint8_t param);
void UI_func_filter_cutoff(uint8_t param);
void UI_func_filter_resonance(uint8_t param);
void UI_func_drum_reverb_send(uint8_t param);
#endif
void UI_func_transpose(uint8_t param);
void UI_func_tune(uint8_t param);
void UI_func_midi_channel(uint8_t param);
void UI_func_lowest_note(uint8_t param);
void UI_func_highest_note(uint8_t param);
void UI_func_sound_intensity(uint8_t param);
void UI_func_panorama(uint8_t param);
void UI_func_stereo_mono(uint8_t param);
void UI_func_note_refresh(uint8_t param);
void UI_func_polyphony(uint8_t param);
void UI_func_mono_poly(uint8_t param);
void UI_func_pb_range(uint8_t param);
void UI_func_pb_step(uint8_t param);
void UI_func_mw_range(uint8_t param);
void UI_func_mw_assign(uint8_t param);
void UI_func_mw_mode(uint8_t param);
void UI_func_fc_range(uint8_t param);
void UI_func_fc_assign(uint8_t param);
void UI_func_fc_mode(uint8_t param);
void UI_func_bc_range(uint8_t param);
void UI_func_bc_assign(uint8_t param);
void UI_func_bc_mode(uint8_t param);
void UI_func_at_range(uint8_t param);
void UI_func_at_assign(uint8_t param);
void UI_func_at_mode(uint8_t param);
void UI_func_portamento_mode(uint8_t param);
void UI_func_portamento_glissando(uint8_t param);
void UI_func_portamento_time(uint8_t param);
void UI_handle_OP(uint8_t param);
void UI_func_information(uint8_t param);
void UI_func_seq_pattern_editor(uint8_t param);
void UI_func_seq_vel_editor(uint8_t param);
void UI_func_seq_live_transpose_oct(uint8_t param);
void UI_func_arp_shift(uint8_t param);
void UI_func_seq_chord_keys_ammount(uint8_t param);
void UI_func_seq_lenght(uint8_t param);
void UI_func_seq_tempo(uint8_t param);
void UI_func_seq_pat_chain(uint8_t param);
void UI_func_arpeggio(uint8_t param);
void UI_func_seq_track_setup(uint8_t param);
void UI_func_dexed_assign(uint8_t param);
void UI_func_seq_display_style(uint8_t param);
void UI_func_seq_state_load(uint8_t param);
void UI_func_seq_state_save(uint8_t param);
void UI_func_set_sequence_name(uint8_t param);
void UI_func_volume(uint8_t param);
void UI_func_smart_filter(uint8_t param);
void UI_func_drum_midi_channel(uint8_t param);
void UI_func_drum_monitor(uint8_t param);
void UI_func_load_performance(uint8_t param);
void UI_func_save_performance(uint8_t param);
void UI_func_load_voiceconfig(uint8_t param);
void UI_func_save_voiceconfig(uint8_t param);
void UI_func_save_voice(uint8_t param);
void UI_func_load_fx(uint8_t param);
void UI_func_save_fx(uint8_t param);
void UI_func_eeprom_reset(uint8_t param);
void UI_func_midi_soft_thru(uint8_t param);
void UI_func_velocity_level(uint8_t param);
void UI_func_voice_select(uint8_t param);
void UI_func_sysex_send_voice(uint8_t param);
void UI_func_sysex_receive_bank(uint8_t param);
void UI_func_sysex_send_bank(uint8_t param);
void UI_func_eq_1(uint8_t param);
void UI_func_eq_2(uint8_t param);
void UI_func_eq_3(uint8_t param);
void UI_func_eq_4(uint8_t param);
void UI_func_eq_5(uint8_t param);
void UI_func_eq_6(uint8_t param);
void UI_func_eq_7(uint8_t param);
void UI_function_not_enabled(void);
void UI_function_not_implemented(uint8_t param);
void UI_func_favorites(uint8_t param);
void UI_update_instance_icons();
bool UI_select_name(uint8_t y, uint8_t x, char* edit_string, uint8_t len, bool init);
uint8_t search_accepted_char(uint8_t c);
void lcd_display_int(int16_t var, uint8_t size, bool zeros, bool brackets, bool sign);
void lcd_display_float(float var, uint8_t size_number, uint8_t size_fraction, bool zeros, bool brackets, bool sign);
void lcd_display_bar_int(const char* title, uint32_t value, float factor, int32_t min_value, int32_t max_value, uint8_t size, bool zeros, bool sign, bool init);
void lcd_display_bar_float(const char* title, float value, float factor, int32_t min_value, int32_t max_value, uint8_t size_number, uint8_t size_fraction, bool zeros, bool sign, bool init);
void lcd_display_meter_int(const char* title, uint32_t value, float factor, float offset, int32_t min_value, int32_t max_value, uint8_t size, bool zeros, bool sign, bool init);
void lcd_display_meter_float(const char* title, float value, float factor, float offset, int32_t min_value, int32_t max_value, uint8_t size_number, uint8_t size_fraction, bool zeros, bool sign, bool init);
void lcd_active_instance_number(uint8_t instance_id);
void lcd_OP_active_instance_number(uint8_t instance_id, uint8_t op);
void lcd_special_chars(uint8_t mode);
void eeprom_update_var(uint16_t pos, uint8_t val, const char* val_string);
void lcd_display_delay_sync(uint8_t sync);
void string_trim(char *s);
void save_favorite(uint8_t b, uint8_t v, uint8_t instance_id);
void draw_favorite_icon(uint8_t b, uint8_t v, uint8_t instance_id);
bool check_favorite(uint8_t b, uint8_t v, uint8_t instance_id);
bool quick_check_favorites_in_bank(uint8_t b, uint8_t instance_id);
void locate_previous_non_favorite();
void locate_previous_favorite();
void locate_next_favorite();
void locate_next_non_favorite();
void locate_random_non_favorite();
void UI_func_drums_main_volume(uint8_t param);
void UI_func_drum_volume(uint8_t param);
void UI_func_drum_pan(uint8_t param);
void UI_func_drum_pitch(uint8_t param);

char* basename(const char* filename);
char* strip_extension(char* filename);

// normal menu
LCDMenuLib2_menu LCDML_0(255, 0, 0, NULL, NULL); // normal root menu element (do not change)
LCDMenuLib2 LCDML(LCDML_0, _LCDML_DISP_rows, _LCDML_DISP_cols, lcdml_menu_display, lcdml_menu_clear, lcdml_menu_control);

#if defined(USE_FX)
#if defined(TEENSY4)
#include "UI_FX_T4.h"
#else
#include "UI_FX.h"
#endif
#else
#include "UI_NO_FX.h"
#endif

int favsearcher = 0;

// create menu
LCDML_createMenu(_LCDML_DISP_cnt);

/***********************************************************************
   CONTROL
 ***********************************************************************/
class EncoderDirection
{
  public:
    EncoderDirection(void)
    {
      reset();
    }

    void reset(void)
    {
      button_short = false;
      button_long = false;
      button_pressed = false;
      left = false;
      right = false;
      up = false;
      down = false;
    }

    void ButtonShort(bool state)
    {
      button_short = state;
    }

    bool ButtonShort(void)
    {
      if (button_short == true)
      {
        button_short = false;
        return (true);
      }
      return (false);
    }

    void ButtonLong(bool state)
    {
      button_long = state;
    }

    bool ButtonLong(void)
    {
      if (button_long == true)
      {
        button_long = false;
        return (true);
      }
      return (false);
    }

    void ButtonPressed(bool state)
    {
      button_pressed = state;
    }

    bool ButtonPressed(void)
    {
      return (button_pressed);
    }

    void Left(bool state)
    {
      left = state;
    }

    bool Left(void)
    {
      if (left == true)
      {
        left = false;
        return (true);
      }
      return (false);
    }

    void Right(bool state)
    {
      right = state;
    }

    bool Right(void)
    {
      if (right == true)
      {
        right = false;
        return (true);
      }
      return (false);
    }

    void Up(bool state)
    {
      up = state;
    }

    bool Up(void)
    {
      if (up == true)
      {
        up = false;
        return (true);
      }
      return (false);
    }

    void Down(bool state)
    {
      down = state;
    }

    bool Down(void)
    {
      if (down == true)
      {
        down = false;
        return (true);
      }
      return (false);
    }

  private:
    bool button_short;
    bool button_long;
    bool button_pressed;
    bool left;
    bool right;
    bool up;
    bool down;
};

//Encoder ENCODER[NUM_ENCODER] = {Encoder(ENC_R_PIN_B, ENC_R_PIN_A), Encoder(ENC_L_PIN_B, ENC_L_PIN_A)};
MD_REncoder ENCODER[NUM_ENCODER] = {MD_REncoder(ENC_R_PIN_B, ENC_R_PIN_A), MD_REncoder(ENC_L_PIN_B, ENC_L_PIN_A)};
EncoderDirection encoderDir[NUM_ENCODER];

long  g_LCDML_CONTROL_button_press_time[NUM_ENCODER] = {0, 0};
bool  g_LCDML_CONTROL_button_prev[NUM_ENCODER] = {HIGH, HIGH};
uint8_t g_LCDML_CONTROL_prev[NUM_ENCODER] = {0, 0};
bool menu_init = true;

#ifdef U8X8_DISPLAY
const uint8_t * flipped_scroll_bar[5];
const uint8_t * flipped_block_bar[7];
const uint8_t * flipped_meter_bar[7];

uint8_t * rotTile(const uint8_t * tile)
{
  uint8_t * newt = new uint8_t[8];
  for (int x = 0; x < 8; x++) {
    uint8_t newb = 0;
    for (int y = 0 ; y < 8; y++) {
      newb |= (tile[y] << x) & 0x80;
      newb >>= 1;
    }
    newt[x] = newb;
  }
  return newt;
}
#endif

void smart_filter(uint8_t dir)
{
  bool found = false;

  //search backwards
  if (dir == 0) {
    if (smartfilter) {
      do {
        if (found == false)  activesample = constrain(activesample - 1, 0, NUM_DRUMSET_CONFIG - 2);
        for (uint8_t d = 0; d < 10; d++)
        {
          if (seq_content_type[d] == 0) {
            for (uint8_t y = 0; y < 16; y++)
            {
              if (drum_config[activesample].midinote == seq_data[d][y] || drum_config[activesample].midinote == seq_vel[d][y])
              {
                found = true;
                break;
              }
            }
          }
        }
      } while (found == false && activesample > 0 );
    }
    else
      activesample = constrain(activesample - 1, 0, NUM_DRUMSET_CONFIG - 2);
  }
  else //search forwards
  {
    if (smartfilter) {
      do {
        if (found == false)  activesample = constrain(activesample + 1, 0, NUM_DRUMSET_CONFIG - 2);
        for (uint8_t d = 0; d < 10; d++)
        {
          if (seq_content_type[d] == 0) {

            for (uint8_t y = 0; y < 16; y++)
            {
              if (drum_config[activesample].midinote == seq_data[d][y] || drum_config[activesample].midinote == seq_vel[d][y])
              {
                found = true;
                break;
              }
            }
          }
        }
      } while (found == false && activesample < NUM_DRUMSET_CONFIG - 2 );
    }
    else
      activesample = constrain(activesample + 1, 0, NUM_DRUMSET_CONFIG - 2);
  }
}

void setup_ui(void)
{
  // LCD Begin
#ifdef I2C_DISPLAY
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.noCursor();
#else
  lcd.begin();
  lcd.clear();
  lcd.setFont(u8x8_font_amstrad_cpc_extended_f);
#endif
  lcd.setCursor(3, 0);
  lcd.print(F("MicroDexed"));
  lcd.setCursor(0, 1);
  lcd.print(F("(c)parasiTstudio"));

  lcd_special_chars(SCROLLBAR);
  // LCDMenuLib Setup
  LCDML_setup(_LCDML_DISP_cnt);
  // Enable Menu Rollover
  //LCDML.MENU_enRollover();
  // Enable Screensaver (screensaver menu function, time to activate in ms)
  //LCDML.SCREEN_enable(UI_func_voice_select, VOICE_SELECTION_MS);
}

#ifdef DEBUG
void setup_debug_message(void)
{
  // LCD Begin
  lcd.clear();
  lcd.setCursor(1, 0);
  lcd.print(F("* DEBUG MODE *"));
  lcd.setCursor(1, 1);
  lcd.print(F("ENABLE CONSOLE"));
  delay(300);
  lcd.setCursor(1, 1);
  lcd.print(_LCDML_VERSION);
  lcd.print(F("  "));
}
#endif

/***********************************************************************
   MENU CONTROL
 ***********************************************************************/
uint8_t get_current_cursor_id(void)
{
  LCDMenuLib2_menu *tmp;

  if ((tmp = LCDML.MENU_getCurrentObj()) != NULL)
    return (tmp->getChild(LCDML.MENU_getCursorPosAbs())->getID());
  else
    return (0);
}

void lcdml_menu_control(void)
{
  // If something must init, put in in the setup condition
  if (LCDML.BT_setup())
  {
    pinMode(BUT_R_PIN, INPUT_PULLUP);
    pinMode(BUT_L_PIN, INPUT_PULLUP);

    ENCODER[ENC_R].begin();
    ENCODER[ENC_L].begin();
  }

  if (back_from_volume > BACK_FROM_VOLUME_MS && LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_volume))
  {
    encoderDir[ENC_L].reset();
    encoderDir[ENC_R].reset();

    if (LCDML.MENU_getLastActiveFunctionID() < 0xff)
      LCDML.OTHER_jumpToID(LCDML.MENU_getLastActiveFunctionID());
    else
      LCDML.OTHER_setCursorToID(LCDML.MENU_getLastCursorPositionID());
    //LCDML.FUNC_goBackToMenu();
  }

  // Volatile Variables
  long g_LCDML_CONTROL_Encoder_position[NUM_ENCODER] = {ENCODER[ENC_R].read(), ENCODER[ENC_L].read()};
  bool button[NUM_ENCODER] = {digitalRead(BUT_R_PIN), digitalRead(BUT_L_PIN)};

  /************************************************************************************
    Basic encoder handling (from LCDMenuLib2)
   ************************************************************************************/

  // RIGHT
  if (g_LCDML_CONTROL_Encoder_position[ENC_R] <= -3)
  {
    if (!button[ENC_R])
    {
      LCDML.BT_left();
#ifdef DEBUG
      Serial.println(F("ENC-R left"));
#endif
      encoderDir[ENC_R].Left(true);
      g_LCDML_CONTROL_button_prev[ENC_R] = LOW;
      g_LCDML_CONTROL_button_press_time[ENC_R] = -1;
    }
    else
    {
#ifdef DEBUG
      Serial.println(F("ENC-R down"));
#endif
      encoderDir[ENC_R].Down(true);
      LCDML.BT_down();
    }
    ENCODER[ENC_R].write(g_LCDML_CONTROL_Encoder_position[ENC_R] + 4);
  }
  else if (g_LCDML_CONTROL_Encoder_position[ENC_R] >= 3)
  {
    if (!button[ENC_R])
    {
#ifdef DEBUG
      Serial.println(F("ENC-R right"));
#endif
      encoderDir[ENC_R].Right(true);
      LCDML.BT_right();
      g_LCDML_CONTROL_button_prev[ENC_R] = LOW;
      g_LCDML_CONTROL_button_press_time[ENC_R] = -1;
    }
    else
    {
#ifdef DEBUG
      Serial.println(F("ENC-R up"));
#endif
      encoderDir[ENC_R].Up(true);
      LCDML.BT_up();
    }
    ENCODER[ENC_R].write(g_LCDML_CONTROL_Encoder_position[ENC_R] - 4);
  }
  else
  {
    if (!button[ENC_R] && g_LCDML_CONTROL_button_prev[ENC_R]) //falling edge, button pressed
    {
      encoderDir[ENC_R].ButtonPressed(true);
      g_LCDML_CONTROL_button_prev[ENC_R] = LOW;
      g_LCDML_CONTROL_button_press_time[ENC_R] = millis();
    }
    else if (button[ENC_R] && !g_LCDML_CONTROL_button_prev[ENC_R]) //rising edge, button not active
    {
      encoderDir[ENC_R].ButtonPressed(false);
      g_LCDML_CONTROL_button_prev[ENC_R] = HIGH;

      if (g_LCDML_CONTROL_button_press_time[ENC_R] < 0)
      {
        g_LCDML_CONTROL_button_press_time[ENC_R] = millis();
        //Reset for left right action
      }
      else if ((millis() - g_LCDML_CONTROL_button_press_time[ENC_R]) >= LONG_BUTTON_PRESS)
      {
#ifdef DEBUG
        Serial.println("ENC-R long released");
#endif
        //LCDML.BT_quit();
        encoderDir[ENC_R].ButtonLong(true);
      }
      else if ((millis() - g_LCDML_CONTROL_button_press_time[ENC_R]) >= BUT_DEBOUNCE_MS)
      {
#ifdef DEBUG
        Serial.println(F("ENC-R short"));
#endif
        encoderDir[ENC_R].ButtonShort(true);

        LCDML.BT_enter();
      }
    }
  }

  if (encoderDir[ENC_R].ButtonPressed() == true && (millis() - g_LCDML_CONTROL_button_press_time[ENC_R]) >= LONG_BUTTON_PRESS)
  {
#ifdef DEBUG
    Serial.println("ENC-R long recognized");
#endif
    encoderDir[ENC_R].ButtonLong(true);

    //    if (seq_running) {
    //      seq_running = false;
    //      seq_recording = false;
    //      seq_step = 0;
    //      seq_chain_active_step = 0;
    //      MicroDexed[0]->panic();
    //    }

    if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_voice_select))
    {
      LCDML.BT_enter();
      LCDML.OTHER_updateFunc();
      LCDML.loop_menu();
      encoderDir[ENC_R].ButtonPressed(false);
      encoderDir[ENC_R].ButtonLong(false);
    }
    else
    {
      if (LCDML.FUNC_getID() < 0xff)
        LCDML.FUNC_setGBAToLastFunc();
      else
        LCDML.FUNC_setGBAToLastCursorPos();

      LCDML.OTHER_jumpToFunc(UI_func_voice_select);
      encoderDir[ENC_R].reset();
    }
  }

  // LEFT
  if (g_LCDML_CONTROL_Encoder_position[ENC_L] <= -3)
  {
    if (!button[ENC_L])
    {
#ifdef DEBUG
      Serial.println(F("ENC-L left"));
#endif
      encoderDir[ENC_L].Left(true);
      LCDML.BT_left();
      g_LCDML_CONTROL_button_prev[ENC_L] = LOW;
      g_LCDML_CONTROL_button_press_time[ENC_L] = -1;
    }
    else
    {
#ifdef DEBUG
      Serial.println(F("ENC-L down"));
#endif
      encoderDir[ENC_L].Down(true);
      LCDML.BT_down();
      if (LCDML.FUNC_getID() != LCDML.OTHER_getIDFromFunction(UI_func_volume))
      {
        LCDML.OTHER_jumpToFunc(UI_func_volume);
      }
    }
    ENCODER[ENC_L].write(g_LCDML_CONTROL_Encoder_position[ENC_L] + 4);
  }
  else if (g_LCDML_CONTROL_Encoder_position[ENC_L] >= 3)
  {
    if (!button[ENC_L])
    {
#ifdef DEBUG
      Serial.println(F("ENC-L right"));
#endif
      encoderDir[ENC_L].Right(true);
      LCDML.BT_right();
      g_LCDML_CONTROL_button_prev[ENC_L] = LOW;
      g_LCDML_CONTROL_button_press_time[ENC_L] = -1;
    }
    else
    {
#ifdef DEBUG
      Serial.println(F("ENC-L up"));
#endif
      encoderDir[ENC_L].Up(true);
      LCDML.BT_up();
      if (LCDML.FUNC_getID() != LCDML.OTHER_getIDFromFunction(UI_func_volume))
      {
        LCDML.OTHER_jumpToFunc(UI_func_volume);
      }
    }
    ENCODER[ENC_L].write(g_LCDML_CONTROL_Encoder_position[ENC_L] - 4);
  }
  else
  {
    if (!button[ENC_L] && g_LCDML_CONTROL_button_prev[ENC_L]) //falling edge, button pressed
    {
      encoderDir[ENC_L].ButtonPressed(true);
      g_LCDML_CONTROL_button_prev[ENC_L] = LOW;
      g_LCDML_CONTROL_button_press_time[ENC_L] = millis();
    }
    else if (button[ENC_L] && !g_LCDML_CONTROL_button_prev[ENC_L]) //rising edge, button not active
    {
      encoderDir[ENC_L].ButtonPressed(false);
      g_LCDML_CONTROL_button_prev[ENC_L] = HIGH;

      if (g_LCDML_CONTROL_button_press_time[ENC_L] < 0)
      {
        g_LCDML_CONTROL_button_press_time[ENC_L] = millis();
        //Reset for left right action
      }
      else if ((millis() - g_LCDML_CONTROL_button_press_time[ENC_L]) >= LONG_BUTTON_PRESS)
      {
#ifdef DEBUG
        Serial.println(F("ENC-L long released"));
#endif
        //encoderDir[ENC_L].ButtonLong(true);
        //LCDML.BT_quit();
      }
      else if ((millis() - g_LCDML_CONTROL_button_press_time[ENC_L]) >= BUT_DEBOUNCE_MS)
      {
        //LCDML.BT_enter();
#ifdef DEBUG
        Serial.println(F("ENC-L short"));
#endif
        encoderDir[ENC_L].ButtonShort(true);

        if ((LCDML.MENU_getLastActiveFunctionID() == 0xff && LCDML.MENU_getLastCursorPositionID() == 0) || menu_init == true)
        {
          LCDML.MENU_goRoot();
          menu_init = false;
        }
        else if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_volume))
        {
          encoderDir[ENC_L].reset();
          encoderDir[ENC_R].reset();

          if (LCDML.MENU_getLastActiveFunctionID() < 0xff)
            LCDML.OTHER_jumpToID(LCDML.MENU_getLastActiveFunctionID());
          else
            LCDML.OTHER_setCursorToID(LCDML.MENU_getLastCursorPositionID());
        }
        else
          LCDML.BT_quit();
      }
    }
  }

  if (encoderDir[ENC_L].ButtonPressed() == true && (millis() - g_LCDML_CONTROL_button_press_time[ENC_L]) >= LONG_BUTTON_PRESS)
  {
#ifdef DEBUG
    Serial.println(F("ENC-L long recognized"));
#endif

    if (LCDML.FUNC_getID() == LCDML.OTHER_getIDFromFunction(UI_func_voice_select))
      save_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);

    //for (uint8_t i = 0; i < NUM_DEXED; i++)
    //  MicroDexed[i]->panic();

    encoderDir[ENC_L].reset();
    encoderDir[ENC_R].reset();
  }
}

/***********************************************************************
   MENU DISPLAY
 ***********************************************************************/
void lcdml_menu_clear(void)
{
  lcd.clear();
  lcd.setCursor(0, 0);
}

void lcdml_menu_display(void)
{
  // update content
  // ***************
  if (LCDML.DISP_checkMenuUpdate()) {
    // clear menu
    // ***************
    LCDML.DISP_clear();

    // declaration of some variables
    // ***************
    // content variable
    char content_text[_LCDML_DISP_cols];  // save the content text of every menu element
    // menu element object
    LCDMenuLib2_menu *tmp;
    // some limit values
    uint8_t i = LCDML.MENU_getScroll();
    uint8_t maxi = _LCDML_DISP_rows + i;
    uint8_t n = 0;

    // check if this element has children
    if ((tmp = LCDML.MENU_getDisplayedObj()) != NULL)
    {
      // loop to display lines
      do
      {
        // check if a menu element has a condition and if the condition be true
        if (tmp->checkCondition())
        {
          // check the type off a menu element
          if (tmp->checkType_menu() == true)
          {
            // display normal content
            LCDML_getContent(content_text, tmp->getID());
            lcd.setCursor(1, n);
            lcd.print(content_text);
          }
          else
          {
            if (tmp->checkType_dynParam()) {
              tmp->callback(n);
            }
          }
          // increment some values
          i++;
          n++;
        }
        // try to go to the next sibling and check the number of displayed rows
      } while (((tmp = tmp->getSibling(1)) != NULL) && (i < maxi));
    }
  }

  if (LCDML.DISP_checkMenuCursorUpdate())
  {
    // init vars
    uint8_t n_max             = (LCDML.MENU_getChilds() >= _LCDML_DISP_rows) ? _LCDML_DISP_rows : (LCDML.MENU_getChilds());
    uint8_t scrollbar_min     = 0;
    uint8_t scrollbar_max     = LCDML.MENU_getChilds();
    uint8_t scrollbar_cur_pos = LCDML.MENU_getCursorPosAbs();
    uint8_t scroll_pos        = ((1.*n_max * _LCDML_DISP_rows) / (scrollbar_max - 1) * scrollbar_cur_pos);

    // display rows
    for (uint8_t n = 0; n < n_max; n++)
    {
      //set cursor
      lcd.setCursor(0, n);

      //set cursor char
      if (n == LCDML.MENU_getCursorPos()) {
        lcd.write(_LCDML_DISP_cfg_cursor);
      } else {
        lcd.write(' ');
      }

      // delete or reset scrollbar
      if (_LCDML_DISP_cfg_scrollbar == 1) {
        if (scrollbar_max > n_max) {
#ifdef I2C_DISPLAY
          lcd.setCursor((_LCDML_DISP_cols - 1), n);
          lcd.write((uint8_t)0);
#else
          lcd.drawTile((_LCDML_DISP_cols - 1), n, 1, flipped_scroll_bar[0]);
          lcd.setCursor((_LCDML_DISP_cols), n + 1);
#endif
        }
        else {
          lcd.setCursor((_LCDML_DISP_cols - 1), n);
          lcd.print(F(" "));
        }
      }
    }

    // display scrollbar
    if (_LCDML_DISP_cfg_scrollbar == 1) {
      if (scrollbar_max > n_max) {
        //set scroll position
        if (scrollbar_cur_pos == scrollbar_min) {
          // min pos
#ifdef I2C_DISPLAY
          lcd.setCursor((_LCDML_DISP_cols - 1), 0);
          lcd.write((uint8_t)1);
#else
          lcd.drawTile((_LCDML_DISP_cols - 1), 0, 1, flipped_scroll_bar[1]);
          lcd.setCursor((_LCDML_DISP_cols), 1);
#endif
        } else if (scrollbar_cur_pos == (scrollbar_max - 1)) {
          // max pos
#ifdef I2C_DISPLAY
          lcd.setCursor((_LCDML_DISP_cols - 1), (n_max - 1));
          lcd.write((uint8_t)4);
#else
          lcd.drawTile((_LCDML_DISP_cols - 1), (n_max - 1), 1, flipped_scroll_bar[4]);
          lcd.setCursor((_LCDML_DISP_cols), (n_max));
#endif
        } else {
          // between
#ifdef I2C_DISPLAY
          lcd.setCursor((_LCDML_DISP_cols - 1), scroll_pos / n_max);
          lcd.write((uint8_t)(scroll_pos % n_max) + 1);
#else
          lcd.drawTile((_LCDML_DISP_cols - 1), scroll_pos / n_max, 1, flipped_scroll_bar[(scroll_pos % n_max) + 1]);
          lcd.setCursor((_LCDML_DISP_cols), (scroll_pos / n_max) + 1);
#endif
        }
      }
    }
  }
}

//####################################################################################################################################################################################################

/***********************************************************************
   MENU
 ***********************************************************************/

#ifdef USE_FX
void UI_func_reverb_roomsize(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Room", configuration.fx.reverb_roomsize, 1.0, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 3, false, false, true);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.reverb_roomsize = constrain(configuration.fx.reverb_roomsize + ENCODER[ENC_R].speed(), REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.reverb_roomsize = constrain(configuration.fx.reverb_roomsize - ENCODER[ENC_R].speed(), REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX);
    }
    lcd_display_bar_int("Reverb Room", configuration.fx.reverb_roomsize, 1.0, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 3, false, false, false);

#ifdef USE_PLATEREVERB
    reverb.size(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0));
#else
    freeverb.roomsize(mapfloat(configuration.fx.reverb_roomsize, REVERB_ROOMSIZE_MIN, REVERB_ROOMSIZE_MAX, 0.0, 1.0));
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_roomsize), configuration.fx.reverb_roomsize);
  }
}

#if USE_PLATEREVERB != 1
void UI_func_reverb_damping(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Damp.", configuration.fx.reverb_damping, 1.0, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 3, false, false, true);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.reverb_damping = constrain(configuration.fx.reverb_damping + ENCODER[ENC_R].speed(), REVERB_DAMPING_MIN, REVERB_DAMPING_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.reverb_damping = constrain(configuration.fx.reverb_damping - ENCODER[ENC_R].speed(), REVERB_DAMPING_MIN, REVERB_DAMPING_MAX);
    }

    lcd_display_bar_int("Reverb Damp.", configuration.fx.reverb_damping, 1.0, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 3, false, false, false);

    freeverb.damping(mapfloat(configuration.fx.reverb_damping, REVERB_DAMPING_MIN, REVERB_DAMPING_MAX, 0.0, 1.0));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_damping), configuration.fx.reverb_damping);
  }
}
#else
void UI_func_reverb_lowpass(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Lowpass", configuration.fx.reverb_lowpass, 1.0, REVERB_LOWPASS_MIN, REVERB_LOWPASS_MAX, 3, false, false, true);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.reverb_lowpass = constrain(configuration.fx.reverb_lowpass + ENCODER[ENC_R].speed(), REVERB_LOWPASS_MIN, REVERB_LOWPASS_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.reverb_lowpass = constrain(configuration.fx.reverb_lowpass - ENCODER[ENC_R].speed(), REVERB_LOWPASS_MIN, REVERB_LOWPASS_MAX);
    }

    lcd_display_bar_int("Reverb Lowpass", configuration.fx.reverb_lowpass, 1.0, REVERB_LOWPASS_MIN, REVERB_LOWPASS_MAX, 3, false, false, false);

    reverb.lowpass(mapfloat(configuration.fx.reverb_lowpass, REVERB_LOWPASS_MIN, REVERB_LOWPASS_MAX, 0.0, 1.0));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_lowpass), configuration.fx.reverb_lowpass);
  }
}

void UI_func_reverb_lodamp(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Lodamp.", configuration.fx.reverb_lodamp, 1.0, REVERB_LODAMP_MIN, REVERB_LODAMP_MAX, 3, false, false, true);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.reverb_lodamp = constrain(configuration.fx.reverb_lodamp + ENCODER[ENC_R].speed(), REVERB_LODAMP_MIN, REVERB_LODAMP_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.reverb_lodamp = constrain(configuration.fx.reverb_lodamp - ENCODER[ENC_R].speed(), REVERB_LODAMP_MIN, REVERB_LODAMP_MAX);
    }

    lcd_display_bar_int("Reverb Lodamp.", configuration.fx.reverb_lodamp, 1.0, REVERB_LODAMP_MIN, REVERB_LODAMP_MAX, 3, false, false, false);

    reverb.lodamp(mapfloat(configuration.fx.reverb_lodamp, REVERB_LODAMP_MIN, REVERB_LODAMP_MAX, 0.0, 1.0));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_lodamp), configuration.fx.reverb_lodamp);
  }
}

void UI_func_reverb_hidamp(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Hidamp.", configuration.fx.reverb_hidamp, 1.0, REVERB_HIDAMP_MIN, REVERB_HIDAMP_MAX, 3, false, false, true);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.reverb_hidamp = constrain(configuration.fx.reverb_hidamp + ENCODER[ENC_R].speed(), REVERB_HIDAMP_MIN, REVERB_HIDAMP_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.reverb_hidamp = constrain(configuration.fx.reverb_hidamp - ENCODER[ENC_R].speed(), REVERB_HIDAMP_MIN, REVERB_HIDAMP_MAX);
    }

    lcd_display_bar_int("Reverb Hidamp.", configuration.fx.reverb_hidamp, 1.0, REVERB_HIDAMP_MIN, REVERB_HIDAMP_MAX, 3, false, false, false);

    reverb.hidamp(mapfloat(configuration.fx.reverb_hidamp, REVERB_HIDAMP_MIN, REVERB_HIDAMP_MAX, 0.0, 1.0));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_hidamp), configuration.fx.reverb_hidamp);
  }
}

void UI_func_reverb_diffusion(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Diff.", configuration.fx.reverb_diffusion, 1.0, REVERB_DIFFUSION_MIN, REVERB_DIFFUSION_MAX, 3, false, false, true);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.reverb_diffusion = constrain(configuration.fx.reverb_diffusion + ENCODER[ENC_R].speed(), REVERB_DIFFUSION_MIN, REVERB_DIFFUSION_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.reverb_diffusion = constrain(configuration.fx.reverb_diffusion - ENCODER[ENC_R].speed(), REVERB_DIFFUSION_MIN, REVERB_DIFFUSION_MAX);
    }

    lcd_display_bar_int("Reverb Diff.", configuration.fx.reverb_diffusion, 1.0, REVERB_DIFFUSION_MIN, REVERB_DIFFUSION_MAX, 3, false, false, false);

    reverb.diffusion(mapfloat(configuration.fx.reverb_diffusion, REVERB_DIFFUSION_MIN, REVERB_DIFFUSION_MAX, 0.0, 1.0));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_diffusion), configuration.fx.reverb_diffusion);
  }
}
#endif // PLATEREVERB != 1

void UI_func_reverb_level(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Level", configuration.fx.reverb_level, 1.0, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 3, false, false, true);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.reverb_level = constrain(configuration.fx.reverb_level + ENCODER[ENC_R].speed(), REVERB_LEVEL_MIN, REVERB_LEVEL_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.reverb_level = constrain(configuration.fx.reverb_level - ENCODER[ENC_R].speed(), REVERB_LEVEL_MIN, REVERB_LEVEL_MAX);
    }

    lcd_display_bar_int("Reverb Level", configuration.fx.reverb_level, 1.0, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 3, false, false, true);

    //master_mixer_r.gain(3, pseudo_log_curve(mapfloat(configuration.fx.reverb_level, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 0.0, 1.0)));
    //master_mixer_l.gain(3, pseudo_log_curve(mapfloat(configuration.fx.reverb_level, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 0.0, 1.0)));
    master_mixer_r.gain(3, volume_transform(mapfloat(configuration.fx.reverb_level, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 0.0, VOL_MAX_FLOAT)));
    master_mixer_l.gain(3, volume_transform(mapfloat(configuration.fx.reverb_level, REVERB_LEVEL_MIN, REVERB_LEVEL_MAX, 0.0, VOL_MAX_FLOAT)));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_level), configuration.fx.reverb_level);
  }
}

void UI_func_chorus_frequency(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_float("Chorus Frq.", configuration.fx.chorus_frequency[selected_instance_id], 0.1, CHORUS_FREQUENCY_MIN, CHORUS_FREQUENCY_MAX, 2, 1, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.chorus_frequency[selected_instance_id] = constrain(configuration.fx.chorus_frequency[selected_instance_id] + ENCODER[ENC_R].speed(), CHORUS_FREQUENCY_MIN, CHORUS_FREQUENCY_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.chorus_frequency[selected_instance_id] = constrain(configuration.fx.chorus_frequency[selected_instance_id] - ENCODER[ENC_R].speed(), CHORUS_FREQUENCY_MIN, CHORUS_FREQUENCY_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }
    lcd_display_bar_float("Chorus Frq.", configuration.fx.chorus_frequency[selected_instance_id], 0.1, CHORUS_FREQUENCY_MIN, CHORUS_FREQUENCY_MAX, 2, 1, false, false, false);

    chorus_modulator[selected_instance_id]->frequency(configuration.fx.chorus_frequency[selected_instance_id] / 10.0);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_frequency[0]), configuration.fx.chorus_frequency[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_frequency[1]), configuration.fx.chorus_frequency[1]);
#endif
  }
}

void UI_func_chorus_waveform(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Chorus Wavefrm"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (LCDML.BT_checkDown() && encoderDir[ENC_R].Down())
      configuration.fx.chorus_waveform[selected_instance_id] = constrain(configuration.fx.chorus_waveform[selected_instance_id] + 1, CHORUS_WAVEFORM_MIN, CHORUS_WAVEFORM_MAX);
    else if (LCDML.BT_checkUp() && encoderDir[ENC_R].Up())
      configuration.fx.chorus_waveform[selected_instance_id] = constrain(configuration.fx.chorus_waveform[selected_instance_id] - 1, CHORUS_WAVEFORM_MIN, CHORUS_WAVEFORM_MAX);
#if NUM_DEXED > 1
    else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {
      selected_instance_id = !selected_instance_id;
      lcd_active_instance_number(selected_instance_id);
      UI_update_instance_icons();
    }
#endif

    lcd.setCursor(0, 1);
    switch (configuration.fx.chorus_waveform[selected_instance_id])
    {
      case 0:
        chorus_modulator[selected_instance_id]->begin(WAVEFORM_TRIANGLE);
        lcd.print(F("[TRIANGLE]"));
        break;
      case 1:
        chorus_modulator[selected_instance_id]->begin(WAVEFORM_SINE);
        lcd.print(F("[SINE    ]"));
        break;
      default:
        chorus_modulator[selected_instance_id]->begin(WAVEFORM_TRIANGLE);
        lcd.print(F("[TRIANGLE]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_waveform[0]), configuration.fx.chorus_waveform[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_waveform[1]), configuration.fx.chorus_waveform[1]);
#endif
  }
}

void UI_func_chorus_depth(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Chorus Dpt.", configuration.fx.chorus_depth[selected_instance_id], 1.0, CHORUS_DEPTH_MIN, CHORUS_DEPTH_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.fx.chorus_depth[selected_instance_id] = constrain(configuration.fx.chorus_depth[selected_instance_id] + ENCODER[ENC_R].speed(), CHORUS_DEPTH_MIN, CHORUS_DEPTH_MAX);
      else if (LCDML.BT_checkUp())
        configuration.fx.chorus_depth[selected_instance_id] = constrain(configuration.fx.chorus_depth[selected_instance_id] - ENCODER[ENC_R].speed(), CHORUS_DEPTH_MIN, CHORUS_DEPTH_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Chorus Dpt.", configuration.fx.chorus_depth[selected_instance_id], 1.0, CHORUS_DEPTH_MIN, CHORUS_DEPTH_MAX, 3, false, false, false);

    chorus_modulator[selected_instance_id]->amplitude(configuration.fx.chorus_depth[selected_instance_id] / 100.0);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_depth[0]), configuration.fx.chorus_depth[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_depth[1]), configuration.fx.chorus_depth[1]);
#endif
  }
}

void UI_func_chorus_level(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Chorus Lvl.", configuration.fx.chorus_level[selected_instance_id], 1.0, CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.chorus_level[selected_instance_id] = constrain(configuration.fx.chorus_level[selected_instance_id] + ENCODER[ENC_R].speed(), CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 93, configuration.fx.chorus_level[selected_instance_id]);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.chorus_level[selected_instance_id] = constrain(configuration.fx.chorus_level[selected_instance_id] - ENCODER[ENC_R].speed(), CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 93, configuration.fx.chorus_level[selected_instance_id]);
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Chorus Lvl.", configuration.fx.chorus_level[selected_instance_id], 1.0, CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX, 3, false, false, false);

    //chorus_mixer[selected_instance_id]->gain(0, pseudo_log_curve(1.0 - mapfloat(configuration.fx.chorus_level[selected_instance_id], CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX, 0.0, 0.5)));
    //chorus_mixer[selected_instance_id]->gain(1, pseudo_log_curve(mapfloat(configuration.fx.chorus_level[selected_instance_id], CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX, 0.0, 0.5)));
    //chorus_mixer[selected_instance_id]->gain(0, 1.0 - mapfloat(configuration.fx.chorus_level[selected_instance_id], CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX, 0.0, 0.5));
    chorus_mixer[selected_instance_id]->gain(1, mapfloat(configuration.fx.chorus_level[selected_instance_id], CHORUS_LEVEL_MIN, CHORUS_LEVEL_MAX, 0.0, 0.5));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_level[0]), configuration.fx.chorus_level[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.chorus_level[1]), configuration.fx.chorus_level[1]);
#endif
  }
}

void UI_func_delay_time(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    if (configuration.fx.delay_sync[selected_instance_id] > 0)
    {
      lcd_display_delay_sync(configuration.fx.delay_sync[selected_instance_id]); //goto MIDI Sync
    }
    else
    {
#if DELAY_TIME_MAX >= 100
      lcd_display_bar_int("Delay Time", configuration.fx.delay_time[selected_instance_id], 10.0, DELAY_TIME_MIN, DELAY_TIME_MAX, 4, false, false, true);
#else
      lcd_display_bar_int("Delay Time", configuration.fx.delay_time[selected_instance_id], 10.0, DELAY_TIME_MIN, DELAY_TIME_MAX, 3, false, false, true);
#endif
    }
    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        if (configuration.fx.delay_time[selected_instance_id] == DELAY_TIME_MIN && configuration.fx.delay_sync[selected_instance_id] > DELAY_SYNC_MIN)
        {
          // MIDI-sync delay
          configuration.fx.delay_sync[selected_instance_id] = constrain(configuration.fx.delay_sync[selected_instance_id] - 1, DELAY_SYNC_MIN, DELAY_SYNC_MAX);
        }
        else
        {
          configuration.fx.delay_time[selected_instance_id] = constrain(configuration.fx.delay_time[selected_instance_id] + ENCODER[ENC_R].speed(), DELAY_TIME_MIN, DELAY_TIME_MAX);
          MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 105, configuration.fx.delay_time[selected_instance_id]);
        }
      }
      else if (LCDML.BT_checkUp())
      {
        if (configuration.fx.delay_time[selected_instance_id] == DELAY_TIME_MIN && configuration.fx.delay_sync[selected_instance_id] > DELAY_SYNC_MIN)
        {
          // MIDI-sync delay
          configuration.fx.delay_sync[selected_instance_id] = constrain(configuration.fx.delay_sync[selected_instance_id] + 1, DELAY_SYNC_MIN, DELAY_SYNC_MAX);
        }
        else
        {
          if (configuration.fx.delay_time[selected_instance_id] == DELAY_TIME_MIN)
            configuration.fx.delay_sync[selected_instance_id] = DELAY_SYNC_MIN + 1;
          else
          {
            configuration.fx.delay_time[selected_instance_id] = constrain(configuration.fx.delay_time[selected_instance_id] - ENCODER[ENC_R].speed(), DELAY_TIME_MIN, DELAY_TIME_MAX);
            MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 105, configuration.fx.delay_time[selected_instance_id]);
          }
        }
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    if (configuration.fx.delay_sync[selected_instance_id] > 0)
    {
      lcd_display_delay_sync(configuration.fx.delay_sync[selected_instance_id]);//MIDI Sync Delay
    }
    else
    {
#if DELAY_TIME_MAX >= 100
      lcd_display_bar_int("Delay Time", configuration.fx.delay_time[selected_instance_id], 10.0, DELAY_TIME_MIN, DELAY_TIME_MAX, 4, false, false, true);
#else
      lcd_display_bar_int("Delay Time", configuration.fx.delay_time[selected_instance_id], 10.0, DELAY_TIME_MIN, DELAY_TIME_MAX, 3, false, false, true);
#endif
      if (configuration.fx.delay_time[selected_instance_id] <= DELAY_TIME_MIN)
        delay_fx[selected_instance_id]->disable(0);
      else
        delay_fx[selected_instance_id]->delay(0, constrain(configuration.fx.delay_time[selected_instance_id], DELAY_TIME_MIN, DELAY_TIME_MAX) * 10);
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_time[0]), configuration.fx.delay_time[0]);
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_sync[0]), configuration.fx.delay_sync[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_time[1]), configuration.fx.delay_time[1]);
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_sync[1]), configuration.fx.delay_sync[1]);
#endif
  }
}

void UI_func_delay_feedback(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Delay Feedb.", configuration.fx.delay_feedback[selected_instance_id], 1.0, DELAY_FEEDBACK_MIN, DELAY_FEEDBACK_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.delay_feedback[selected_instance_id] = constrain(configuration.fx.delay_feedback[selected_instance_id] + ENCODER[ENC_R].speed(), DELAY_FEEDBACK_MIN, DELAY_FEEDBACK_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 106, configuration.fx.delay_feedback[selected_instance_id]);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.delay_feedback[selected_instance_id] = constrain(configuration.fx.delay_feedback[selected_instance_id] - ENCODER[ENC_R].speed(), DELAY_FEEDBACK_MIN, DELAY_FEEDBACK_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 106, configuration.fx.delay_feedback[selected_instance_id]);
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Delay Feedb.", configuration.fx.delay_feedback[selected_instance_id], 1.0, DELAY_FEEDBACK_MIN, DELAY_FEEDBACK_MAX, 3, false, false, false);

    delay_fb_mixer[selected_instance_id]->gain(1, midi_volume_transform(map(configuration.fx.delay_feedback[selected_instance_id], DELAY_FEEDBACK_MIN, DELAY_FEEDBACK_MAX, 0, 127))); // amount of feedback
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_feedback[0]), configuration.fx.delay_feedback[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_feedback[1]), configuration.fx.delay_feedback[1]);
#endif
  }
}

void UI_func_delay_level(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Delay Lvl.", configuration.fx.delay_level[selected_instance_id], 1.0, DELAY_LEVEL_MIN, DELAY_LEVEL_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.delay_level[selected_instance_id] = constrain(configuration.fx.delay_level[selected_instance_id] + ENCODER[ENC_R].speed(), DELAY_LEVEL_MIN, DELAY_LEVEL_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 107, configuration.fx.delay_level[selected_instance_id]);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.delay_level[selected_instance_id] = constrain(configuration.fx.delay_level[selected_instance_id] - ENCODER[ENC_R].speed(), DELAY_LEVEL_MIN, DELAY_LEVEL_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 107, configuration.fx.delay_level[selected_instance_id]);
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Delay Lvl.", configuration.fx.delay_level[selected_instance_id], 1.0, DELAY_LEVEL_MIN, DELAY_LEVEL_MAX, 3, false, false, false);

    delay_mixer[selected_instance_id]->gain(1, midi_volume_transform(map(configuration.fx.delay_level[selected_instance_id], DELAY_LEVEL_MIN, DELAY_LEVEL_MAX, 0, 127)));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_level[0]), configuration.fx.delay_level[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.delay_level[1]), configuration.fx.delay_level[1]);
#endif
  }
}

void UI_func_reverb_send(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Reverb Send", configuration.fx.reverb_send[selected_instance_id], 1.0, REVERB_SEND_MIN, REVERB_SEND_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.reverb_send[selected_instance_id] = constrain(configuration.fx.reverb_send[selected_instance_id] + ENCODER[ENC_R].speed(), REVERB_SEND_MIN, REVERB_SEND_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 91, configuration.fx.reverb_send[selected_instance_id]);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.reverb_send[selected_instance_id] = constrain(configuration.fx.reverb_send[selected_instance_id] - ENCODER[ENC_R].speed(), REVERB_SEND_MIN, REVERB_SEND_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 91, configuration.fx.reverb_send[selected_instance_id]);
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Reverb Send", configuration.fx.reverb_send[selected_instance_id], 1.0, REVERB_SEND_MIN, REVERB_SEND_MAX, 3, false, false, false);

    reverb_mixer_r.gain(selected_instance_id, volume_transform(mapfloat(configuration.fx.reverb_send[selected_instance_id], REVERB_SEND_MIN, REVERB_SEND_MAX, 0.0, VOL_MAX_FLOAT)));
    reverb_mixer_l.gain(selected_instance_id, volume_transform(mapfloat(configuration.fx.reverb_send[selected_instance_id], REVERB_SEND_MIN, REVERB_SEND_MAX, 0.0, VOL_MAX_FLOAT)));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_send[0]), configuration.fx.reverb_send[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.reverb_send[1]), configuration.fx.reverb_send[1]);
#endif
  }
}

void UI_func_filter_cutoff(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Filter Cutoff", configuration.fx.filter_cutoff[selected_instance_id], 1.0, FILTER_CUTOFF_MIN, FILTER_CUTOFF_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.filter_cutoff[selected_instance_id] = constrain(configuration.fx.filter_cutoff[selected_instance_id] + ENCODER[ENC_R].speed(), FILTER_CUTOFF_MIN, FILTER_CUTOFF_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 104, configuration.fx.filter_cutoff[selected_instance_id]);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.filter_cutoff[selected_instance_id] = constrain(configuration.fx.filter_cutoff[selected_instance_id] - ENCODER[ENC_R].speed(), FILTER_CUTOFF_MIN, FILTER_CUTOFF_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 104, configuration.fx.filter_cutoff[selected_instance_id]);
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Filter Cutoff", configuration.fx.filter_cutoff[selected_instance_id], 1.0, FILTER_CUTOFF_MIN, FILTER_CUTOFF_MAX, 3, false, false, false);

    MicroDexed[selected_instance_id]->setFilterCutoff(mapfloat(configuration.fx.filter_cutoff[selected_instance_id], FILTER_CUTOFF_MIN, FILTER_CUTOFF_MAX, 1.0, 0.0));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.filter_cutoff[0]), configuration.fx.filter_cutoff[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.filter_cutoff[1]), configuration.fx.filter_cutoff[1]);
#endif
  }
}

void UI_func_filter_resonance(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Filter Reso.", configuration.fx.filter_resonance[selected_instance_id], 1.0, FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.filter_resonance[selected_instance_id] = constrain(configuration.fx.filter_resonance[selected_instance_id] + ENCODER[ENC_R].speed(), FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 103, configuration.fx.filter_resonance[selected_instance_id]);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.filter_resonance[selected_instance_id] = constrain(configuration.fx.filter_resonance[selected_instance_id] - ENCODER[ENC_R].speed(), FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 103, configuration.fx.filter_resonance[selected_instance_id]);
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Filter Reso.", configuration.fx.filter_resonance[selected_instance_id], 1.0, FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX, 3, false, false, false);

    MicroDexed[selected_instance_id]->setFilterResonance(mapfloat(configuration.fx.filter_resonance[selected_instance_id], FILTER_RESONANCE_MIN, FILTER_RESONANCE_MAX, 1.0, 0.0));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.filter_resonance[0]), configuration.fx.filter_resonance[0]);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.filter_resonance[1]), configuration.fx.filter_resonance[1]);
#endif
  }
}
#endif

void UI_func_transpose(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(METERBAR);
    lcd_display_meter_int("Transpose", configuration.dexed[selected_instance_id].transpose, 1.0, -24.0, TRANSPOSE_MIN, TRANSPOSE_MAX, 2, false, true, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].transpose = constrain(configuration.dexed[selected_instance_id].transpose + ENCODER[ENC_R].speed(), TRANSPOSE_MIN, TRANSPOSE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].transpose = constrain(configuration.dexed[selected_instance_id].transpose - ENCODER[ENC_R].speed(), TRANSPOSE_MIN, TRANSPOSE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_meter_int("Transpose", configuration.dexed[selected_instance_id].transpose, 1.0, -24.0, TRANSPOSE_MIN, TRANSPOSE_MAX, 2, false, true, true);

    MicroDexed[selected_instance_id]->setTranspose(configuration.dexed[selected_instance_id].transpose);
    MicroDexed[selected_instance_id]->notesOff();
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 144, configuration.dexed[selected_instance_id].transpose, 0);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].transpose), configuration.dexed[0].transpose);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].transpose), configuration.dexed[1].transpose);
#endif
  }
}

void UI_func_tune(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(METERBAR);
    lcd_display_meter_int("Fine Tune", configuration.dexed[selected_instance_id].tune, 1.0, -100.0, TUNE_MIN, TUNE_MAX, 3, false, true, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.dexed[selected_instance_id].tune = constrain(configuration.dexed[selected_instance_id].tune + ENCODER[ENC_R].speed(), TUNE_MIN, TUNE_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 94, configuration.dexed[selected_instance_id].tune);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.dexed[selected_instance_id].tune = constrain(configuration.dexed[selected_instance_id].tune - ENCODER[ENC_R].speed(), TUNE_MIN, TUNE_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 94, configuration.dexed[selected_instance_id].tune);
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_meter_int("Fine Tune", configuration.dexed[selected_instance_id].tune, 1.0, -100.0, TUNE_MIN, TUNE_MAX, 3, false, true, false);

    MicroDexed[selected_instance_id]->setMasterTune((int((configuration.dexed[selected_instance_id].tune - 100) / 100.0 * 0x4000) << 11) * (1.0 / 12));
    MicroDexed[selected_instance_id]->doRefreshVoice();
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].tune), configuration.dexed[0].tune);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].tune), configuration.dexed[1].tune);
#endif
  }
}

void UI_func_midi_channel(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("MIDI Channel"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (LCDML.BT_checkDown() && encoderDir[ENC_R].Down())
      configuration.dexed[selected_instance_id].midi_channel = constrain(configuration.dexed[selected_instance_id].midi_channel + ENCODER[ENC_R].speed(), MIDI_CHANNEL_MIN, MIDI_CHANNEL_MAX);
    else if (LCDML.BT_checkUp() && encoderDir[ENC_R].Up())
      configuration.dexed[selected_instance_id].midi_channel = constrain(configuration.dexed[selected_instance_id].midi_channel - ENCODER[ENC_R].speed(), MIDI_CHANNEL_MIN, MIDI_CHANNEL_MAX);
#if NUM_DEXED > 1
    else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {
      selected_instance_id = !selected_instance_id;
      lcd_active_instance_number(selected_instance_id);
      UI_update_instance_icons();
    }
#endif

    lcd.setCursor(0, 1);
    if (configuration.dexed[selected_instance_id].midi_channel == 0)
    {
      lcd.print(F("[OMNI]"));
    }
    else
    {
      lcd_display_int(configuration.dexed[selected_instance_id].midi_channel, 4, false, true, false);
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].midi_channel), configuration.dexed[0].midi_channel);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].midi_channel), configuration.dexed[1].midi_channel);
#endif
  }
}

void getNoteName(char* noteName, uint8_t noteNumber)
{
  char notes [12][3] = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
  uint8_t oct_index = noteNumber - 12;

  noteNumber -= 21;
  sprintf(noteName, "%2s%1d", notes[noteNumber % 12], oct_index / 12);
}

void UI_func_lowest_note(uint8_t param)
{
  char note_name[4];

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    getNoteName(note_name, configuration.dexed[selected_instance_id].lowest_note);
    lcd.setCursor(0, 0);
    lcd.print(F("Lowest Note"));
    lcd.setCursor(0, 1);
    lcd.print(F("["));
    lcd.print(note_name);
    lcd.print(F("]"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].lowest_note = constrain(configuration.dexed[selected_instance_id].lowest_note + ENCODER[ENC_R].speed(), INSTANCE_LOWEST_NOTE_MIN, INSTANCE_LOWEST_NOTE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].lowest_note = constrain(configuration.dexed[selected_instance_id].lowest_note - ENCODER[ENC_R].speed(), INSTANCE_LOWEST_NOTE_MIN, INSTANCE_LOWEST_NOTE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    getNoteName(note_name, configuration.dexed[selected_instance_id].lowest_note);
    lcd.setCursor(1, 1);
    lcd.print(note_name);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].lowest_note), configuration.dexed[0].lowest_note);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].lowest_note), configuration.dexed[1].lowest_note);
#endif
  }
}

void UI_func_highest_note(uint8_t param)
{
  char note_name[4];

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    getNoteName(note_name, configuration.dexed[selected_instance_id].highest_note);
    lcd.setCursor(0, 0);
    lcd.print(F("Highest Note"));
    lcd.setCursor(0, 1);
    lcd.print(F("["));
    lcd.print(note_name);
    lcd.print(F("]"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].highest_note = constrain(configuration.dexed[selected_instance_id].highest_note + ENCODER[ENC_R].speed(), INSTANCE_HIGHEST_NOTE_MIN, INSTANCE_HIGHEST_NOTE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].highest_note = constrain(configuration.dexed[selected_instance_id].highest_note - ENCODER[ENC_R].speed(), INSTANCE_HIGHEST_NOTE_MIN, INSTANCE_HIGHEST_NOTE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    getNoteName(note_name, configuration.dexed[selected_instance_id].highest_note);
    lcd.setCursor(1, 1);
    lcd.print(note_name);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].highest_note), configuration.dexed[0].highest_note);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].highest_note), configuration.dexed[1].highest_note);
#endif
  }
}

void UI_func_sound_intensity(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Voice Level", configuration.dexed[selected_instance_id].sound_intensity, 1.0, SOUND_INTENSITY_MIN, SOUND_INTENSITY_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      encoderDir[ENC_R].reset();

      if (LCDML.BT_checkDown())
      {
        configuration.dexed[selected_instance_id].sound_intensity = constrain(configuration.dexed[selected_instance_id].sound_intensity + ENCODER[ENC_R].speed(), SOUND_INTENSITY_MIN, SOUND_INTENSITY_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 7, configuration.dexed[selected_instance_id].sound_intensity);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.dexed[selected_instance_id].sound_intensity = constrain(configuration.dexed[selected_instance_id].sound_intensity - ENCODER[ENC_R].speed(), SOUND_INTENSITY_MIN, SOUND_INTENSITY_MAX);
        MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 7, configuration.dexed[selected_instance_id].sound_intensity);
      }

#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Voice Level", configuration.dexed[selected_instance_id].sound_intensity, 1.0, SOUND_INTENSITY_MIN, SOUND_INTENSITY_MAX, 3, false, false, false);
    MicroDexed[selected_instance_id]->setGain(midi_volume_transform(map(configuration.dexed[selected_instance_id].sound_intensity, SOUND_INTENSITY_MIN, SOUND_INTENSITY_MAX, 0, 127)));
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].sound_intensity), configuration.dexed[0].sound_intensity);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].sound_intensity), configuration.dexed[1].sound_intensity);
#endif
  }
}

void UI_func_panorama(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    if (configuration.sys.mono > 0)
    {
      lcd.setCursor(0, 0);
      lcd.print(F("Panorama"));
      lcd.setCursor(0, 1);
      lcd.print(F("MONO-disabled"));
      return;
    }
    lcd_special_chars(METERBAR);
    lcd_display_meter_float("Panorama", configuration.dexed[selected_instance_id].pan, 0.05, -20.0, PANORAMA_MIN, PANORAMA_MAX, 1, 1, false, true, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (LCDML.BT_checkDown() && encoderDir[ENC_R].Down() && configuration.sys.mono == 0)
    {
      configuration.dexed[selected_instance_id].pan = constrain(configuration.dexed[selected_instance_id].pan + ENCODER[ENC_R].speed(), PANORAMA_MIN, PANORAMA_MAX);
      MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 10, map(configuration.dexed[selected_instance_id].pan, PANORAMA_MIN, PANORAMA_MAX, 0, 127));
    }
    else if (LCDML.BT_checkUp() && encoderDir[ENC_R].Up() && configuration.sys.mono == 0)
    {
      configuration.dexed[selected_instance_id].pan = constrain(configuration.dexed[selected_instance_id].pan - ENCODER[ENC_R].speed(), PANORAMA_MIN, PANORAMA_MAX);
      MD_sendControlChange(configuration.dexed[selected_instance_id].midi_channel, 10, map(configuration.dexed[selected_instance_id].pan, PANORAMA_MIN, PANORAMA_MAX, 0, 127));
    }
#if NUM_DEXED > 1
    else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {
      selected_instance_id = !selected_instance_id;
      lcd_active_instance_number(selected_instance_id);
      UI_update_instance_icons();
    }
#endif

    if (configuration.sys.mono == 0)
    {
      lcd_display_meter_float("Panorama", configuration.dexed[selected_instance_id].pan, 0.05, -20.0, PANORAMA_MIN, PANORAMA_MAX, 1, 1, false, true, false);
      mono2stereo[selected_instance_id]->panorama(mapfloat(configuration.dexed[selected_instance_id].pan, PANORAMA_MIN, PANORAMA_MAX, -1.0, 1.0));
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].pan), configuration.dexed[0].pan);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].pan), configuration.dexed[1].pan);
#endif
  }
}

void UI_func_favorites(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_L].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Favorites"));
    lcd.setCursor(0, 1);
    switch (configuration.sys.favorites)
    {
      case 0:
        lcd.print(F("[ All  presets ]"));
        break;
      case 1:
        lcd.print(F("[  FAVs. only  ]"));
        break;
      case 2:
        lcd.print(F("[non-FAVs. only]"));
        break;
      case 3:
        lcd.print(F("[random non-FAV]"));
        break;
    }
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (LCDML.BT_checkDown())
      configuration.sys.favorites = constrain(configuration.sys.favorites + 1, 0, 3);
    else if (LCDML.BT_checkUp())
      configuration.sys.favorites = constrain(configuration.sys.favorites - 1, 0, 3);

    lcd.setCursor(0, 1);
    switch (configuration.sys.favorites)
    {
      case 0:
        lcd.print(F("[ All  presets ]"));
        break;
      case 1:
        lcd.print(F("[  FAVs. only  ]"));
        break;
      case 2:
        lcd.print(F("[non-FAVs. only]"));
        break;
      case 3:
        lcd.print(F("[random non-FAV]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_L].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, sys.favorites), configuration.sys.favorites);
  }
}


void UI_func_stereo_mono(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Stereo/Mono"));
    lcd.setCursor(0, 1);
    switch (configuration.sys.mono)
    {
      case 0:
        lcd.print(F("[STEREO]"));
        stereo2mono.stereo(true);
        break;
      case 1:
        lcd.print(F("[MONO  ]"));
        stereo2mono.stereo(false);
        break;
      case 2:
        lcd.print(F("[MONO-R]"));
        stereo2mono.stereo(false);
        break;
      case 3:
        lcd.print(F("[MONO-L]"));
        stereo2mono.stereo(false);
        break;
    }
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (LCDML.BT_checkDown())
      configuration.sys.mono = constrain(configuration.sys.mono + 1, MONO_MIN, MONO_MAX);
    else if (LCDML.BT_checkUp())
      configuration.sys.mono = constrain(configuration.sys.mono - 1, MONO_MIN, MONO_MAX);

    lcd.setCursor(0, 1);
    switch (configuration.sys.mono)
    {
      case 0:
        lcd.print(F("[STEREO]"));
        stereo2mono.stereo(true);
        break;
      case 1:
        lcd.print(F("[MONO  ]"));
        stereo2mono.stereo(false);
        break;
      case 2:
        lcd.print(F("[MONO-R]"));
        stereo2mono.stereo(false);
        break;
      case 3:
        lcd.print(F("[MONO-L]"));
        stereo2mono.stereo(false);
        break;
    }
    set_volume(configuration.sys.vol, configuration.sys.mono);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, sys.mono), configuration.sys.mono);
  }
}

void UI_func_polyphony(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
#if NUM_DEXED>1
    lcd_display_bar_int("Polyphony", configuration.dexed[selected_instance_id].polyphony, 1.0, POLYPHONY_MIN, POLYPHONY_MAX - configuration.dexed[(selected_instance_id + 1) % NUM_DEXED].polyphony, 2, false, false, true);
#else
    lcd_display_bar_int("Polyphony", configuration.dexed[selected_instance_id].polyphony, 1.0, POLYPHONY_MIN, POLYPHONY_MAX, 2, false, false, true);
#endif
    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
#if NUM_DEXED>1
        if (configuration.dexed[selected_instance_id].polyphony < POLYPHONY_MAX - configuration.dexed[(selected_instance_id + 1) % NUM_DEXED].polyphony)
          configuration.dexed[selected_instance_id].polyphony = constrain(configuration.dexed[selected_instance_id].polyphony + 1, POLYPHONY_MIN, POLYPHONY_MAX - configuration.dexed[(selected_instance_id + 1) % NUM_DEXED].polyphony);
#else
        configuration.dexed[selected_instance_id].polyphony = constrain(configuration.dexed[selected_instance_id].polyphony + 1, POLYPHONY_MIN, POLYPHONY_MAX);
#endif
      }
      else if (LCDML.BT_checkUp())
      {
        if (configuration.dexed[selected_instance_id].polyphony - 1 < 0)
          configuration.dexed[selected_instance_id].polyphony = 0;
        else
        {
#if NUM_DEXED>1
          configuration.dexed[selected_instance_id].polyphony = constrain(configuration.dexed[selected_instance_id].polyphony - 1, POLYPHONY_MIN, POLYPHONY_MAX - configuration.dexed[(selected_instance_id + 1) % NUM_DEXED].polyphony);
#else
          configuration.dexed[selected_instance_id].polyphony = constrain(configuration.dexed[selected_instance_id].polyphony - 1, POLYPHONY_MIN, POLYPHONY_MAX);
#endif
        }
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
      lcd_active_instance_number(selected_instance_id);
#endif
    }

#if NUM_DEXED>1
    lcd_display_bar_int("Polyphony", configuration.dexed[selected_instance_id].polyphony, 1.0, POLYPHONY_MIN, POLYPHONY_MAX - configuration.dexed[(selected_instance_id + 1) % NUM_DEXED].polyphony, 2, false, false, false);
#else
    lcd_display_bar_int("Polyphony", configuration.dexed[selected_instance_id].polyphony, 1.0, POLYPHONY_MIN, POLYPHONY_MAX, 2, false, false, false);
#endif
    MicroDexed[selected_instance_id]->setMaxNotes(configuration.dexed[selected_instance_id].polyphony);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].polyphony), configuration.dexed[0].polyphony);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].polyphony), configuration.dexed[1].polyphony);
#endif
  }
}

void UI_func_mono_poly(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Mono/Poly"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].monopoly = constrain(configuration.dexed[selected_instance_id].monopoly + 1, MONOPOLY_MIN, MONOPOLY_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].monopoly = constrain(configuration.dexed[selected_instance_id].monopoly - 1, MONOPOLY_MIN, MONOPOLY_MAX);

      MicroDexed[selected_instance_id]->setMonoMode(!configuration.dexed[selected_instance_id].monopoly);
      configuration.dexed[selected_instance_id].monopoly = MicroDexed[selected_instance_id]->getMonoMode();
    }
#if NUM_DEXED > 1
    if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {
      selected_instance_id = !selected_instance_id;
      lcd_active_instance_number(selected_instance_id);
      UI_update_instance_icons();
    }
    lcd_active_instance_number(selected_instance_id);
#endif
  }

  lcd.setCursor(0, 1);
  switch (configuration.dexed[selected_instance_id].monopoly)
  {
    case 1:
      lcd.print(F("[MONOPHONIC]"));
      break;
    case 0:
      lcd.print(F("[POLYPHONIC]"));
      break;
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].monopoly), configuration.dexed[0].monopoly);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].monopoly), configuration.dexed[1].monopoly);
#endif
  }
}

void UI_func_note_refresh(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Note Refresh"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].note_refresh = constrain(configuration.dexed[selected_instance_id].note_refresh + 1, NOTE_REFRESH_MIN, NOTE_REFRESH_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].note_refresh = constrain(configuration.dexed[selected_instance_id].note_refresh - 1, NOTE_REFRESH_MIN, NOTE_REFRESH_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setRefreshMode(configuration.dexed[selected_instance_id].note_refresh);

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].note_refresh)
    {
      case 0:
        lcd.print(F("[NORMAL     ]"));
        break;
      case 1:
        lcd.print(F("[RETRIGGERED]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].note_refresh), configuration.dexed[0].note_refresh);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].note_refresh), configuration.dexed[1].note_refresh);
#endif
  }
}

void UI_func_pb_range(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("PB Range", configuration.dexed[selected_instance_id].pb_range, 1.0, PB_RANGE_MIN, PB_RANGE_MAX, 2, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].pb_range = constrain(configuration.dexed[selected_instance_id].pb_range + ENCODER[ENC_R].speed(), PB_RANGE_MIN, PB_RANGE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].pb_range = constrain(configuration.dexed[selected_instance_id].pb_range - ENCODER[ENC_R].speed(), PB_RANGE_MIN, PB_RANGE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("PB Range", configuration.dexed[selected_instance_id].pb_range, 1.0, PB_RANGE_MIN, PB_RANGE_MAX, 2, false, false, false);

    MicroDexed[selected_instance_id]->setPBController(configuration.dexed[selected_instance_id].pb_range, configuration.dexed[selected_instance_id].pb_step);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 65, configuration.dexed[selected_instance_id].pb_range, 2);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].pb_range), configuration.dexed[0].pb_range);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].pb_range), configuration.dexed[1].pb_range);
#endif
  }
}

void UI_func_pb_step(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("PB Step", configuration.dexed[selected_instance_id].pb_step, 1.0, PB_STEP_MIN, PB_STEP_MAX, 2, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].pb_step = constrain(configuration.dexed[selected_instance_id].pb_step + ENCODER[ENC_R].speed(), PB_STEP_MIN, PB_STEP_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].pb_step = constrain(configuration.dexed[selected_instance_id].pb_step - ENCODER[ENC_R].speed(), PB_STEP_MIN, PB_STEP_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("PB Step", configuration.dexed[selected_instance_id].pb_step, 1.0, PB_STEP_MIN, PB_STEP_MAX, 2, false, false, false);

    MicroDexed[selected_instance_id]->setPBController(configuration.dexed[selected_instance_id].pb_range, configuration.dexed[selected_instance_id].pb_step);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 66, configuration.dexed[selected_instance_id].pb_step, 2);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].pb_step), configuration.dexed[0].pb_step);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].pb_step), configuration.dexed[1].pb_step);
#endif
  }
}

void UI_func_mw_range(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("MW Range", configuration.dexed[selected_instance_id].mw_range, 1.0, MW_RANGE_MIN, MW_RANGE_MAX, 2, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].mw_range = constrain(configuration.dexed[selected_instance_id].mw_range + ENCODER[ENC_R].speed(), MW_RANGE_MIN, MW_RANGE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].mw_range = constrain(configuration.dexed[selected_instance_id].mw_range - ENCODER[ENC_R].speed(), MW_RANGE_MIN, MW_RANGE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("MW Range", configuration.dexed[selected_instance_id].mw_range, 1.0, MW_RANGE_MIN, MW_RANGE_MAX, 2, false, false, false);

    MicroDexed[selected_instance_id]->setMWController(configuration.dexed[selected_instance_id].mw_range, configuration.dexed[selected_instance_id].mw_assign, configuration.dexed[selected_instance_id].mw_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 70, configuration.dexed[selected_instance_id].mw_range, 2);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].mw_range), configuration.dexed[0].mw_range);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].mw_range), configuration.dexed[1].mw_range);
#endif
  }
}

void UI_func_mw_assign(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("MW Assign"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].mw_assign = constrain(configuration.dexed[selected_instance_id].mw_assign + 1, MW_ASSIGN_MIN, MW_ASSIGN_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].mw_assign = constrain(configuration.dexed[selected_instance_id].mw_assign - 1, MW_ASSIGN_MIN, MW_ASSIGN_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setMWController(configuration.dexed[selected_instance_id].mw_range, configuration.dexed[selected_instance_id].mw_assign, configuration.dexed[selected_instance_id].mw_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 71, configuration.dexed[selected_instance_id].mw_assign, 2);

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].mw_assign)
    {
      case 0:
        lcd.print(F("[   NONE    ]"));
        break;
      case 1:
        lcd.print(F("[PTCH       ]"));
        break;
      case 2:
        lcd.print(F("[     AMP   ]"));
        break;
      case 3:
        lcd.print(F("[PTCH AMP   ]"));
        break;
      case 4:
        lcd.print(F("[         EG]"));
        break;
      case 5:
        lcd.print(F("[PTCH     EG]"));
        break;
      case 6:
        lcd.print(F("[     AMP EG]"));
        break;
      case 7:
        lcd.print(F("[PTCH AMP EG]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].mw_assign), configuration.dexed[0].mw_assign);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].mw_assign), configuration.dexed[1].mw_assign);
#endif
  }
}

void UI_func_mw_mode(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("MW Mode"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].mw_mode = constrain(configuration.dexed[selected_instance_id].mw_mode + 1, MW_MODE_MIN, MW_MODE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].mw_mode = constrain(configuration.dexed[selected_instance_id].mw_mode - 1, MW_MODE_MIN, MW_MODE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setMWController(configuration.dexed[selected_instance_id].mw_range, configuration.dexed[selected_instance_id].mw_assign, configuration.dexed[selected_instance_id].mw_mode);
    MicroDexed[selected_instance_id]->ControllersRefresh();

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].mw_mode)
    {
      case 0:
        lcd.print(F("[LINEAR      ]"));
        break;
      case 1:
        lcd.print(F("[REVERSE LIN.]"));
        break;
      case 2:
        lcd.print(F("[DIRECT      ]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].mw_mode), configuration.dexed[0].mw_mode);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].mw_mode), configuration.dexed[1].mw_mode);
#endif
  }
}

void UI_func_fc_range(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("FC Range", configuration.dexed[selected_instance_id].fc_range, 1.0, FC_RANGE_MIN, FC_RANGE_MAX, 2, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].fc_range = constrain(configuration.dexed[selected_instance_id].fc_range + ENCODER[ENC_R].speed(), FC_RANGE_MIN, FC_RANGE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].fc_range = constrain(configuration.dexed[selected_instance_id].fc_range - ENCODER[ENC_R].speed(), FC_RANGE_MIN, FC_RANGE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("FC Range", configuration.dexed[selected_instance_id].fc_range, 1.0, FC_RANGE_MIN, FC_RANGE_MAX, 2, false, false, false);

    MicroDexed[selected_instance_id]->setFCController(configuration.dexed[selected_instance_id].fc_range, configuration.dexed[selected_instance_id].fc_assign, configuration.dexed[selected_instance_id].fc_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 72, configuration.dexed[selected_instance_id].fc_range, 2);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].fc_range), configuration.dexed[0].fc_range);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].fc_range), configuration.dexed[1].fc_range);
#endif
  }
}

void UI_func_fc_assign(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("FC Assign"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].fc_assign = constrain(configuration.dexed[selected_instance_id].fc_assign + 1, FC_ASSIGN_MIN, FC_ASSIGN_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].fc_assign = constrain(configuration.dexed[selected_instance_id].fc_assign - 1, FC_ASSIGN_MIN, FC_ASSIGN_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setFCController(configuration.dexed[selected_instance_id].fc_range, configuration.dexed[selected_instance_id].fc_assign, configuration.dexed[selected_instance_id].fc_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 73, configuration.dexed[selected_instance_id].fc_assign, 2);

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].fc_assign)
    {
      case 0:
        lcd.print(F("[   NONE    ]"));
        break;
      case 1:
        lcd.print(F("[PTCH       ]"));
        break;
      case 2:
        lcd.print(F("[     AMP   ]"));
        break;
      case 3:
        lcd.print(F("[PTCH AMP   ]"));
        break;
      case 4:
        lcd.print(F("[         EG]"));
        break;
      case 5:
        lcd.print(F("[PTCH     EG]"));
        break;
      case 6:
        lcd.print(F("[     AMP EG]"));
        break;
      case 7:
        lcd.print(F("[PTCH AMP EG]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].fc_assign), configuration.dexed[0].fc_assign);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].fc_assign), configuration.dexed[1].fc_assign);
#endif
  }
}

void UI_func_fc_mode(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("FC Mode"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].fc_mode = constrain(configuration.dexed[selected_instance_id].fc_mode + 1, FC_MODE_MIN, FC_MODE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].fc_mode = constrain(configuration.dexed[selected_instance_id].fc_mode - 1, FC_MODE_MIN, FC_MODE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setFCController(configuration.dexed[selected_instance_id].fc_range, configuration.dexed[selected_instance_id].fc_assign, configuration.dexed[selected_instance_id].fc_mode);
    MicroDexed[selected_instance_id]->ControllersRefresh();

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].fc_mode)
    {
      case 0:
        lcd.print(F("[LINEAR      ]"));
        break;
      case 1:
        lcd.print(F("[REVERSE LIN.]"));
        break;
      case 2:
        lcd.print(F("[DIRECT      ]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].fc_mode), configuration.dexed[0].fc_mode);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].fc_mode), configuration.dexed[1].fc_mode);
#endif
  }
}

void UI_func_bc_range(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("BC Range", configuration.dexed[selected_instance_id].bc_range, 1.0, BC_RANGE_MIN, BC_RANGE_MAX, 2, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].bc_range = constrain(configuration.dexed[selected_instance_id].bc_range + ENCODER[ENC_R].speed(), BC_RANGE_MIN, BC_RANGE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].bc_range = constrain(configuration.dexed[selected_instance_id].bc_range - ENCODER[ENC_R].speed(), BC_RANGE_MIN, BC_RANGE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("BC Range", configuration.dexed[selected_instance_id].bc_range, 1.0, BC_RANGE_MIN, BC_RANGE_MAX, 2, false, false, false);

    MicroDexed[selected_instance_id]->setBCController(configuration.dexed[selected_instance_id].bc_range, configuration.dexed[selected_instance_id].bc_assign, configuration.dexed[selected_instance_id].bc_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 74, configuration.dexed[selected_instance_id].bc_range, 2);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].bc_range), configuration.dexed[0].bc_range);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].bc_range), configuration.dexed[1].bc_range);
#endif
  }
}

void UI_func_bc_assign(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("BC Assign"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].bc_assign = constrain(configuration.dexed[selected_instance_id].bc_assign + 1, BC_ASSIGN_MIN, BC_ASSIGN_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].bc_assign = constrain(configuration.dexed[selected_instance_id].bc_assign - 1, BC_ASSIGN_MIN, BC_ASSIGN_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setBCController(configuration.dexed[selected_instance_id].bc_range, configuration.dexed[selected_instance_id].bc_assign, configuration.dexed[selected_instance_id].bc_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 75, configuration.dexed[selected_instance_id].bc_assign, 2);

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].bc_assign)
    {
      case 0:
        lcd.print(F("[   NONE    ]"));
        break;
      case 1:
        lcd.print(F("[PTCH       ]"));
        break;
      case 2:
        lcd.print(F("[     AMP   ]"));
        break;
      case 3:
        lcd.print(F("[PTCH AMP   ]"));
        break;
      case 4:
        lcd.print(F("[         EG]"));
        break;
      case 5:
        lcd.print(F("[PTCH     EG]"));
        break;
      case 6:
        lcd.print(F("[     AMP EG]"));
        break;
      case 7:
        lcd.print(F("[PTCH AMP EG]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].bc_assign), configuration.dexed[0].bc_assign);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].bc_assign), configuration.dexed[1].bc_assign);
#endif
  }
}

void UI_func_bc_mode(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("BC Mode"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].bc_mode = constrain(configuration.dexed[selected_instance_id].bc_mode + 1, BC_MODE_MIN, BC_MODE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].bc_mode = constrain(configuration.dexed[selected_instance_id].bc_mode - 1, BC_MODE_MIN, BC_MODE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setBCController(configuration.dexed[selected_instance_id].bc_range, configuration.dexed[selected_instance_id].bc_assign, configuration.dexed[selected_instance_id].bc_mode);
    MicroDexed[selected_instance_id]->ControllersRefresh();

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].bc_mode)
    {
      case 0:
        lcd.print(F("[LINEAR      ]"));
        break;
      case 1:
        lcd.print(F("[REVERSE LIN.]"));
        break;
      case 2:
        lcd.print(F("[DIRECT      ]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].bc_mode), configuration.dexed[0].bc_mode);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].bc_mode), configuration.dexed[1].bc_mode);
#endif
  }
}

void UI_func_at_range(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("AT Range", configuration.dexed[selected_instance_id].at_range, 1.0, AT_RANGE_MIN, AT_RANGE_MAX, 2, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].at_range = constrain(configuration.dexed[selected_instance_id].at_range + ENCODER[ENC_R].speed(), AT_RANGE_MIN, AT_RANGE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].at_range = constrain(configuration.dexed[selected_instance_id].at_range - ENCODER[ENC_R].speed(), AT_RANGE_MIN, AT_RANGE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("AT Range", configuration.dexed[selected_instance_id].at_range, 1.0, AT_RANGE_MIN, AT_RANGE_MAX, 2, false, false, false);

    MicroDexed[selected_instance_id]->setATController(configuration.dexed[selected_instance_id].at_range, configuration.dexed[selected_instance_id].at_assign, configuration.dexed[selected_instance_id].at_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 76, configuration.dexed[selected_instance_id].at_range, 2);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].at_range), configuration.dexed[0].at_range);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].at_range), configuration.dexed[1].at_range);
#endif
  }
}

void UI_func_at_assign(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("AT Assign"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].at_assign = constrain(configuration.dexed[selected_instance_id].at_assign + 1, AT_ASSIGN_MIN, AT_ASSIGN_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].at_assign = constrain(configuration.dexed[selected_instance_id].at_assign - 1, AT_ASSIGN_MIN, AT_ASSIGN_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setATController(configuration.dexed[selected_instance_id].at_range, configuration.dexed[selected_instance_id].at_assign, configuration.dexed[selected_instance_id].at_mode);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 77, configuration.dexed[selected_instance_id].at_assign, 2);

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].at_assign)
    {
      case 0:
        lcd.print(F("[   NONE    ]"));
        break;
      case 1:
        lcd.print(F("[PTCH       ]"));
        break;
      case 2:
        lcd.print(F("[     AMP   ]"));
        break;
      case 3:
        lcd.print(F("[PTCH AMP   ]"));
        break;
      case 4:
        lcd.print(F("[         EG]"));
        break;
      case 5:
        lcd.print(F("[PTCH     EG]"));
        break;
      case 6:
        lcd.print(F("[     AMP EG]"));
        break;
      case 7:
        lcd.print(F("[PTCH AMP EG]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].at_assign), configuration.dexed[0].at_assign);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].at_assign), configuration.dexed[1].at_assign);
#endif
  }
}

void UI_func_at_mode(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("AT Mode"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].at_mode = constrain(configuration.dexed[selected_instance_id].at_mode + 1, AT_MODE_MIN, AT_MODE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].at_mode = constrain(configuration.dexed[selected_instance_id].at_mode - 1, AT_MODE_MIN, AT_MODE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setATController(configuration.dexed[selected_instance_id].at_range, configuration.dexed[selected_instance_id].at_assign, configuration.dexed[selected_instance_id].at_mode);
    MicroDexed[selected_instance_id]->ControllersRefresh();

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].at_mode)
    {
      case 0:
        lcd.print(F("[LINEAR      ]"));
        break;
      case 1:
        lcd.print(F("[REVERSE LIN.]"));
        break;
      case 2:
        lcd.print(F("[DIRECT      ]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].at_mode), configuration.dexed[0].at_mode);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].at_mode), configuration.dexed[1].at_mode);
#endif
  }
}

void UI_func_portamento_mode(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Port. Mode"));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].portamento_mode = constrain(configuration.dexed[selected_instance_id].portamento_mode + 1, PORTAMENTO_MODE_MIN, PORTAMENTO_MODE_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].portamento_mode = constrain(configuration.dexed[selected_instance_id].portamento_mode - 1, PORTAMENTO_MODE_MIN, PORTAMENTO_MODE_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setPortamentoMode(configuration.dexed[selected_instance_id].portamento_mode, configuration.dexed[selected_instance_id].portamento_glissando, configuration.dexed[selected_instance_id].portamento_time);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 67, configuration.dexed[selected_instance_id].portamento_mode, 2);

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].portamento_mode)
    {
      case 0:
        if (configuration.dexed[selected_instance_id].monopoly == 1)
          lcd.print(F("[RETAIN  ]"));
        else
          lcd.print(F("[FINGERED]"));
        break;
      case 1:
        if (configuration.dexed[selected_instance_id].monopoly == 1)
          lcd.print(F("[FOLLOW  ]"));
        else
          lcd.print(F("[FULL    ]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].portamento_mode), configuration.dexed[0].portamento_mode);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].portamento_mode), configuration.dexed[1].portamento_mode);
#endif
  }
}

void UI_func_portamento_glissando(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Port. Gliss."));

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].portamento_glissando = constrain(configuration.dexed[selected_instance_id].portamento_glissando + 1, PORTAMENTO_GLISSANDO_MIN, PORTAMENTO_GLISSANDO_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].portamento_glissando = constrain(configuration.dexed[selected_instance_id].portamento_glissando - 1, PORTAMENTO_GLISSANDO_MIN, PORTAMENTO_GLISSANDO_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    MicroDexed[selected_instance_id]->setPortamentoMode(configuration.dexed[selected_instance_id].portamento_mode, configuration.dexed[selected_instance_id].portamento_glissando, configuration.dexed[selected_instance_id].portamento_time);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 68, configuration.dexed[selected_instance_id].portamento_glissando, 2);

    lcd.setCursor(0, 1);
    switch (configuration.dexed[selected_instance_id].portamento_glissando)
    {
      case 0:
        lcd.print(F("[OFF]"));
        break;
      case 1:
        lcd.print(F("[ON ]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].portamento_glissando), configuration.dexed[0].portamento_glissando);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].portamento_glissando), configuration.dexed[1].portamento_glissando);
#endif
  }
}

void UI_func_portamento_time(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Port. Time", configuration.dexed[selected_instance_id].portamento_time, 1.0, PORTAMENTO_TIME_MIN, PORTAMENTO_TIME_MAX, 2, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].portamento_time = constrain(configuration.dexed[selected_instance_id].portamento_time + ENCODER[ENC_R].speed(), PORTAMENTO_TIME_MIN, PORTAMENTO_TIME_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].portamento_time = constrain(configuration.dexed[selected_instance_id].portamento_time - ENCODER[ENC_R].speed(), PORTAMENTO_TIME_MIN, PORTAMENTO_TIME_MAX);
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    lcd_display_bar_int("Portam. Time", configuration.dexed[selected_instance_id].portamento_time, 1.0, PORTAMENTO_TIME_MIN, PORTAMENTO_TIME_MAX, 2, false, false, false);

    MicroDexed[selected_instance_id]->setPortamentoMode(configuration.dexed[selected_instance_id].portamento_mode, configuration.dexed[selected_instance_id].portamento_glissando, configuration.dexed[selected_instance_id].portamento_time);
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 69, configuration.dexed[selected_instance_id].portamento_time, 2);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].portamento_time), configuration.dexed[0].portamento_time);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].portamento_time), configuration.dexed[1].portamento_time);
#endif
  }
}

void UI_handle_OP(uint8_t param)
{
  static uint8_t op_selected;

  lcd_OP_active_instance_number(selected_instance_id, configuration.dexed[selected_instance_id].op_enabled);

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("OP Enable"));
    lcd.setCursor(0, 1);
    for (uint8_t i = 2; i < 8; i++)
      lcd.write(i);

    UI_update_instance_icons();

    lcd.setCursor(op_selected, 1);
    lcd.blink();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (LCDML.BT_checkUp() && encoderDir[ENC_R].Up())
    {
#if NUM_DEXED>1
      if (op_selected == 0)
      {
        selected_instance_id = !selected_instance_id;
        op_selected = 5;
        lcd_OP_active_instance_number(selected_instance_id, configuration.dexed[selected_instance_id].op_enabled);
      }
      else
#endif
        op_selected = constrain(op_selected - 1, 0, 5);
    }
    else if (LCDML.BT_checkDown() && encoderDir[ENC_R].Down())
    {
#if NUM_DEXED>1
      if (op_selected == 5)
      {
        selected_instance_id = !selected_instance_id;
        op_selected = 0;
        lcd_OP_active_instance_number(selected_instance_id, configuration.dexed[selected_instance_id].op_enabled);
      }
      else
#endif
        op_selected = constrain(op_selected + 1, 0, 5);
    }
    else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {
      if (bitRead(configuration.dexed[selected_instance_id].op_enabled, op_selected))
        bitClear(configuration.dexed[selected_instance_id].op_enabled, op_selected);
      else
        bitSet(configuration.dexed[selected_instance_id].op_enabled, op_selected);

      lcd_OP_active_instance_number(selected_instance_id, configuration.dexed[selected_instance_id].op_enabled);
    }

    lcd.setCursor(op_selected, 1);

    MicroDexed[selected_instance_id]->setOPAll(configuration.dexed[selected_instance_id].op_enabled);
    MicroDexed[selected_instance_id]->doRefreshVoice();
    send_sysex_param(configuration.dexed[selected_instance_id].midi_channel, 155, configuration.dexed[selected_instance_id].op_enabled, 0);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd.noBlink();
    lcd.noCursor();
    lcd_special_chars(SCROLLBAR);

    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].op_enabled), configuration.dexed[0].op_enabled);
#if NUM_DEXED > 1
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].op_enabled), configuration.dexed[1].op_enabled);
#endif
  }
}

void UI_func_drum_reverb_send(uint8_t param)
{
  char displayname[8] = {0, 0, 0, 0, 0, 0, 0};
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    temp_int = (int)(drum_config[activesample].reverb_send * 100);
    lcd.setCursor(0, 0);
    lcd.print("Drum Rev. Send");
    lcd.setCursor(1, 1);
    sprintf(displayname, "%02d", activesample);
    lcd.print(displayname);
    lcd.show(4, 5, 6, basename(drum_config[activesample].name));
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (menu_select_toggle == false) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          // activesample = constrain(activesample + ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
          smart_filter(1);
        }
        else if (LCDML.BT_checkUp())
        {
          // activesample = constrain(activesample - ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
          smart_filter(0);
        }
      }
    } else {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          temp_int = constrain(temp_int + ENCODER[ENC_R].speed(), 0, REVERB_SEND_MAX);
        }
        else if (LCDML.BT_checkUp())
        {
          temp_int = constrain(temp_int - ENCODER[ENC_R].speed(), 0, REVERB_SEND_MAX);
        }
      }
    }
    if (LCDML.BT_checkEnter())
    {
      if (menu_select_toggle) {
        menu_select_toggle = false;
      } else
      { menu_select_toggle = true;
        temp_int = (int)(drum_config[activesample].reverb_send * 100);
      }
    }
    if (menu_select_toggle == false) {
      lcd.setCursor(11, 1);
      lcd.print(" ");
      lcd.setCursor(15, 1);
      lcd.print(" ");
      lcd.setCursor(0, 1);
      lcd.print("[");
      lcd.setCursor(3, 1);
      lcd.print("]");
      lcd.setCursor(1, 1);
      sprintf(displayname, "%02d", activesample);
      lcd.print(displayname);
      lcd.show(4, 5, 6, basename(drum_config[activesample].name));
      sprintf(displayname, "%03d", (int)(drum_config[activesample].reverb_send * 100) );
      lcd.setCursor(12, 1);
      lcd.print(displayname);
    } else {
      lcd.setCursor(0, 1);
      lcd.print(" ");
      lcd.setCursor(3, 1);
      lcd.print(" ");
      lcd.setCursor(11, 1);
      lcd.print("[");
      lcd.setCursor(15, 1);
      lcd.print("]");
      sprintf(displayname, "%03d", temp_int);
      lcd.setCursor(12, 1);
      lcd.print(displayname);
      drum_config[activesample].reverb_send = mapfloat(temp_int, 0, 100, 0.0, 1.0);
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_drum_midi_channel(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("MIDI Channel"));
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (LCDML.BT_checkDown() && encoderDir[ENC_R].Down())
      drum_midi_channel = constrain(drum_midi_channel + ENCODER[ENC_R].speed(), MIDI_CHANNEL_MIN, MIDI_CHANNEL_MAX);
    else if (LCDML.BT_checkUp() && encoderDir[ENC_R].Up())
      drum_midi_channel = constrain(drum_midi_channel - ENCODER[ENC_R].speed(), MIDI_CHANNEL_MIN, MIDI_CHANNEL_MAX);

    lcd.setCursor(0, 1);
    if (drum_midi_channel == 0)
    {
      lcd.print(F("[OMNI]"));
    }
    else
    {
      lcd_display_int(drum_midi_channel, 4, false, true, false);
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
    //  EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].midi_channel), configuration.dexed[0].midi_channel);  }
  }
}
void UI_func_drums_main_volume(uint8_t param)
{
  char displayname[4] = {0, 0, 0, 0};
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    temp_int = mapfloat(drums_volume, 0.0, VOL_MAX_FLOAT, 0, 100);
    lcd.setCursor(0, 0);
    lcd.print(" Drums M.Volume ");
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        temp_int = constrain(temp_int + ENCODER[ENC_R].speed(), 0, 100);
      }
      else if (LCDML.BT_checkUp())
      {
        temp_int = constrain(temp_int - ENCODER[ENC_R].speed(), 0, 100);
      }
    }
    lcd.setCursor(5, 1);
    sprintf(displayname, "%03d", temp_int);
    lcd.print(displayname);
    lcd.setCursor(8, 1);
    lcd.print("/100");
    master_mixer_r.gain (2, volume_transform(mapfloat(temp_int, 0, 100, 0.0, VOL_MAX_FLOAT)));
    master_mixer_l.gain (2, volume_transform(mapfloat(temp_int, 0, 100, 0.0, VOL_MAX_FLOAT)));
    drums_volume = mapfloat(temp_int, 0, 100, 0.0, VOL_MAX_FLOAT);
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}
void UI_func_drum_pitch(uint8_t param)
{
  char displayname[8] = {0, 0, 0, 0, 0, 0, 0};
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    temp_int = (int)(drum_config[activesample].p_offset * 100);
    lcd.setCursor(0, 0);
    lcd.print("DrumSmp. Pitch");
    lcd.setCursor(1, 1);
    lcd.setCursor(1, 1);
    sprintf(displayname, "%02d", activesample);
    lcd.print(displayname);
    lcd.show(1, 4, 7, basename(drum_config[activesample].name));

  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (menu_select_toggle == false) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          activesample = constrain(activesample + ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
        }
        else if (LCDML.BT_checkUp())
        {
          activesample = constrain(activesample - ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
        }
      }
    } else {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          temp_int = constrain(temp_int + ENCODER[ENC_R].speed(), 0, 200);
        }
        else if (LCDML.BT_checkUp())
        {
          temp_int = constrain(temp_int - ENCODER[ENC_R].speed(), 0, 200);
        }
      }
    }
    if (LCDML.BT_checkEnter())
    {
      if (menu_select_toggle) {
        menu_select_toggle = false;
      } else
      { menu_select_toggle = true;
        temp_int = (int)(drum_config[activesample].p_offset * 100);
      }
    }
    if (menu_select_toggle == false) {
      lcd.setCursor(11, 1);
      lcd.print(" ");
      lcd.setCursor(15, 1);
      lcd.print(" ");
      lcd.setCursor(0, 1);
      lcd.print("[");
      lcd.setCursor(3, 1);
      lcd.print("]");
      lcd.setCursor(1, 1);
      sprintf(displayname, "%02d", activesample);
      lcd.print(displayname);
      lcd.show(1, 4, 7, basename(drum_config[activesample].name));
      sprintf(displayname, "%03d", (int)(drum_config[activesample].p_offset * 100) );
      lcd.setCursor(12, 1);
      lcd.print(displayname);
    } else {
      temp_float = mapfloat(temp_int, 0, 200, 0.0, 2.0);
      lcd.setCursor(0, 1);
      lcd.print(" ");
      lcd.setCursor(3, 1);
      lcd.print(" ");
      lcd.setCursor(11, 1);
      lcd.print("[");
      lcd.setCursor(15, 1);
      lcd.print("]");
      sprintf(displayname, "%03d", temp_int);
      lcd.setCursor(12, 1);
      lcd.print(displayname);
      drum_config[activesample].p_offset = temp_float;
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_drum_volume(uint8_t param)
{
  char displayname[8] = {0, 0, 0, 0, 0, 0, 0};
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    temp_int = (int)(drum_config[activesample].vol_max * 100);
    lcd.setCursor(0, 0);
    lcd.print("DrumSmp. Volume");
    lcd.setCursor(1, 1);
    lcd.setCursor(1, 1);
    sprintf(displayname, "%02d", activesample);
    lcd.print(displayname);
    lcd.show(1, 4, 7, basename(drum_config[activesample].name));

  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (menu_select_toggle == false) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          // activesample = constrain(activesample + ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
          smart_filter(1);
        }
        else if (LCDML.BT_checkUp())
        {
          //  activesample = constrain(activesample - ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
          smart_filter(0);
        }
      }
    } else {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          temp_int = constrain(temp_int + ENCODER[ENC_R].speed(), 0, 100);
        }
        else if (LCDML.BT_checkUp())
        {
          temp_int = constrain(temp_int - ENCODER[ENC_R].speed(), 0, 100);
        }
      }
    }
    if (LCDML.BT_checkEnter())
    {
      if (menu_select_toggle) {
        menu_select_toggle = false;
      } else
      { menu_select_toggle = true;
        temp_int = (int)(drum_config[activesample].vol_max * 100);
      }
    }
    if (menu_select_toggle == false) {
      lcd.setCursor(11, 1);
      lcd.print(" ");
      lcd.setCursor(15, 1);
      lcd.print(" ");
      lcd.setCursor(0, 1);
      lcd.print("[");
      lcd.setCursor(3, 1);
      lcd.print("]");
      lcd.setCursor(1, 1);
      sprintf(displayname, "%02d", activesample);
      lcd.print(displayname);
      lcd.show(1, 4, 7, basename(drum_config[activesample].name));
      sprintf(displayname, "%03d", (int)(drum_config[activesample].vol_max * 100) );
      lcd.setCursor(12, 1);
      lcd.print(displayname);
    } else {
      temp_float = mapfloat(temp_int, 0, 100, 0.0, 1.0);
      lcd.setCursor(0, 1);
      lcd.print(" ");
      lcd.setCursor(3, 1);
      lcd.print(" ");
      lcd.setCursor(11, 1);
      lcd.print("[");
      lcd.setCursor(15, 1);
      lcd.print("]");
      sprintf(displayname, "%03d", temp_int);
      lcd.setCursor(12, 1);
      lcd.print(displayname);
      drum_config[activesample].vol_max = temp_float;
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_arp_shift(uint8_t param)
{
  char displayname[4] = {0, 0, 0, 0};
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print("Arp/Chord Transp");
    lcd.setCursor(0, 1);
    lcd.print("Oct");
    lcd.setCursor(4, 1);
    sprintf(displayname, "%02d", seq_oct_shift);
    lcd.print(displayname);
    lcd.setCursor(8, 1);
    lcd.print("Shift");
    lcd.setCursor(14, 1);
    lcd.print(seq_element_shift);
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (menu_select_toggle == false) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          seq_oct_shift = constrain(seq_oct_shift + ENCODER[ENC_R].speed(), -2, 2);
        }
        else if (LCDML.BT_checkUp())
        {
          seq_oct_shift = constrain(seq_oct_shift - ENCODER[ENC_R].speed(), -2, 2);
        }
      }
    } else {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          seq_element_shift = constrain(seq_element_shift + ENCODER[ENC_R].speed(), 0, 6);
        }
        else if (LCDML.BT_checkUp())
        {
          seq_element_shift = constrain(seq_element_shift - ENCODER[ENC_R].speed(), 0, 6);
        }
      }
    }
    if (LCDML.BT_checkEnter())
    {
      menu_select_toggle = !menu_select_toggle;
    }
    if (menu_select_toggle == false)
    { lcd.setCursor(13, 1);
      lcd.print(" ");
      lcd.setCursor(15, 1);
      lcd.print(" ");
      lcd.setCursor(3, 1);
      lcd.print("[");
      lcd.setCursor(6, 1);
      lcd.print("]");
      lcd.setCursor(4, 1);
      sprintf(displayname, "%02d", seq_oct_shift);
      lcd.print(displayname);

    } else {
      lcd.setCursor(3, 1);
      lcd.print(" ");
      lcd.setCursor(6, 1);
      lcd.print(" ");
      lcd.setCursor(13, 1);
      lcd.print("[");
      lcd.setCursor(15, 1);
      lcd.print("]");
      lcd.setCursor(14, 1);
      lcd.print(seq_element_shift);

    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_drum_pan(uint8_t param)
{
  char displayname[8] = {0, 0, 0, 0, 0, 0, 0};
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    temp_int = mapfloat(drum_config[activesample].pan, -1.0, 1.0, -99, 99);
    lcd.setCursor(0, 0);
    lcd.print("DrmSmp. Panorama");
    lcd.setCursor(1, 1);
    sprintf(displayname, "%02d", activesample);
    lcd.print(displayname);
    lcd.show(1, 4, 6, basename(drum_config[activesample].name));
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (menu_select_toggle == false) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          //activesample = constrain(activesample + ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
          smart_filter(1);
        }
        else if (LCDML.BT_checkUp())
        {
          //activesample = constrain(activesample - ENCODER[ENC_R].speed(), 0, NUM_DRUMSET_CONFIG - 2);
          smart_filter(0);
        }
      }
    } else {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          temp_int = constrain(temp_int + ENCODER[ENC_R].speed(), -99, 99);
        }
        else if (LCDML.BT_checkUp())
        {
          temp_int = constrain(temp_int - ENCODER[ENC_R].speed(), -99, 99);
        }
      }
    }
    if (LCDML.BT_checkEnter())
    {
      if (menu_select_toggle) {
        menu_select_toggle = false;
      } else
      { menu_select_toggle = true;
      }
    }
    if (menu_select_toggle == false) {
      lcd.setCursor(11, 1);
      lcd.print(" ");
      lcd.setCursor(15, 1);
      lcd.print(" ");
      lcd.setCursor(0, 1);
      lcd.print("[");
      lcd.setCursor(3, 1);
      lcd.print("]");
      lcd.setCursor(1, 1);
      sprintf(displayname, "%02d", activesample);
      lcd.print(displayname);
      lcd.show(1, 4, 6, basename(drum_config[activesample].name));
    } else {
      lcd.setCursor(0, 1);
      lcd.print(" ");
      lcd.setCursor(3, 1);
      lcd.print(" ");
      lcd.setCursor(11, 1);
      lcd.print("[");
      lcd.setCursor(15, 1);
      lcd.print("]");
      drum_config[activesample].pan = mapfloat(temp_int, -99, 99, -1.0, 1.0);
    }

    temp_int = mapfloat(drum_config[activesample].pan, -1.0, 1.0, -99, 99);

    lcd.setCursor(12, 1);
    if (temp_int > 1) {
      lcd.print("R");
    } else if (temp_int < 0) {
      lcd.print("L");
    }
    else {
      lcd.print("C");
    }
    sprintf(displayname, "%02d", abs(temp_int));
    lcd.setCursor(13, 1);
    lcd.print( displayname);
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

const char* seq_find_drum_name_from_note(uint8_t note)
{
  bool found = false;
  const char* shortname;
  for (uint8_t d = 0; d < NUM_DRUMSET_CONFIG - 1; d++)
  {
    if (note == drum_config[d].midinote)
    { shortname = basename(drum_config[d].name);
      found = true;
      break;
    }
  }
  if (found == false) shortname = " ";

  return shortname;
}

const char* seq_find_shortname(uint8_t sstep)
{
  const char* shortname;
  bool found = false;
  if (seq_content_type[seq_active_track] == 0 && seq_vel[seq_active_track][sstep] < 210) //is Drumtrack and not a pitched sample
  {
    for (uint8_t d = 0; d < NUM_DRUMSET_CONFIG - 1; d++)
    {
      if (seq_data[seq_active_track][sstep] == drum_config[d].midinote)
      {
        shortname = drum_config[d].shortname;
        found = true;
        break;
      }
    }
    if (found == false) shortname = "-";
  } else
  {
    if (seq_data[seq_active_track][sstep] > 0  && seq_data[seq_active_track][sstep] != 130)
      shortname = noteNames[seq_data[seq_active_track][sstep] % 12];
    else if (seq_data[seq_active_track][sstep] == 130)  shortname = "~"; // note has tie/latch
    else
      shortname = "-";
  }
  return shortname;
}

void seq_printAllSeqSteps()
{
  lcd.setCursor(0, 1);
  for (uint8_t i = 0; i < 16; i++)
  {
    lcd.print(seq_find_shortname(i)[0]);
  }
}

#ifdef TESTDISPLAY20x4
void seq_printVelGraphBar()
{
  lcd.setCursor(0, 2);
  for (uint8_t i = 0; i < 16; i++)
  {
    if (seq_vel[seq_active_track][i] == 0)
      lcd.print(" ");
    else if (seq_vel[seq_active_track][i] > 0 && seq_vel[seq_active_track][i] <= 20)
      lcd.write(1);
    else if (seq_vel[seq_active_track][i] > 21 && seq_vel[seq_active_track][i] <= 40)
      lcd.write(2);
    else if (seq_vel[seq_active_track][i] > 41 && seq_vel[seq_active_track][i] <= 60)
      lcd.write(3);
    else if (seq_vel[seq_active_track][i] > 61 && seq_vel[seq_active_track][i] <= 80)
      lcd.write(4);
    else if (seq_vel[seq_active_track][i] > 81 && seq_vel[seq_active_track][i] <= 90)
      lcd.write(5);
    else if (seq_vel[seq_active_track][i] > 91 && seq_vel[seq_active_track][i] <= 105)
      lcd.write(6);
    else if (seq_vel[seq_active_track][i] > 105 && seq_vel[seq_active_track][i] <= 128)
      lcd.write(7);
    else
      lcd.print("C");
  }
}
#endif

void UI_func_seq_display_style(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print("Seq. Disp. Style");
    lcd.setCursor(0, 1);
    lcd.print("Pat.");
    lcd.setCursor(8, 1);
    lcd.print("=");
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (menu_select_toggle == false) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          seq_active_track = constrain(seq_active_track + ENCODER[ENC_R].speed(), 0, 9);
        }
        else if (LCDML.BT_checkUp())
        {
          seq_active_track = constrain(seq_active_track - ENCODER[ENC_R].speed(), 0, 9);
        }
      }
    } else {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
      {
        if (LCDML.BT_checkDown())
        {
          seq_content_type[seq_active_track] = constrain(seq_content_type[seq_active_track] + ENCODER[ENC_R].speed(), 0, 2);
        }
        else if (LCDML.BT_checkUp())
        {
          seq_content_type[seq_active_track] = constrain(seq_content_type[seq_active_track] - ENCODER[ENC_R].speed(), 0, 2);
        }
      }
    }
    if (LCDML.BT_checkEnter())
    {
      menu_select_toggle = !menu_select_toggle;
    }
    if (menu_select_toggle == false) {
      lcd.setCursor(10, 1);
      lcd.print(" ");
      lcd.setCursor(15, 1);
      lcd.print(" ");
      lcd.setCursor(4, 1);
      lcd.print("[");
      lcd.print(seq_active_track);
      lcd.print("]");
    } else {
      lcd.setCursor(4, 1);
      lcd.print(" ");
      lcd.setCursor(6, 1);
      lcd.print(" ");
      lcd.setCursor(10, 1);
      lcd.print("[");
      lcd.setCursor(15, 1);
      lcd.print("]");
    }
    lcd.setCursor(11, 1);
    if (seq_content_type[seq_active_track] == 0)
      lcd.print("Drum");
    else  if (seq_content_type[seq_active_track] == 1)
      lcd.print("Inst");
    else
      lcd.print("Chrd");
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_live_transpose_oct(uint8_t param)
{ //Select octave for live transpose of sequencer instrument track. Should be one of the lowest octaves available on the keyboard.
  //Allowed range: C1-C5 to not restrict too much, even for very unusual user setups/configurations.

  char note_name[4];
  char note_name2[4];
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    getNoteName(note_name, seq_transpose);
    getNoteName(note_name2, seq_transpose + 12);
    lcd.setCursor(0, 0);
    lcd.print(F("Live Transp.Oct."));
    lcd.setCursor(0, 1);
    lcd.print(F("["));
    lcd.print(note_name);
    lcd.print(F("] - "));
    lcd.print(("["));
    lcd.print(note_name2);
    lcd.print(("]"));
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        seq_transpose = constrain(seq_transpose + ENCODER[ENC_R].speed(), 24, 60);
      else if (LCDML.BT_checkUp())
        seq_transpose = constrain(seq_transpose - ENCODER[ENC_R].speed(), 24, 60);
    }
    getNoteName(note_name, seq_transpose);
    getNoteName(note_name2, seq_transpose + 12);
    lcd.setCursor(0, 1);
    lcd.print(F("["));
    lcd.print(note_name);
    lcd.print(F("] - "));
    lcd.print(("["));
    lcd.print(note_name2);
    lcd.print(("]"));
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_chord_keys_ammount(uint8_t param)
{
  char displayname[4] = {0, 0, 0, 0};
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print("ChordTrack Keys:");
    lcd.setCursor(8, 1);
    lcd.print("Keys");
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        seq_chord_key_ammount = constrain(seq_chord_key_ammount + ENCODER[ENC_R].speed(), 1, 7);
      else if (LCDML.BT_checkUp())
        seq_chord_key_ammount = constrain(seq_chord_key_ammount - ENCODER[ENC_R].speed(), 1, 7);
    }
    lcd.setCursor(4, 1);
    lcd.print("[");
    sprintf(displayname, "%02d", seq_chord_key_ammount);
    lcd.print(displayname);
    lcd.print("]");
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_lenght(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print("  Seq. lenght   ");
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        seq_chain_lenght = constrain(seq_chain_lenght + ENCODER[ENC_R].speed(), 0, 3);
      else if (LCDML.BT_checkUp())
        seq_chain_lenght = constrain(seq_chain_lenght - ENCODER[ENC_R].speed(), 0, 3);
    }
    lcd.setCursor(3, 1);
    lcd.print((seq_chain_lenght + 1) * 16 );
    lcd.setCursor(7, 1);
    lcd.print("Steps");
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_tempo(uint8_t param)
{
  char tmp[7];
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print("  Seq. Tempo   ");
    lcd.setCursor(5, 1);
    lcd.print("BPM");
    lcd.setCursor(14, 1);
    lcd.print("ms");
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        seq_bpm = constrain(seq_bpm + ENCODER[ENC_R].speed(), 50, 190);
      else if (LCDML.BT_checkUp())
        seq_bpm = constrain(seq_bpm - ENCODER[ENC_R].speed(), 50, 190);
    }
    seq_tempo_ms = 60000000 / seq_bpm / 4;
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%3d]", seq_bpm);
    lcd.print(tmp);
    lcd.setCursor(11, 1);
    sprintf(tmp, "%3d", seq_tempo_ms / 1000);
    lcd.print(tmp);
#ifdef USE_FX
    for (uint8_t i = 0; i < MAX_DEXED; i++)
    {
      if (configuration.fx.delay_sync[i] > 0)
      {
        uint16_t midi_sync_delay_time = uint16_t(60000.0 *  midi_ticks_factor[configuration.fx.delay_sync[i]] / seq_bpm);
        delay_fx[i]->delay(0, constrain(midi_sync_delay_time, DELAY_TIME_MIN, DELAY_TIME_MAX * 10));
      }
    }
#endif
    //timer1.stop();
    timer1.begin(sequencer, seq_tempo_ms / 2);
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_vel_editor(uint8_t param)
{
  char tmp[5];
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    // setup function
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print("Vel:");
    sprintf(tmp, "%03d", seq_vel[seq_active_track][seq_menu - 1]);
    lcd.setCursor(4, 0);
    lcd.print(tmp);
    lcd.setCursor(12, 0);
    lcd.print("S[");
    lcd.setCursor(15, 0);
    lcd.print("]");
    lcd.setCursor(14, 0);
    lcd.print(seq_active_track);
    seq_printAllSeqSteps();
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (seq_active_function == 99)
    {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_menu = constrain(seq_menu + 1, 0, 16);
        else if (LCDML.BT_checkUp())
          seq_menu = constrain(seq_menu - 1, 0, 16);
      }
    }
    else if (seq_active_function == 0)
    {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_active_track = constrain(seq_active_track + 1, 0, NUM_SEQ_PATTERN - 1);
        else if (LCDML.BT_checkUp())
          seq_active_track = constrain(seq_active_track - 1, 0, NUM_SEQ_PATTERN - 1);
      }
    }
    if ( seq_data[seq_active_track][seq_menu - 1] > 0 )
    {
      if (seq_vel[seq_active_track][seq_menu - 1] < 210) //it is a normal sample
      {
        if (seq_active_function == 1 && seq_content_type[seq_active_track] < 2 )
        { //if is Drum or normal Instrument Track
          if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
          {
            if (LCDML.BT_checkDown())
              seq_vel[seq_active_track][seq_menu - 1] = constrain(seq_vel[seq_active_track][seq_menu - 1] + 1, 0, 127);
            else if (LCDML.BT_checkUp())
              seq_vel[seq_active_track][seq_menu - 1] = constrain(seq_vel[seq_active_track][seq_menu - 1] - 1, 0, 127);
          }
        }
        else if (seq_active_function == 1 && seq_content_type[seq_active_track] > 1 )
        { //is in Chord or Arp Mode
          if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
          {
            if (LCDML.BT_checkDown())
              seq_vel[seq_active_track][seq_menu - 1] = constrain(seq_vel[seq_active_track][seq_menu - 1] + 1, 200, 205);
            else if (LCDML.BT_checkUp())
              seq_vel[seq_active_track][seq_menu - 1] = constrain(seq_vel[seq_active_track][seq_menu - 1] - 1, 200, 205);
          }
        }
      }
      else
      {
        //is in pitched Sample Mode
        if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
        {
          if (LCDML.BT_checkDown())
            seq_vel[seq_active_track][seq_menu - 1] = constrain(seq_vel[seq_active_track][seq_menu - 1] + 1, 210, 214);
          else if (LCDML.BT_checkUp())
            seq_vel[seq_active_track][seq_menu - 1] = constrain(seq_vel[seq_active_track][seq_menu - 1] - 1, 210, 214);
        }
      }
    }
    if (LCDML.BT_checkEnter())  //handle button presses during menu >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    {
      if ( seq_menu == 0 && seq_active_function == 99)
      {
        seq_active_function = 0;
      } else if ( seq_menu == 0 && seq_active_function == 0)
      {
        seq_active_function = 99;
      }
      else if (seq_menu > 0 && seq_active_function == 99)
      {
        seq_active_function = 1;
      }
      else
        seq_active_function = 99;
    }
    //button check end <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    if ( seq_content_type[seq_active_track] > 1 && seq_vel[seq_active_track][seq_menu - 1] < 200) seq_vel[seq_active_track][seq_menu - 1] = 200;
    if (seq_active_function == 0) {
      lcd.setCursor(0, 0);
      if (seq_content_type[seq_active_track] == 0)
        lcd.print("Drum Track   ");
      else  if (seq_content_type[seq_active_track] == 1)
        lcd.print("Instr. Track ");
      else
        lcd.print("Chord Track  ");
    }
    if (seq_menu > 0 && seq_content_type[seq_active_track] == 0)
    {
      lcd.setCursor(0, 0);
      if (seq_vel[seq_active_track][seq_menu - 1] < 210 && seq_vel[seq_active_track][seq_menu - 1] > 0) //it is a normal sample
      {
        lcd.print("Vel:");
        sprintf(tmp, "%03d", seq_vel[seq_active_track][seq_menu - 1]);
        lcd.setCursor(4, 0);
        lcd.print(tmp);
      }
      else
      { // else it is a live-pitched sample
        if (seq_vel[seq_active_track][seq_menu - 1] > 0) {
          lcd.setCursor(0, 0);
          lcd.print("Smp:[");
          lcd.setCursor(12, 0);
          lcd.print("]");
          lcd.show(0, 5, 7, basename(drum_config[seq_vel[seq_active_track][seq_menu - 1] - 210].name));
        }
      }
    }
    if (seq_menu == 0) {
      if (seq_active_function != 0)
      {
        lcd.setCursor(0, 0);
        lcd.print("             ");
      }
      lcd.setCursor(13, 0);
      lcd.print("[");
      lcd.print(seq_active_track);
      lcd.print("]");
      lcd.setCursor(0, 1);
      seq_printAllSeqSteps();
    } else if (seq_menu == 1) {
      lcd.setCursor(13, 0);
      lcd.print(" ");
      lcd.setCursor(15, 0);
      lcd.print(" ");
      lcd.setCursor(0, 1);
      lcd.write(219); // cursor symbol
      lcd.setCursor(1, 1);
      lcd.print(seq_find_shortname(1)[0] );
      if (seq_vel[seq_active_track][seq_menu - 1] < 210 && seq_content_type[seq_active_track] < 2) //it is a normal sample
      {
        sprintf(tmp, "%03d", seq_vel[seq_active_track][seq_menu - 1]);
        lcd.setCursor(4, 0);
        lcd.print(tmp);
      }
    } else if (seq_menu > 1)
    {
      lcd.setCursor(seq_menu - 1, 1);
      lcd.write(219); // cursor symbol
      lcd.setCursor(seq_menu - 2, 1);
      lcd.print(seq_find_shortname(seq_menu - 2)[0] );
      if (seq_menu < 16) {
        lcd.setCursor(seq_menu , 1);
        lcd.print(seq_find_shortname(seq_menu)[0] );
      }
    }
    if (seq_menu > 0) {
      lcd.setCursor(4, 0);
      if (seq_data[seq_active_track][seq_menu - 1] > 0)
      {
        if (seq_content_type[seq_active_track] == 0) //is Drumtrack
        {
          lcd.setCursor(0, 0);
          if (seq_vel[seq_active_track][seq_menu - 1] < 210) //it is a normal sample
          {
            lcd.print("Vel:");
            sprintf(tmp, "%03d", seq_vel[seq_active_track][seq_menu - 1]);
            lcd.setCursor(7, 0);
            lcd.print(" ");
            lcd.show(0, 8, 5, seq_find_drum_name_from_note( seq_data[seq_active_track][seq_menu - 1]) );
          }
          else
          { // else it is a live-pitched sample
            lcd.setCursor(0, 0);
            lcd.print("Smp:[");
            lcd.setCursor(12, 0);
            lcd.print("]");
            lcd.setCursor(1, 0);
            lcd.show(0, 5, 7, basename(drum_config[seq_vel[seq_active_track][seq_menu - 1] - 210].name));
          }
        } else
        {
          if ( seq_data[seq_active_track][seq_menu - 1] != 130  ) //note not latched
          {
            if (seq_content_type[seq_active_track] < 2)
            {
              lcd.setCursor(0, 0);
              lcd.print("Vel:");
              sprintf(tmp, "%03d", seq_vel[seq_active_track][seq_menu - 1]);
              lcd.setCursor(4, 0);
              lcd.print(tmp);
            }
            lcd.setCursor(8, 0);
            lcd.print(noteNames[seq_data[seq_active_track][seq_menu - 1] % 12 ][0] );
            if (noteNames[seq_data[seq_active_track][seq_menu - 1] % 12 ][1] != '\0' ) {
              lcd.print(noteNames[seq_data[seq_active_track][seq_menu - 1] % 12 ][1] );
            }
            lcd.print( (seq_data[seq_active_track][seq_menu - 1] / 12) - 1);
            lcd.print("  ");
          }
          else
          { //note is latched
            lcd.setCursor(0, 0);
            lcd.print("latched note  ");
          }
          if (seq_content_type[seq_active_track] > 1)
          {
            lcd.setCursor(0, 0);
            if (seq_vel[seq_active_track][seq_menu - 1] == 200) lcd.print("Major" );
            else if (seq_vel[seq_active_track][seq_menu - 1] == 201) lcd.print("Minor" );
            else if (seq_vel[seq_active_track][seq_menu - 1] == 202) lcd.print("Seven" );
            else if (seq_vel[seq_active_track][seq_menu - 1] == 203) lcd.print("Aug  " );
            else if (seq_vel[seq_active_track][seq_menu - 1] == 204) lcd.print("Dim  " );
            else if (seq_vel[seq_active_track][seq_menu - 1] == 205) lcd.print("Maj7 " );
          }
        }
      }
      else
      {
        lcd.setCursor(0, 0);
        lcd.print("              ");
      }
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void seq_clear_active_pattern()
{
  memset(seq_data[seq_active_track], 0, sizeof(seq_data[seq_active_track]));
  memset(seq_vel[seq_active_track], 0, sizeof(seq_vel[seq_active_track]));
}
void seq_clear_all_patterns()
{
  for (uint8_t i = 0; i < 10; i++)
  {
    memset(seq_data[i], 0, sizeof(seq_data[i]));
    memset(seq_vel[i], 0, sizeof(seq_vel[i]));
  }
}

void seq_refresh_display_play_status()
{
  if (seq_running == false && seq_recording == false)
  {
    lcd.createChar(0, (uint8_t*)special_chars[19]); //play symbol
#ifdef TESTDISPLAY20x4
    lcd.setCursor(15, 0);
#else
    lcd.setCursor(11, 0);
#endif
    lcd.write(0);
  } else if (seq_running == true && seq_recording == false)
  {
    seq_note_in = 0;
    lcd.createChar(0, (uint8_t*)special_chars[20]); //record symbol
#ifdef TESTDISPLAY20x4
    lcd.setCursor(15, 0);
#else
    lcd.setCursor(11, 0);
#endif
    lcd.write(0);
  } else if (seq_running == true && seq_recording == true)
  {
    seq_note_in = 0;
    lcd.createChar(0, (uint8_t*)special_chars[21]); //stop symbol
#ifdef TESTDISPLAY20x4
    lcd.setCursor(15, 0);
#else
    lcd.setCursor(11, 0);
#endif
    lcd.write(0);
  }
}

void arp_refresh_display_play_status()
{
  if (seq_running == false )
  {
    lcd.createChar(0, (uint8_t*)special_chars[19]); //play symbol
    lcd.setCursor(14, 0);
    lcd.write(0);
  }  else if (seq_running == true )
  {
    seq_note_in = 0;
    lcd.createChar(0, (uint8_t*)special_chars[21]); //stop symbol
    lcd.setCursor(14, 0);
    lcd.write(0);
  }
}

void UI_func_drum_monitor(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Drum Status "));
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    ;
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_pattern_editor(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    lcd.createChar(0, (uint8_t*)special_chars[19]); //play symbol + record symbol switching
#ifdef TESTDISPLAY20x4
    lcd.createChar(1, (uint8_t*)special_chars[9]); //bar graph
    lcd.createChar(2, (uint8_t*)special_chars[10]); //bar graph
    lcd.createChar(3, (uint8_t*)special_chars[11]); //bar graph
    lcd.createChar(4, (uint8_t*)special_chars[12]); //bar graph
    lcd.createChar(5, (uint8_t*)special_chars[13]); //bar graph
    lcd.createChar(6, (uint8_t*)special_chars[14]); //bar graph
    lcd.createChar(7, (uint8_t*)special_chars[15]); //bar graph
#endif
    temp_int = seq_data[seq_active_track][0];
    encoderDir[ENC_R].reset();
    seq_note_in = 0;
    // setup function
    seq_refresh_display_play_status();
#ifdef TESTDISPLAY20x4
    lcd.setCursor(18, 0);
#else
    lcd.setCursor(14, 0);
#endif
    lcd.print(seq_active_track);
    seq_printAllSeqSteps();
#ifdef TESTDISPLAY20x4
    seq_printVelGraphBar();
#endif
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  { if (seq_menu == 33 ) { // is in sub-function - fill pattern
      seq_active_function = 95;
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown()) {
          seq_temp_active_menu = constrain(seq_temp_active_menu + 1, 0, 3);
        }
        else if (LCDML.BT_checkUp()) {
          seq_temp_active_menu = constrain(seq_temp_active_menu - 1, 0, 3);
        }
      }
    }
    else if (seq_menu == 32 ) { // is in sub-function - fill pattern
      seq_active_function = 97;
      if (seq_content_type[seq_active_track] == 0) {
        if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
        {
          if (LCDML.BT_checkDown()) {
            seq_temp_select_menu = constrain(seq_temp_select_menu + 1, 0, NUM_DRUMSET_CONFIG - 1);
          }
          else if (LCDML.BT_checkUp()) {
            seq_temp_select_menu = constrain(seq_temp_select_menu - 1, 0, NUM_DRUMSET_CONFIG - 1);
          }
        }
      }
      else
      {
        if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
        {
          if (LCDML.BT_checkDown())
            seq_temp_select_menu = constrain(seq_temp_select_menu + 1, 0, 108 );
          else if (LCDML.BT_checkUp())
            seq_temp_select_menu = constrain(seq_temp_select_menu - 1, 0, 108 );
        }
      }
    }
    else if (seq_menu == 30 || seq_menu == 31 ) { // is in sub-function - swap pattern or copy pattern
      seq_active_function = 98;
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown()) {

          temp_int = constrain(temp_int + 1, 0, NUM_SEQ_PATTERN - 1);
          if (temp_int == seq_active_track)temp_int++;
          if (temp_int > 9)temp_int = 0;
        }
        else if (LCDML.BT_checkUp()) {
          temp_int = constrain(temp_int - 1, 0, NUM_SEQ_PATTERN - 1);
          if (temp_int == seq_active_track)temp_int--;
          if (temp_int < 0)temp_int = NUM_SEQ_PATTERN - 1;
        }
      }
    }
    else if (seq_menu == 34 ) { // is in transpose edit
      seq_active_function = 94;
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown()) {
          temp_int = constrain(temp_int + 1, -36, 36);
          if (temp_int > 36)temp_int = 36;
        }
        else if (LCDML.BT_checkUp()) {
          temp_int = constrain(temp_int - 1, -36, 36);
          if (temp_int < -36)temp_int = -36;
        }
      }
    }
    else if (seq_active_function == 99)
    {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_menu = constrain(seq_menu + 1, 0, 18);
        else if (LCDML.BT_checkUp())
          seq_menu = constrain(seq_menu - 1, 0, 18);
      }
    }
    else if (seq_active_function == 0 ) {
      if (seq_content_type[seq_active_track] == 0) // is in Drumedit mode
      {
        if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
        {
          if (LCDML.BT_checkDown())
            activesample = constrain(activesample + 1, 0, NUM_DRUMSET_CONFIG + 5 );
          else if (LCDML.BT_checkUp())
            activesample = constrain(activesample - 1, 0, NUM_DRUMSET_CONFIG + 5 );
        }
      }
      else //is in Instrument Mode
      {
        if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
        {
          if (LCDML.BT_checkDown())
            temp_int = constrain(temp_int + 1, 0, 116 );
          else if (LCDML.BT_checkUp())
            temp_int = constrain(temp_int - 1, 0, 116 );
        }
      }
    } else if (seq_active_function == 2)
    {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_active_track = constrain(seq_active_track + 1, 0, NUM_SEQ_PATTERN - 1);
        else if (LCDML.BT_checkUp())
          seq_active_track = constrain(seq_active_track - 1, 0, NUM_SEQ_PATTERN - 1);

        lcd.setCursor(1, 0);
        if (seq_content_type[seq_active_track] == 0) lcd.print("Drum  "); else if (seq_content_type[seq_active_track] == 1) lcd.print("Instr "); else if (seq_content_type[seq_active_track] == 2) lcd.print("Chord "); else lcd.print("Arp   ");
      }
    }
    if (LCDML.BT_checkEnter())  //handle button presses during menu >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    {
      if (seq_menu == 34 ) //transpose pattern
      {
        seq_menu = 0;
        seq_active_function = 0;
        activesample = 0;
        temp_int = seq_data[seq_active_track][0];
        lcd.setCursor(8, 0);
        lcd.print("  ");
        seq_refresh_display_play_status();
        seq_printAllSeqSteps();
      }
      else if (seq_menu == 32 && seq_active_function == 97) //fill pattern every 1/4, 1/8, 1/16 step with active sample/note  step 1
      {
        seq_active_function = 96;
        seq_menu = 33;
      } else if (seq_menu == 33 && seq_active_function == 95) //fill pattern every 1/4, 1/8, 1/16 step with active sample/note  step 2
      {
        if (seq_content_type[seq_active_track] == 0) { //Drumtrack
          for (uint8_t i = 0; i < 16; i++)
          {
            seq_data[seq_active_track][i] = drum_config[seq_temp_select_menu].midinote;
            seq_vel[seq_active_track][i] = 120;
            if (seq_temp_active_menu == 0) i = i + 3; else if (seq_temp_active_menu == 1) i = i + 1;
          }
        }
        else
        { //Inst. Track
          for (uint8_t i = 0; i < 16; i++)
          {
            seq_data[seq_active_track][i] = seq_temp_select_menu;
            seq_vel[seq_active_track][i] = 120;
            if (seq_temp_active_menu == 0) i = i + 3; else if (seq_temp_active_menu == 1) i = i + 1;
          }
        }
        seq_menu = 0;
        seq_active_function = 0;
        activesample = 0;
        temp_int = seq_data[seq_active_track][0];
        lcd.setCursor(8, 0);
        lcd.print("  ");
        seq_refresh_display_play_status();
        seq_printAllSeqSteps();
      } else if (seq_menu == 31) //copy patterns
      {
        memcpy( seq_data[temp_int], seq_data[seq_active_track], sizeof(seq_data[0]));
        memcpy( seq_vel[temp_int], seq_vel[seq_active_track], sizeof(seq_vel[0]));
        seq_content_type[temp_int] = seq_content_type[seq_active_track];
        seq_menu = 0;
        seq_active_function = 0;
        activesample = 0;
        temp_int = seq_data[seq_active_track][0];
        lcd.setCursor(8, 0);
        lcd.print("  ");
        seq_refresh_display_play_status();
        seq_printAllSeqSteps();
      } else if (seq_menu == 30) //swap patterns
      {
        uint8_t data_temp[1][16];
        uint8_t vel_temp[1][16];
        uint8_t content_type_temp;
        memcpy( data_temp[0], seq_data[seq_active_track], sizeof(data_temp[0]));
        memcpy( vel_temp[0], seq_vel[seq_active_track], sizeof(vel_temp[0]));
        content_type_temp = seq_content_type[seq_active_track];
        memcpy( seq_data[seq_active_track], seq_data[temp_int], sizeof(data_temp[0]));
        memcpy( seq_vel[seq_active_track], seq_vel[temp_int], sizeof(vel_temp[0]));
        seq_content_type[seq_active_track] = seq_content_type[temp_int];
        memcpy( seq_data[temp_int], data_temp[0], sizeof(data_temp[0]));
        memcpy( seq_vel[temp_int], vel_temp[0], sizeof(vel_temp[0]));
        seq_content_type[temp_int] = content_type_temp;
        seq_menu = 0;
        seq_active_function = 0;
        activesample = 0;
        temp_int = seq_data[seq_active_track][0];
        lcd.setCursor(8, 0);
        lcd.print("  ");
        seq_refresh_display_play_status();
        seq_printAllSeqSteps();
      }
      if ( seq_menu == 0 && seq_active_function == 99)
      {
        seq_active_function = 0;
      } else if ( seq_menu == 0 && seq_active_function == 0)
      {
        if ( (seq_content_type[seq_active_track] == 0 && activesample == NUM_DRUMSET_CONFIG + 5) || (seq_content_type[seq_active_track] > 0 && temp_int == 116) )
        { //transpose pattern
          lcd.setCursor(0, 0);
          lcd.print("Transpose: 00");
          for (uint8_t i = 0; i < 16; i++)
          {
            seq_data_buffer[i] = seq_data[seq_active_track][i];
          }
          seq_menu = 34;
          temp_int = 0;
          seq_temp_select_menu = 0;
          seq_temp_active_menu = 0;
        }
        if ( (seq_content_type[seq_active_track] == 0 && activesample == NUM_DRUMSET_CONFIG + 4) || (seq_content_type[seq_active_track] > 0 && temp_int == 115) )
        { //fill patterns
          lcd.setCursor(0, 0);
          lcd.print("Fill Pattern:");
          lcd.setCursor(9, 1);
          lcd.print("       ");
          seq_menu = 32;
          seq_temp_select_menu = 0;
          seq_temp_active_menu = 0;
        }
        else if ( (seq_content_type[seq_active_track] == 0 && activesample == NUM_DRUMSET_CONFIG + 3) || (seq_content_type[seq_active_track] > 0 && temp_int == 114) )
        { //swap patterns: Active pattern <-> destination pattern
          lcd.setCursor(0, 0);
          lcd.print("Swap Pattern:");
          temp_int = seq_active_track + 1;
          if (temp_int > 9)temp_int = 0;
          seq_menu = 30;
        } else if ( (seq_content_type[seq_active_track] == 0 && activesample == NUM_DRUMSET_CONFIG + 2) || (seq_content_type[seq_active_track] > 0 && temp_int == 113) )
        { //copy pattern
          lcd.setCursor(0, 0);
          lcd.print("Copy Pattern:");
          temp_int = seq_active_track + 1;
          if (temp_int > 9)temp_int = 0;
          seq_menu = 31;
        } else if ( (seq_content_type[seq_active_track] == 0 && activesample == NUM_DRUMSET_CONFIG + 1) || (seq_content_type[seq_active_track] > 0 && temp_int == 112) )
        { //clear all patterns
          seq_clear_all_patterns();
          seq_printAllSeqSteps();
        } else if ( (seq_content_type[seq_active_track] == 0 && activesample == NUM_DRUMSET_CONFIG) || (seq_content_type[seq_active_track] > 0 && temp_int == 111) )
        { //clear pattern
          seq_clear_active_pattern();
          seq_printAllSeqSteps();
        }
        seq_active_function = 99;
      }
      if ( seq_menu == 1)
      {
        if (seq_running == false && seq_recording == false)
        {
          seq_running = true;
          timer1.start();
        } else if (seq_running == true && seq_recording == false)
        {
          seq_running = true;
          seq_recording = true;
          seq_note_in = 0;
        } else if (seq_running == true && seq_recording == true)
        {
          seq_running = false;
          seq_recording = false;
          seq_note_in = 0;
          seq_step = 0;
          seq_chain_active_step = 0;
          timer1.stop();
          MicroDexed[0]->panic();
        }
      } else if ( seq_menu == 2)
      {
        if (seq_active_function != 2) seq_active_function = 2; else  seq_active_function = 99;

        if (seq_content_type[seq_active_track] == 0)
        {
          if (activesample < NUM_DRUMSET_CONFIG - 1)
            lcd.show(0, 1, 6, basename(drum_config[activesample].name));
        } else
        {
          if (temp_int < 109) {
            lcd.setCursor(3, 0);
            lcd.print("    ");
            lcd.setCursor(1, 0);
            lcd.print(noteNames[temp_int % 12 ]);
            lcd.print( (temp_int / 12) - 1);
          }
        }
      } else if (seq_menu > 2 && seq_menu < 30)
      {
        if (seq_active_function == 99)
        {
          if (seq_content_type[seq_active_track] == 0)
          { //Drumtrack
            //check if note is already there, if not -> insert it, else remove it from grid.
            if (seq_data[seq_active_track][seq_menu - 3] == drum_config[activesample].midinote)
            {
              seq_data[seq_active_track][seq_menu - 3] = 0;
              seq_vel[seq_active_track][seq_menu - 3] = 0;
            } else
            {
              seq_data[seq_active_track][seq_menu - 3] = drum_config[activesample].midinote;
              seq_vel[seq_active_track][seq_menu - 3] = 120;
            }
          }
          else
          { //Inst. Track
            if (temp_int == 109 || seq_data[seq_active_track][seq_menu - 3] == temp_int )
            { //clear note
              seq_data[seq_active_track][seq_menu - 3] = 0;
              seq_vel[seq_active_track][seq_menu - 3] = 0;
            } else if (temp_int == 110) { //latch note
              seq_data[seq_active_track][seq_menu - 3] = 130;
              //seq_vel[seq_active_track][seq_menu - 3] = 0;
            } else
            {
              seq_data[seq_active_track][seq_menu - 3] = temp_int;
              seq_vel[seq_active_track][seq_menu - 3] = 120;
            }
          }
        }
        else
          seq_active_function = 99;
      }
    }
    //button check end <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    if (seq_menu == 35  )
    { //transpose
      ;
    }
    else if (seq_menu == 34 && seq_active_function == 94)
    { //transpose
      char displayname[4] = {0, 0, 0, 0};
      lcd.setCursor(10, 0);
      if (temp_int > 1) {
        lcd.print("+");
      } else if (temp_int < 0)
      {
        lcd.print("-");
      }
      else {
        lcd.print(" ");
      }
      sprintf(displayname, "%02d", abs(temp_int));
      lcd.setCursor(11, 0);
      lcd.print( displayname);
      for (uint8_t i = 0; i < 16; i++)
      {
        if (seq_content_type[seq_active_track] == 0) { //drums
          if (seq_data_buffer[i] != 0 && seq_data_buffer[i] + temp_int >= 0 && seq_data_buffer[i] + temp_int < 254
              && seq_vel[seq_active_track][i] >= 210 ) // pitched drums only
            seq_data[seq_active_track][i] = seq_data_buffer[i] + temp_int;
        }
        else
          //instruments
          if (seq_content_type[seq_active_track] > 0) {
            if (seq_data_buffer[i] != 0 && seq_data_buffer[i] != 130 && seq_data_buffer[i] + temp_int > 0 && seq_data_buffer[i] + temp_int < 254)
              seq_data[seq_active_track][i] = seq_data_buffer[i] + temp_int;
          }
      }
      seq_printAllSeqSteps();
    }
    else if (seq_menu == 33)
    { //fill pattern 2nd parameter
      lcd.setCursor(4, 1);
      lcd.print(" ");
      lcd.setCursor(9, 1);
      lcd.print(" ");
      lcd.setCursor(10, 1);
      lcd.print("[");
      lcd.setCursor(15, 1);
      lcd.print("]");
      lcd.setCursor(11, 1);
      if (seq_temp_active_menu == 0) lcd.print(" 1/4");
      else if (seq_temp_active_menu == 1) lcd.print(" 1/8");
      else if (seq_temp_active_menu == 2) lcd.print("1/16");
    } else if (seq_menu == 32  ) { //fill pattern
      if (seq_content_type[seq_active_track] == 0) { //drum
        lcd.setCursor(0, 1);
        lcd.print("with");
        lcd.setCursor(4, 1);
        lcd.print("[");
        lcd.setCursor(9, 1);
        lcd.print("]");
        lcd.show(1, 5, 4, basename(drum_config[seq_temp_select_menu].name));
        lcd.setCursor(11, 1);
        if (seq_temp_active_menu == 0) lcd.print(" 1/4");
        else if (seq_temp_active_menu == 1) lcd.print(" 1/8");
        else if (seq_temp_active_menu == 2) lcd.print("1/16");
      } else
      { //inst
        lcd.setCursor(0, 1);
        lcd.print("with");
        lcd.setCursor(4, 1);
        lcd.print("[");
        lcd.setCursor(5, 1);
        lcd.print(noteNames[seq_temp_select_menu % 12 ]);
        lcd.print( (seq_temp_select_menu / 12) - 1);
        lcd.print(" ");
        lcd.setCursor(9, 1);
        lcd.print("]");
        lcd.setCursor(11, 1);
        if (seq_temp_active_menu == 0) lcd.print(" 1/4");
        else if (seq_temp_active_menu == 1) lcd.print(" 1/8");
        else if (seq_temp_active_menu == 2) lcd.print("1/16");
      }
    } else if (seq_menu == 31) { //copy pattern
      lcd.setCursor(13, 0);
      lcd.print("[");
      lcd.setCursor(15, 0);
      lcd.print("]");
      lcd.setCursor(0, 1);
      lcd.print("         to: [ ]");
      lcd.setCursor(14, 1);
      lcd.print(temp_int);
    } else if (seq_menu == 30) { //swap pattern
      lcd.setCursor(13, 0);
      lcd.print("[");
      lcd.setCursor(15, 0);
      lcd.print("]");
      lcd.setCursor(0, 1);
      lcd.print("       with: [ ]");
      lcd.setCursor(14, 1);
      lcd.print(temp_int);
    } else if (seq_menu == 0) {
#ifdef TESTDISPLAY20x4
      lcd.setCursor(14, 0);
      lcd.print(" ");
      lcd.setCursor(16, 0);
      lcd.print(" ");
#else
      lcd.setCursor(10, 0);
      lcd.print(" ");
      lcd.setCursor(12, 0);
      lcd.print(" ");
#endif
      lcd.setCursor(0, 0);
      lcd.print("[");
      if (seq_content_type[seq_active_track] == 0) //Drum Mode
      {
        if (activesample < NUM_DRUMSET_CONFIG - 1) {
          lcd.show(0, 1, 6, basename(drum_config[activesample].name));
        } else if (activesample == NUM_DRUMSET_CONFIG - 1) {
          lcd.setCursor(1, 0);
          lcd.print("EMPTY ");
        } else if (activesample == NUM_DRUMSET_CONFIG ) {
          lcd.setCursor(1, 0);
          lcd.print("ClrPat");
        } else if (activesample == NUM_DRUMSET_CONFIG + 1) {
          lcd.setCursor(1, 0);
          lcd.print("ClrAll");
        }
        else if (activesample == NUM_DRUMSET_CONFIG + 2) {
          lcd.setCursor(1, 0);
          lcd.print("Copy P");
        }
        else if (activesample == NUM_DRUMSET_CONFIG + 3) {
          lcd.setCursor(1, 0);
          lcd.print("Swap P");
        }
        else if (activesample == NUM_DRUMSET_CONFIG + 4) {
          lcd.setCursor(1, 0);
          lcd.print("Fill P");
        }
        else if (activesample == NUM_DRUMSET_CONFIG + 5) {
          lcd.setCursor(1, 0);
          lcd.print("Transp");
        }
        lcd.setCursor(7, 0);
        lcd.print("]");
      } else //Inst. Mode
      {
        if (temp_int < 109) {
          lcd.setCursor(3, 0);
          lcd.print("    ");
          lcd.setCursor(1, 0);
          lcd.print(noteNames[temp_int % 12 ]);
          lcd.print( (temp_int / 12) - 1);
        } else if (temp_int == 109) {
          lcd.setCursor(1, 0);
          lcd.print("EMPTY ");
        } else if (temp_int == 110) {
          lcd.setCursor(1, 0);
          lcd.print("LATCH ");
        } else if (temp_int == 111) {
          lcd.setCursor(1, 0);
          lcd.print("ClrPat");
        } else if (temp_int == 112) {
          lcd.setCursor(1, 0);
          lcd.print("ClrAll");
        } else if (temp_int == 113) {
          lcd.setCursor(1, 0);
          lcd.print("Copy P");
        }  else if (temp_int == 114) {
          lcd.setCursor(1, 0);
          lcd.print("Swap P");
        } else if (temp_int == 115) {
          lcd.setCursor(1, 0);
          lcd.print("Fill P");
        } else if (temp_int == 116) {
          lcd.setCursor(1, 0);
          lcd.print("Transp");
        }
        lcd.setCursor(7, 0);
        lcd.print("]");
      }
    }
    else if (seq_menu == 1) {
      lcd.setCursor(0, 0);
      lcd.print(" ");
      lcd.setCursor(7, 0);
      lcd.print(" ");
#ifdef TESTDISPLAY20x4
      lcd.setCursor(17, 0);
      lcd.print(" ");
      lcd.setCursor(19, 0);
      lcd.print(" ");
#else
      lcd.setCursor(13, 0);
      lcd.print(" ");
      lcd.setCursor(15, 0);
      lcd.print(" ");
#endif
#ifdef TESTDISPLAY20x4
      lcd.setCursor(14, 0);
      lcd.print("[");
      seq_refresh_display_play_status();
      lcd.setCursor(16, 0);
      lcd.print("]");
#else
      lcd.setCursor(10, 0);
      lcd.print("[");
      seq_refresh_display_play_status();
      lcd.setCursor(12, 0);
      lcd.print("]");
#endif
    }
    if (seq_menu == 2) {
#ifdef TESTDISPLAY20x4
      lcd.setCursor(14, 0);
      lcd.print(" ");
      lcd.setCursor(16, 0);
      lcd.print(" ");
      lcd.setCursor(17, 0);
      lcd.print("[");
      lcd.setCursor(18, 0);
      lcd.print(seq_active_track);
      lcd.setCursor(19, 0);
      lcd.print("]");
#else
      lcd.setCursor(10, 0);
      lcd.print(" ");
      lcd.setCursor(12, 0);
      lcd.print(" ");
      lcd.setCursor(13, 0);
      lcd.print("[");
      lcd.setCursor(14, 0);
      lcd.print(seq_active_track);
      lcd.setCursor(15, 0);
      lcd.print("]");
#endif
      lcd.setCursor(0, 1);
      seq_printAllSeqSteps();
#ifdef TESTDISPLAY20x4
      seq_printVelGraphBar();
#endif
    }
    if (seq_menu == 3) {
#ifdef TESTDISPLAY20x4
      lcd.setCursor(17, 0);
      lcd.print(" ");
      lcd.setCursor(19, 0);
      lcd.print(" ");
#else
      lcd.setCursor(13, 0);
      lcd.print(" ");
      lcd.setCursor(15, 0);
      lcd.print(" ");
#endif
      lcd.setCursor(0, 1);
      lcd.print("_");
      lcd.setCursor(1, 1);
      lcd.print(seq_find_shortname(1)[0]);
    }
    if (seq_menu > 3 && seq_menu < 19) {
      lcd.setCursor(seq_menu - 3, 1);
      lcd.write(219); // cursor symbol
      lcd.setCursor(seq_menu - 4, 1);
      lcd.print(seq_find_shortname(seq_menu - 4)[0]);
      if (seq_menu < 18)
      {
        lcd.setCursor(seq_menu - 2, 1);
        lcd.print(seq_find_shortname(seq_menu - 2)[0]);
      }
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
    seq_menu = 0;
    seq_active_function = 99;
    lcd_special_chars(SCROLLBAR);
  }
}

void UI_func_arpeggio(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.createChar(0, (uint8_t*)special_chars[19]); //play symbol
    lcd.createChar(2, (uint8_t*)special_chars[21]); //stop symbol
    seq_temp_select_menu = 0;
    seq_temp_active_menu = 0;
    lcd.setCursor( 0, 0);
    lcd.print("Len");
    lcd.setCursor(7, 0);
    lcd.print( seq_chord_names[arp_chord][0]);
    lcd.print( seq_chord_names[arp_chord][1]);
    lcd.print( seq_chord_names[arp_chord][2]);
    lcd.print( seq_chord_names[arp_chord][3]);
    lcd.setCursor( 0, 1);
    lcd.print("Style");
    lcd.setCursor( 11, 1);
    lcd.print("1/16");
    arp_refresh_display_play_status();
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (seq_temp_active_menu == 0)
      {
        if (LCDML.BT_checkDown())
          seq_temp_select_menu = constrain(seq_temp_select_menu + ENCODER[ENC_R].speed(), 0, 3);
        else if (LCDML.BT_checkUp())
          seq_temp_select_menu = constrain(seq_temp_select_menu - ENCODER[ENC_R].speed(), 0, 3);
      }
      else if (seq_temp_active_menu == 1)  // Octave setting
      {
        if (LCDML.BT_checkDown())
          arp_lenght = constrain(arp_lenght + ENCODER[ENC_R].speed(), 0, 9);
        else if (LCDML.BT_checkUp())
          arp_lenght = constrain(arp_lenght - ENCODER[ENC_R].speed(), 0, 9);
      }
      else if (seq_temp_active_menu == 2)  // Style setting
      {
        if (LCDML.BT_checkDown())
          arp_style = constrain(arp_style + ENCODER[ENC_R].speed(), 0, 3);
        else if (LCDML.BT_checkUp())
          arp_style = constrain(arp_style - ENCODER[ENC_R].speed(), 0, 3);
      }
      else if (seq_temp_active_menu == 3)  // Arp Speed setting
      {
        if (LCDML.BT_checkDown())
          arp_speed = constrain(arp_speed + ENCODER[ENC_R].speed(), 0, 1);
        else if (LCDML.BT_checkUp())
          arp_speed = constrain(arp_speed - ENCODER[ENC_R].speed(), 0, 1);
      }

      if (LCDML.BT_checkEnter())  //handle button presses during menu >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      {
        if ( seq_temp_select_menu == 0 && seq_temp_active_menu == 0 )
        {
          seq_temp_active_menu = 1;
        }
        else if ( seq_temp_select_menu == 0 && seq_temp_active_menu == 1 )
        {
          seq_temp_active_menu = 0;
        }
        if ( seq_temp_select_menu == 1 && seq_temp_active_menu == 0 )
        {
          seq_temp_active_menu = 2;
        }
        else if ( seq_temp_select_menu == 1 && seq_temp_active_menu == 2 )
        {
          seq_temp_active_menu = 0;
        }

        else if ( seq_temp_select_menu == 2 )
        {

          if (seq_running) {
            seq_running = !seq_running;
            timer1.stop();
            MicroDexed[0]->panic();
#if NUM_DEXED > 1
            MicroDexed[1]->panic();
#endif
            arp_refresh_display_play_status();
            seq_step = 0;
            arp_octave = 0;
            arp_step = 0;
            seq_chain_active_step = 0;
          } else
          {
            seq_running = !seq_running;
            arp_refresh_display_play_status();
            timer1.start();
          }
        }
        else if ( seq_temp_select_menu == 3 && seq_temp_active_menu == 0 )
        {
          seq_temp_active_menu = 3;
        }
        else if ( seq_temp_select_menu == 3 && seq_temp_active_menu == 3 )
        {
          seq_temp_active_menu = 0;
        }
      }
    }
    lcd.setCursor( 4, 0);
    if (arp_lenght == 0)   lcd.print("A"); else  lcd.print(arp_lenght); //play all elements or from 1-xx elements
    lcd.setCursor( 6, 1);
    lcd.print( arp_style_names[arp_style][0] );
    lcd.print( arp_style_names[arp_style][1] );
    lcd.print( arp_style_names[arp_style][2] );
    lcd.setCursor( 11, 1);
    if (arp_speed == 0)lcd.print("1/16"); else if (arp_speed == 1)lcd.print("1/8 ");

    if (seq_temp_select_menu == 0) {
      lcd.setCursor( 3, 0);
      lcd.print("[");
      lcd.setCursor( 5, 0);
      lcd.print("]");
      lcd.setCursor( 5, 1);
      lcd.print(" ");
      lcd.setCursor( 9, 1);
      lcd.print(" ");
      lcd.setCursor( 13, 1);
      lcd.print(" ");
      lcd.setCursor( 15, 1);
      lcd.print(" ");
    }
    else if (seq_temp_select_menu == 1)
    {
      lcd.setCursor( 5, 1);
      lcd.print("[");
      lcd.setCursor( 9, 1);
      lcd.print("]");
      lcd.setCursor( 3, 0);
      lcd.print(" ");
      lcd.setCursor( 5, 0);
      lcd.print(" ");
      lcd.setCursor( 13, 0);
      lcd.print(" ");
      lcd.setCursor( 15, 0);
      lcd.print(" ");
    }
    else if (seq_temp_select_menu == 2)
    {
      lcd.setCursor( 5, 1);
      lcd.print(" ");
      lcd.setCursor( 9, 1);
      lcd.print(" ");
      lcd.setCursor( 13, 0);
      lcd.print("[");
      lcd.setCursor( 15, 0);
      lcd.print("]");
      lcd.setCursor( 10, 1);
      lcd.print(" ");
      lcd.setCursor( 15, 1);
      lcd.print(" ");
    }
    else if (seq_temp_select_menu == 3)
    {
      lcd.setCursor( 13, 0);
      lcd.print(" ");
      lcd.setCursor( 15, 0);
      lcd.print(" ");
      lcd.setCursor( 10, 1);
      lcd.print("[");
      lcd.setCursor( 15, 1);
      lcd.print("]");
      lcd.setCursor( 3, 0);
      lcd.print(" ");
      lcd.setCursor( 5, 0);
      lcd.print(" ");
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(SCROLLBAR);
  }
}

void UI_func_seq_pat_chain(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    // setup function
    seq_temp_select_menu = 0;
    seq_temp_active_menu = 99;
    lcd.setCursor( 12, 0);
    lcd.print(seq_chain_active_chainstep + 1);
    lcd.setCursor( 13, 0);
    lcd.print("/");
    lcd.setCursor( 14, 0);
    lcd.print(seq_chain_lenght + 1);

    lcd.setCursor(0 , 0);
    if (seq_track_type[0] == 0 ) lcd.print("D"); else if (seq_track_type[0] == 1 ) lcd.print("I"); else if (seq_track_type[0] == 2 ) lcd.print("C"); else lcd.print("A");
    lcd.setCursor(2 , 0);
    lcd.print( seq_patternchain[seq_chain_active_chainstep][0]);

    lcd.setCursor(0 , 1);
    if (seq_track_type[1] == 0 ) lcd.print("D"); else if (seq_track_type[1] == 1 ) lcd.print("I"); else if (seq_track_type[1] == 2 ) lcd.print("C"); else lcd.print("A");
    lcd.setCursor(2 , 1);
    lcd.print( seq_patternchain[seq_chain_active_chainstep][1]);

    lcd.setCursor(6 , 0);
    if (seq_track_type[2] == 0 ) lcd.print("D"); else if (seq_track_type[2] == 1 ) lcd.print("I"); else if (seq_track_type[2] == 2 ) lcd.print("C"); else lcd.print("A");
    lcd.setCursor(8 , 0);
    lcd.print( seq_patternchain[seq_chain_active_chainstep][2]);

    lcd.setCursor(6 , 1);
    if (seq_track_type[3] == 0 ) lcd.print("D"); else if (seq_track_type[3] == 1 ) lcd.print("I"); else if (seq_track_type[3] == 2 ) lcd.print("C"); else lcd.print("A");
    lcd.setCursor(8 , 1);
    lcd.print( seq_patternchain[seq_chain_active_chainstep][3]);
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (seq_temp_active_menu == 99) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_temp_select_menu = constrain(seq_temp_select_menu + 1, 0, 4);
        else if (LCDML.BT_checkUp())
          seq_temp_select_menu = constrain(seq_temp_select_menu - 1, 0, 4);
      }
    } else if (seq_temp_active_menu == 4) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_chain_active_chainstep = constrain(seq_chain_active_chainstep + 1, 0, seq_chain_lenght);
        else if (LCDML.BT_checkUp())
          seq_chain_active_chainstep = constrain(seq_chain_active_chainstep - 1, 0, seq_chain_lenght);
      }
    }
    else if (seq_temp_active_menu < 4) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_patternchain[seq_chain_active_chainstep][seq_temp_active_menu] = constrain(seq_patternchain[seq_chain_active_chainstep][seq_temp_active_menu] + 1, 0, 9);
        else if (LCDML.BT_checkUp())
          seq_patternchain[seq_chain_active_chainstep][seq_temp_active_menu] = constrain(seq_patternchain[seq_chain_active_chainstep][seq_temp_active_menu] - 1, 0, 9);
      }
    }

    if (seq_temp_select_menu == 0 && seq_temp_active_menu == 99) // Drum 0
    {
      lcd.setCursor(1 , 0);
      lcd.print("[");
      lcd.setCursor(3 , 0);
      lcd.print("]");
      lcd.setCursor(1 , 1);
      lcd.print(" ");
      lcd.setCursor(3 , 1);
      lcd.print(" ");
    } else if (seq_temp_select_menu == 1 && seq_temp_active_menu == 99) // Drum 1
    {
      lcd.setCursor(1 , 0);
      lcd.print(" ");
      lcd.setCursor(3 , 0);
      lcd.print(" ");
      lcd.setCursor(7 , 0);
      lcd.print(" ");
      lcd.setCursor(9 , 0);
      lcd.print(" ");
      lcd.setCursor(1 , 1);
      lcd.print("[");
      lcd.setCursor(3 , 1);
      lcd.print("]");
    } else if (seq_temp_select_menu == 2 && seq_temp_active_menu == 99) // Drum 2
    {
      lcd.setCursor(7 , 0);
      lcd.print("[");
      lcd.setCursor(9 , 0);
      lcd.print("]");
      lcd.setCursor(1 , 1);
      lcd.print(" ");
      lcd.setCursor(3 , 1);
      lcd.print(" ");
      lcd.setCursor(7 , 1);
      lcd.print(" ");
      lcd.setCursor(9 , 1);
      lcd.print(" ");
    } else if (seq_temp_select_menu == 3 && seq_temp_active_menu == 99) // Inst
    {
      lcd.setCursor(7 , 0);
      lcd.print(" ");
      lcd.setCursor(9 , 0);
      lcd.print(" ");
      lcd.setCursor(7 , 1);
      lcd.print("[");
      lcd.setCursor(9 , 1);
      lcd.print("]");
      lcd.setCursor( 11, 0);
      lcd.print(" ");
      lcd.setCursor( 15, 0);
      lcd.print(" ");
    } else if (seq_temp_select_menu == 4 && seq_temp_active_menu == 99) // Chainselect
    {
      lcd.setCursor(7 , 1);
      lcd.print(" ");
      lcd.setCursor(9 , 1);
      lcd.print(" ");
      lcd.setCursor( 11, 0);
      lcd.print("[");
      lcd.setCursor( 12, 0);
      lcd.print(seq_chain_active_chainstep + 1);
      lcd.setCursor( 13, 0);
      lcd.print("/");
      lcd.setCursor( 14, 0);
      lcd.print(seq_chain_lenght + 1);
      lcd.setCursor( 15, 0);
      lcd.print("]");
    } else if (seq_temp_select_menu == 4 && seq_temp_active_menu == 4) // Chainselect modify
    {
      lcd.setCursor( 11, 0);
      lcd.print("[");
      lcd.setCursor( 12, 0);
      lcd.print(seq_chain_active_chainstep + 1);
      lcd.setCursor( 13, 0);
      lcd.print("/");
      lcd.setCursor( 14, 0);
      lcd.print(seq_chain_lenght + 1);
      lcd.setCursor( 15, 0);
      lcd.print("]");
      lcd.setCursor(2 , 0);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][0]);
      lcd.setCursor(2 , 1);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][1]);
      lcd.setCursor(8 , 0);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][2]);
      lcd.setCursor(8 , 1);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][3]);
    } else if (seq_temp_select_menu == 0 && seq_temp_active_menu == 0) // Drum 0 modify
    {
      lcd.setCursor(2 , 0);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][0]);
    } else if (seq_temp_select_menu == 1 && seq_temp_active_menu == 1) // Drum 1 modify
    {
      lcd.setCursor(2 , 1);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][1]);
    } else if (seq_temp_select_menu == 2 && seq_temp_active_menu == 2) // Drum 2 modify
    {
      lcd.setCursor(8 , 0);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][2]);
    } else if (seq_temp_select_menu == 3 && seq_temp_active_menu == 3) // Inst modify
    {
      lcd.setCursor(8 , 1);
      lcd.print( seq_patternchain[seq_chain_active_chainstep][3]);
    }

    if (LCDML.BT_checkEnter())  //handle button presses during menu >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    {
      if (seq_temp_active_menu == seq_temp_select_menu) {
        seq_temp_active_menu = 99;
      }  else
      {
        seq_temp_active_menu = seq_temp_select_menu;
      }
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_track_setup(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    // setup function
    seq_temp_active_menu = 99;
    lcd.setCursor(0 , 0);
    lcd.print("T1");
    lcd.setCursor(0 , 1);
    lcd.print("T2");
    lcd.setCursor(9 , 0);
    lcd.print("T3");
    lcd.setCursor(9 , 1);
    lcd.print("T4");
    lcd.setCursor(3 , 0);
    if (seq_track_type[0] == 0 ) lcd.print("Drm"); else if (seq_track_type[0] == 1 ) lcd.print("Ins"); else if (seq_track_type[0] == 2 )lcd.print("Chd"); else lcd.print("Arp");
    lcd.setCursor(3 , 1);
    if (seq_track_type[1] == 0 ) lcd.print("Drm"); else if (seq_track_type[1] == 1 ) lcd.print("Ins"); else if (seq_track_type[1] == 2 )lcd.print("Chd"); else lcd.print("Arp");
    lcd.setCursor(12 , 0);
    if (seq_track_type[2] == 0 ) lcd.print("Drm"); else if (seq_track_type[2] == 1 ) lcd.print("Ins"); else if (seq_track_type[2] == 2 )lcd.print("Chd"); else lcd.print("Arp");
    lcd.setCursor(12 , 1);
    if (seq_track_type[3] == 0 ) lcd.print("Drm"); else if (seq_track_type[3] == 1 ) lcd.print("Ins"); else if (seq_track_type[3] == 2 )lcd.print("Chd"); else lcd.print("Arp");
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (seq_temp_active_menu == 99) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_temp_select_menu = constrain(seq_temp_select_menu + 1, 0, 3);
        else if (LCDML.BT_checkUp())
          seq_temp_select_menu = constrain(seq_temp_select_menu - 1, 0, 3);
      }
    } else  {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_track_type[seq_temp_active_menu] = constrain(seq_track_type[seq_temp_active_menu] + 1, 0, 3);
        else if (LCDML.BT_checkUp())
          seq_track_type[seq_temp_active_menu] = constrain(seq_track_type[seq_temp_active_menu] - 1, 0, 3);
      }
    }
    if (LCDML.BT_checkEnter())  //handle button presses during menu >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    {
      if (seq_temp_active_menu == 99) {
        seq_temp_active_menu = seq_temp_select_menu;
      }  else
      {
        seq_temp_active_menu = 99;
      }
    }
    if (seq_temp_select_menu == 0)
    {
      lcd.setCursor(2 , 0);
      lcd.print("[");
      lcd.setCursor(6 , 0);
      lcd.print("]");
      lcd.setCursor(2 , 1);
      lcd.print(" ");
      lcd.setCursor(6 , 1);
      lcd.print(" ");
      lcd.setCursor(3 , 0);
      if (seq_track_type[0] == 0 ) lcd.print("Drm"); else if (seq_track_type[0] == 1 ) lcd.print("Ins"); else if (seq_track_type[0] == 2 )lcd.print("Chd"); else lcd.print("Arp");
    } else if (seq_temp_select_menu == 1)
    {
      lcd.setCursor(2 , 0);
      lcd.print(" ");
      lcd.setCursor(6 , 0);
      lcd.print(" ");
      lcd.setCursor(2 , 1);
      lcd.print("[");
      lcd.setCursor(6 , 1);
      lcd.print("]");
      lcd.setCursor(11 , 0);
      lcd.print(" ");
      lcd.setCursor(15 , 0);
      lcd.print(" ");
      lcd.setCursor(3 , 1);
      if (seq_track_type[1] == 0 ) lcd.print("Drm"); else if (seq_track_type[1] == 1 ) lcd.print("Ins"); else if (seq_track_type[1] == 2 )lcd.print("Chd"); else lcd.print("Arp");
    } else if (seq_temp_select_menu == 2)
    {
      lcd.setCursor(2 , 1);
      lcd.print(" ");
      lcd.setCursor(6 , 1);
      lcd.print(" ");
      lcd.setCursor(11 , 0);
      lcd.print("[");
      lcd.setCursor(15 , 0);
      lcd.print("]");
      lcd.setCursor(11 , 1);
      lcd.print(" ");
      lcd.setCursor(15 , 1);
      lcd.print(" ");
      lcd.setCursor(12 , 0);
      if (seq_track_type[2] == 0 ) lcd.print("Drm"); else if (seq_track_type[2] == 1 ) lcd.print("Ins"); else if (seq_track_type[2] == 2 )lcd.print("Chd"); else lcd.print("Arp");
    } else if (seq_temp_select_menu == 3)
    {
      lcd.setCursor(11 , 0);
      lcd.print(" ");
      lcd.setCursor(15 , 0);
      lcd.print(" ");
      lcd.setCursor(11 , 1);
      lcd.print("[");
      lcd.setCursor(15 , 1);
      lcd.print("]");
      lcd.setCursor(12 , 1);
      if (seq_track_type[3] == 0 ) lcd.print("Drm"); else if (seq_track_type[3] == 1 ) lcd.print("Ins"); else if (seq_track_type[3] == 2 )lcd.print("Chd"); else lcd.print("Arp");
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_dexed_assign(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    // setup function
    seq_temp_active_menu = 99;
    lcd.setCursor(0 , 0);
    lcd.print("T1");
    lcd.setCursor(0 , 1);
    lcd.print("T2");
    lcd.setCursor(9 , 0);
    lcd.print("T3");
    lcd.setCursor(9 , 1);
    lcd.print("T4");
    lcd.setCursor(3 , 0);
    if (seq_inst_dexed[0] == 0 ) lcd.print("D1"); else lcd.print("D2");
    lcd.setCursor(3 , 1);
    if (seq_inst_dexed[1] == 0 ) lcd.print("D1"); else lcd.print("D2");
    lcd.setCursor(12 , 0);
    if (seq_inst_dexed[2] == 0 ) lcd.print("D1"); else lcd.print("D2");
    lcd.setCursor(12 , 1);
    if (seq_inst_dexed[3] == 0 ) lcd.print("D1"); else lcd.print("D2");
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if (seq_temp_active_menu == 99) {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_temp_select_menu = constrain(seq_temp_select_menu + 1, 0, 3);
        else if (LCDML.BT_checkUp())
          seq_temp_select_menu = constrain(seq_temp_select_menu - 1, 0, 3);
      }
    } else  {
      if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
      {
        if (LCDML.BT_checkDown())
          seq_inst_dexed[seq_temp_active_menu] = constrain(seq_inst_dexed[seq_temp_active_menu] + 1, 0, 1);
        else if (LCDML.BT_checkUp())
          seq_inst_dexed[seq_temp_active_menu] = constrain(seq_inst_dexed[seq_temp_active_menu] - 1, 0, 1);
      }
    }
    if (LCDML.BT_checkEnter())  //handle button presses during menu >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    {
      if (seq_temp_active_menu == 99) {
        seq_temp_active_menu = seq_temp_select_menu;
      }  else
      {
        seq_temp_active_menu = 99;
      }
    }
    if (seq_temp_select_menu == 0)
    {
      lcd.setCursor(2 , 0);
      lcd.print("[");
      lcd.setCursor(6 , 0);
      lcd.print("]");
      lcd.setCursor(2 , 1);
      lcd.print(" ");
      lcd.setCursor(6 , 1);
      lcd.print(" ");
      lcd.setCursor(3 , 0);
      if (seq_inst_dexed[0] == 0 ) lcd.print("D1"); else lcd.print("D2");
    } else if (seq_temp_select_menu == 1)
    {
      lcd.setCursor(2 , 0);
      lcd.print(" ");
      lcd.setCursor(6 , 0);
      lcd.print(" ");
      lcd.setCursor(2 , 1);
      lcd.print("[");
      lcd.setCursor(6 , 1);
      lcd.print("]");
      lcd.setCursor(11 , 0);
      lcd.print(" ");
      lcd.setCursor(15 , 0);
      lcd.print(" ");
      lcd.setCursor(3 , 1);
      if (seq_inst_dexed[1] == 0 ) lcd.print("D1"); else lcd.print("D2");
    } else if (seq_temp_select_menu == 2)
    {
      lcd.setCursor(2 , 1);
      lcd.print(" ");
      lcd.setCursor(6 , 1);
      lcd.print(" ");
      lcd.setCursor(11 , 0);
      lcd.print("[");
      lcd.setCursor(15 , 0);
      lcd.print("]");
      lcd.setCursor(11 , 1);
      lcd.print(" ");
      lcd.setCursor(15 , 1);
      lcd.print(" ");
      lcd.setCursor(12 , 0);
      if (seq_inst_dexed[2] == 0 ) lcd.print("D1"); else lcd.print("D2");
    } else if (seq_temp_select_menu == 3)
    {
      lcd.setCursor(11 , 0);
      lcd.print(" ");
      lcd.setCursor(15 , 0);
      lcd.print(" ");
      lcd.setCursor(11 , 1);
      lcd.print("[");
      lcd.setCursor(15 , 1);
      lcd.print("]");
      lcd.setCursor(12 , 1);
      if (seq_inst_dexed[3] == 0 ) lcd.print("D1"); else lcd.print("D2");
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_state_load(uint8_t param)
{
  static uint8_t mode;
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    char tmp[10];
    if (seq_state_last_loadsave != 200)temp_int = seq_state_last_loadsave; else temp_int = param;
    mode = 0;
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("Load Seq. state "));
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", param);
    lcd.print(tmp);
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        temp_int = constrain(temp_int + ENCODER[ENC_L].speed(), 0, 99);
      }
      else if (LCDML.BT_checkUp())
      {
        temp_int = constrain(temp_int - ENCODER[ENC_L].speed(), 0, 99);
      }
      else if (LCDML.BT_checkEnter())
      {
        mode = 0xff;
        lcd.setCursor(0, 1);
        if (load_sd_seq_json(temp_int) == false)
          lcd.print("Does not exist.");
        else
        {
          load_sd_seq_json(temp_int);
          seq_state_last_loadsave = temp_int;
          lcd.print("Done.           ");
        }
        delay(MESSAGE_WAIT_TIME);
        LCDML.FUNC_goBackToMenu();
      }
    }
    lcd.setCursor(0, 1);
    char tmp[10];
    sprintf(tmp, "[%2d]", temp_int);
    lcd.print(tmp);
    //cd.setCursor(5, 1);
    if (check_sd_seq_exists(temp_int))
    {
      get_sd_seq_name_json(temp_int);
      if ( seq_name_temp[0] != 0 )
        lcd.show(1, 5, 11, seq_name_temp);
      else
        lcd.print(" -- DATA --");
    }
    else lcd.print("           ");
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }
    else
      encoderDir[ENC_R].reset();
  }
}

void UI_func_seq_state_save(uint8_t param)
{
  static bool overwrite;
  static bool yesno;
  static uint8_t mode;
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    char tmp[FILENAME_LEN];
    yesno = false;
    if (seq_state_last_loadsave != 200)temp_int = seq_state_last_loadsave; else temp_int = 0;
    mode = 0;
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("Save Seq. state:"));
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", temp_int);
    lcd.print(tmp);
    sprintf(tmp, "/%s/%d-S.json", SEQ_CONFIG_PATH, temp_int);
    if (SD.exists(tmp))
      overwrite = true;
    else
      overwrite = false;
    if (check_sd_seq_exists(temp_int))
    {
      get_sd_seq_name_json(temp_int);
      if ( seq_name_temp[0] != 0 )
        lcd.show(1, 5, 11, seq_name_temp);
      else
        lcd.print(" -- DATA --");
    }
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        if (mode == 0)
          temp_int = constrain(temp_int + ENCODER[ENC_L].speed(), 0, 99);
        else
          yesno = true;
      }
      else if (LCDML.BT_checkUp())
      {
        if (mode == 0)
          temp_int = constrain(temp_int - ENCODER[ENC_L].speed(), 0, 99);
        else
          yesno = false;
      }
      else if (LCDML.BT_checkEnter())
      {
        if (mode == 0 && overwrite == true)
        {
          mode = 1;
          lcd.setCursor(0, 1);
          lcd.print(F("Overwrite: [   ]"));
        }
        else
        {
          mode = 0xff;
          if (overwrite == false || yesno == true)
          {
            if (yesno == true)
            {
              char tmp[FILENAME_LEN];
              sprintf(tmp, "/%s/%d-S.json", SEQ_CONFIG_PATH, temp_int);
              SD.remove(tmp);
            }
            save_sd_seq_json(temp_int);
            lcd.show(1, 0, 16, "Done.");
            seq_state_last_loadsave = temp_int;
            delay(MESSAGE_WAIT_TIME);
            LCDML.FUNC_goBackToMenu();
          }
          else if (overwrite == true && yesno == false)
          {
            char tmp[10];

            mode = 0;
            lcd.setCursor(0, 1);
            sprintf(tmp, "[%2d]   ", temp_int);
            lcd.print(tmp);
          }
        }
      }
      if (mode == 0)
      {
        char tmp[FILENAME_LEN];
        sprintf(tmp, "/%s/%d-S.json", SEQ_CONFIG_PATH, temp_int);
        if (SD.exists(tmp))
          overwrite = true;
        else
          overwrite = false;

        lcd.setCursor(0, 1);
        sprintf(tmp, "[%2d]", temp_int);
        lcd.print(tmp);
        lcd.setCursor(5, 1);
        if (overwrite == false) {
          lcd.print("-- empty --");
        } else if (check_sd_seq_exists(temp_int))
        {
          get_sd_seq_name_json(temp_int);
          if ( seq_name_temp[0] != 0 )
            lcd.show(1, 5, 11, seq_name_temp); else
            lcd.print("-- DATA --");
        }
        else lcd.print("          ");
      }
      else
      {
        lcd.setCursor(12, 1);
        if (yesno == true)
          lcd.print(F("YES"));
        else
          lcd.print(F("NO "));
      }
    }
    encoderDir[ENC_R].reset();
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }
    encoderDir[ENC_R].reset();
  }
}

void UI_func_information(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    char version_string[LCD_cols + 1];

    encoderDir[ENC_R].reset();

    generate_version_string(version_string, sizeof(version_string));

    // setup function
    lcd.setCursor(0, 0);
    lcd.print(version_string);
    lcd.setCursor(0, 1);
    lcd.print(sd_string);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    ;
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_midi_soft_thru(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("MIDI Soft THRU"));
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.sys.soft_midi_thru = constrain(configuration.sys.soft_midi_thru + 1, SOFT_MIDI_THRU_MIN, SOFT_MIDI_THRU_MAX);
      else if (LCDML.BT_checkUp())
        configuration.sys.soft_midi_thru = constrain(configuration.sys.soft_midi_thru - 1, SOFT_MIDI_THRU_MIN, SOFT_MIDI_THRU_MAX);
    }

    lcd.setCursor(0, 1);
    switch (configuration.sys.soft_midi_thru)
    {
      case 0:
        lcd.print(F("[OFF]"));
        break;
      case 1:
        lcd.print(F("[ON ]"));
        break;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, sys.soft_midi_thru), configuration.sys.soft_midi_thru);
  }
}

void UI_func_smart_filter(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Drm Smart Filter"));
  }
  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        smartfilter = !smartfilter;
      else if (LCDML.BT_checkUp())
        smartfilter = !smartfilter;
    }
    lcd.setCursor(0, 1);
    if (smartfilter) lcd.print(F("[ON ]")); else  lcd.print(F("[OFF]"));
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_func_velocity_level(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd_special_chars(BLOCKBAR);
    lcd_display_bar_int("Velocity Lvl", configuration.dexed[selected_instance_id].velocity_level, 1.0, VELOCITY_LEVEL_MIN, VELOCITY_LEVEL_MAX, 3, false, false, true);

    lcd_active_instance_number(selected_instance_id);
    UI_update_instance_icons();
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
        configuration.dexed[selected_instance_id].velocity_level = constrain(configuration.dexed[selected_instance_id].velocity_level + ENCODER[ENC_R].speed(), VELOCITY_LEVEL_MIN, VELOCITY_LEVEL_MAX);
      else if (LCDML.BT_checkUp())
        configuration.dexed[selected_instance_id].velocity_level = constrain(configuration.dexed[selected_instance_id].velocity_level - ENCODER[ENC_R].speed(), VELOCITY_LEVEL_MIN, VELOCITY_LEVEL_MAX);
    }
#if NUM_DEXED > 1
    else if (LCDML.BT_checkEnter())
    {
      selected_instance_id = !selected_instance_id;
      lcd_active_instance_number(selected_instance_id);
      UI_update_instance_icons();
    }
#endif

    lcd_display_bar_int("Velocity Lvl", configuration.dexed[selected_instance_id].velocity_level, 1.0, VELOCITY_LEVEL_MIN, VELOCITY_LEVEL_MAX, 3, false, false, false);
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    if (selected_instance_id == 0)
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[0].velocity_level), configuration.dexed[0].velocity_level);
#if NUM_DEXED > 1
    else
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, dexed[1].velocity_level), configuration.dexed[1].velocity_level);
#endif
  }
}

void UI_func_eeprom_reset(uint8_t param)
{
  static bool yesno = false;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    // setup function
    lcd.print("Reset EEPROM?");
    lcd.setCursor(0, 1);
    lcd.print("[NO ]");
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
        yesno = true;
      else if (LCDML.BT_checkUp())
        yesno = false;
      else if (LCDML.BT_checkEnter())
      {
        if (yesno == true)
        {
          LCDML.DISP_clear();
          lcd.print("EEPROM Reset");

          initial_values_from_eeprom(true);
          lcd.setCursor(0, 1);
          lcd.print("Done.");
          delay(MESSAGE_WAIT_TIME);
          _softRestart();
        }
        else
        {
          lcd.setCursor(0, 1);
          lcd.print("Canceled.");
          delay(MESSAGE_WAIT_TIME);
          LCDML.FUNC_goBackToMenu();
        }
      }

      if (yesno == true)
      {
        lcd.setCursor(1, 1);
        lcd.print("YES");
      }
      else
      {
        lcd.setCursor(1, 1);
        lcd.print("NO ");
      }
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd.setCursor(0, 1);
    lcd.print("Canceled.");
    delay(MESSAGE_WAIT_TIME);

    encoderDir[ENC_R].reset();
  }
}

void UI_update_instance_icons()
{
#ifdef TESTDISPLAY20x4
  lcd.setCursor(18, 0);
  lcd.write(0);  //Icon for first instance
  lcd.setCursor(19, 0);
  lcd.write(1); //Icon for second instance
#else
  lcd.setCursor(14, 0);
  lcd.write(0);  //Icon for first instance
  lcd.setCursor(15, 0);
  lcd.write(1); //Icon for second instance
#endif
}

void UI_func_voice_select(uint8_t param)
{
  static uint8_t menu_voice_select = MENU_VOICE_SOUND;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
#ifdef TESTDISPLAY20x4
    lcd.setCursor(0, 3);
    lcd.print("MENU");
    lcd.setCursor(17, 3);
    lcd.print("+/-");
#endif
    encoderDir[ENC_R].reset();

    lcd_active_instance_number(selected_instance_id);

    char bank_name[BANK_NAME_LEN];
    char voice_name[VOICE_NAME_LEN];

    if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
      strcpy(bank_name, "*ERROR*");
    if (!get_voice_by_bank_name(configuration.performance.bank[selected_instance_id], bank_name, configuration.performance.voice[selected_instance_id], voice_name, sizeof(voice_name)))
      strcpy(voice_name, "*ERROR*");

    UI_update_instance_icons();

    lcd.createChar(2, (uint8_t*)special_chars[18]); // favorites symbol

#ifdef TEENSY3_6
    lcd.createChar(6, (uint8_t*)special_chars[16]); // MIDI activity note symbol
    lcd.createChar(7, (uint8_t*)special_chars[16]); // MIDI activity note symbol
#endif
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    char bank_name[BANK_NAME_LEN];
    char voice_name[VOICE_NAME_LEN];

    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && (encoderDir[ENC_R].ButtonShort() || encoderDir[ENC_R].ButtonLong())))
    {
      uint8_t bank_tmp;
      int8_t voice_tmp;

      // Reset Performance Modifiers to 0 after every preset change
      for (uint8_t count_tmp = 0; count_tmp < NUM_DEXED; count_tmp++)
      {
        perform_attack_mod[count_tmp] = 0;
        perform_release_mod[count_tmp] = 0;
      }
      active_perform_page = 1;

      if (LCDML.BT_checkUp())
      {
        //start : show all presets
        if (configuration.sys.favorites == 0)
        {
          switch (menu_voice_select)
          {
            case MENU_VOICE_BANK:
              memset(g_bank_name[selected_instance_id], 0, BANK_NAME_LEN);
              bank_tmp = constrain(configuration.performance.bank[selected_instance_id] - ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);
              configuration.performance.bank[selected_instance_id] = bank_tmp;
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(false);
#endif
              load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(true);
#endif
              break;
            case MENU_VOICE_SOUND:
              memset(g_voice_name[selected_instance_id], 0, VOICE_NAME_LEN);
              voice_tmp = configuration.performance.voice[selected_instance_id] - ENCODER[ENC_R].speed();
              if (voice_tmp < 0 && configuration.performance.bank[selected_instance_id] - 1 >= 0)
              {
                configuration.performance.bank[selected_instance_id]--;
                configuration.performance.bank[selected_instance_id] = constrain(configuration.performance.bank[selected_instance_id], 0, MAX_BANKS - 1);
              }
              else if (voice_tmp < 0 && configuration.performance.bank[selected_instance_id] - 1 <= 0)
              {
                voice_tmp = 0;
              }
              if (voice_tmp < 0)
                voice_tmp = MAX_VOICES + voice_tmp;
              configuration.performance.voice[selected_instance_id] = constrain(voice_tmp, 0, MAX_VOICES - 1);

#ifdef DISPLAY_LCD_SPI
              change_disp_sd(false);
#endif
              load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(true);
#endif
              break;
          }
        }
        else //only Favs
          if (configuration.sys.favorites == 1)
          {
            locate_previous_favorite();
#ifdef DISPLAY_LCD_SPI
            change_disp_sd(false);
#endif
            load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
            change_disp_sd(true);
#endif
          }
          else  //only non-Favs
            if (configuration.sys.favorites == 2)
            {
              locate_previous_non_favorite();
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(false);
#endif
              load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(true);
#endif
              //break;

            } else  //random non-Favs
              if (configuration.sys.favorites == 3)
              {
                locate_random_non_favorite();

#ifdef DISPLAY_LCD_SPI
                change_disp_sd(false);
#endif
                load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
                change_disp_sd(true);
#endif
              }
      }  //end UP
      else if (LCDML.BT_checkDown())
      {
        //start : show all presets
        if (configuration.sys.favorites == 0)
        {
          switch (menu_voice_select)
          {
            case MENU_VOICE_BANK:
              memset(g_bank_name[selected_instance_id], 0, BANK_NAME_LEN);
              bank_tmp = constrain(configuration.performance.bank[selected_instance_id] + ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);
              configuration.performance.bank[selected_instance_id] = bank_tmp;
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(false);
#endif
              load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(true);
#endif
              break;
            case MENU_VOICE_SOUND:
              memset(g_voice_name[selected_instance_id], 0, VOICE_NAME_LEN);
              voice_tmp = configuration.performance.voice[selected_instance_id] + ENCODER[ENC_R].speed();
              if (voice_tmp >= MAX_VOICES && configuration.performance.bank[selected_instance_id] + 1 < MAX_BANKS)
              {
                voice_tmp %= MAX_VOICES;
                configuration.performance.bank[selected_instance_id]++;
                configuration.performance.bank[selected_instance_id] = constrain(configuration.performance.bank[selected_instance_id], 0, MAX_BANKS - 1);
              }
              else if (voice_tmp >= MAX_VOICES && configuration.performance.bank[selected_instance_id] + 1 >= MAX_BANKS)
              {
                voice_tmp = MAX_VOICES - 1;
              }
              configuration.performance.voice[selected_instance_id] =  constrain(voice_tmp, 0, MAX_VOICES - 1);

#ifdef DISPLAY_LCD_SPI
              change_disp_sd(false);
#endif
              load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(true);
#endif
              break;
          }
        }
        else //only Favs
          if (configuration.sys.favorites == 1)
          {

            locate_next_favorite();

#ifdef DISPLAY_LCD_SPI
            change_disp_sd(false);
#endif
            load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
            change_disp_sd(true);
#endif
            //break;
          }
          else  //only non-Favs
            if (configuration.sys.favorites == 2)
            {
              locate_next_non_favorite();
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(false);
#endif
              load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
              change_disp_sd(true);
#endif
              //break;
            } else  //random non-Favs
              if (configuration.sys.favorites == 3)
              {
                locate_random_non_favorite();
#ifdef DISPLAY_LCD_SPI
                change_disp_sd(false);
#endif
                load_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#ifdef DISPLAY_LCD_SPI
                change_disp_sd(true);
#endif
              }

      }
      else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonPressed())
      {
        if (menu_voice_select == MENU_VOICE_BANK)
          menu_voice_select = MENU_VOICE_SOUND;
        else
          menu_voice_select = MENU_VOICE_BANK;
      }
#if NUM_DEXED > 1
      else if (LCDML.BT_checkEnter())
      {
        selected_instance_id = !selected_instance_id;
        lcd_active_instance_number(selected_instance_id);
        UI_update_instance_icons();
      }
#endif
    }

    if (strlen(g_bank_name[selected_instance_id]) > 0)
    {
      strcpy(bank_name, g_bank_name[selected_instance_id]);
    }
    else
    {
      if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
        strcpy(bank_name, "*ERROR*");
    }

    if (strlen(g_voice_name[selected_instance_id]) > 0)
    {
      strcpy(voice_name, g_voice_name[selected_instance_id]);
    }
    else
    {
      if (!get_voice_by_bank_name(configuration.performance.bank[selected_instance_id], bank_name, configuration.performance.voice[selected_instance_id], voice_name, sizeof(voice_name)))
        strcpy(voice_name, "*ERROR*");
    }

    lcd.show(0, 0, 2, configuration.performance.bank[selected_instance_id]);
    lcd.show(1, 0, 2, configuration.performance.voice[selected_instance_id] + 1);
#ifdef TESTDISPLAY20x4
    string_toupper(bank_name);
    lcd.show(0, 4, 8, bank_name);
    string_toupper(voice_name);
    lcd.show(1, 4, 10, voice_name);
    switch (menu_voice_select)
    {
      case MENU_VOICE_BANK:
        lcd.show(0, 3, 1, "[");
        lcd.show(0, 12, 1, "]");
        lcd.show(1, 3, 1, " ");
        lcd.show(1, 14, 1, " ");
        break;
      case MENU_VOICE_SOUND:
        lcd.show(0, 3, 1, " ");
        lcd.show(0, 12, 1, " ");
        lcd.show(1, 3, 1, "[");
        lcd.show(1, 14, 1, "]");
        break;
    }
#else
    string_toupper(bank_name);
    lcd.show(0, 3, 8, bank_name);
    lcd.show(0, 12, 1, " ");  //forced because this char does not clear after fav-search (because the bank name is one char to short to do it).
    string_toupper(voice_name);
    lcd.show(1, 3, 10, voice_name);

    switch (menu_voice_select)
    {
      case MENU_VOICE_BANK:
        lcd.show(0, 2, 1, "[");
        lcd.show(0, 11, 1, "]");
        lcd.show(1, 2, 1, " ");
        lcd.show(1, 13, 1, " ");
        break;
      case MENU_VOICE_SOUND:
        lcd.show(0, 2, 1, " ");
        lcd.show(0, 11, 1, " ");
        lcd.show(1, 2, 1, "[");
        lcd.show(1, 13, 1, "]");
        break;
    }
#endif
    draw_favorite_icon(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    encoderDir[ENC_R].reset();
    if (selected_instance_id == 0)
    {
      //eeprom_update_var(offsetof(configuration_s, performance.voice[0]), configuration.performance.voice[0], "configuration.performance.voice[0]");
      //eeprom_update_var(offsetof(configuration_s, performance.bank[0]), configuration.performance.bank[0], "configuration.performance.bank[0]");
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voice[0]), configuration.performance.voice[0]);
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.bank[0]), configuration.performance.bank[0]);
    }
#if NUM_DEXED > 1
    else
    {
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voice[1]), configuration.performance.voice[1]);
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.bank[1]), configuration.performance.bank[1]);
    }
#endif
  }
}

void UI_func_volume(uint8_t param)
{

  char tmp[6];
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {

    encoderDir[ENC_L].reset();

    if (active_perform_page == 1)
    { //Master Volume
      lcd_special_chars(BLOCKBAR);
      lcd_display_bar_int("Master Vol.", configuration.sys.vol, 1.0, VOLUME_MIN, VOLUME_MAX, 3, false, false, true);
      back_from_volume = 0;
    }

    else if (active_perform_page == 2)
    { // Live Performance Mod - Attack
      lcd.setCursor(0, 0);
      lcd.print("Live Modify");
      lcd.setCursor(0, 1);
      lcd.print("Attack =      ");
      lcd.setCursor(13, 1);
      sprintf(tmp, "%03d", perform_attack_mod[selected_instance_id]);
      lcd.print(tmp);
      back_from_volume = 0;
    }

    else if (active_perform_page == 3)
    { // Live Performance Mod - Release
      lcd.setCursor(0, 0);
      lcd.print("Live Modify");
      lcd.setCursor(11, 1);
      lcd.print("Release =    ");
      lcd.setCursor(13, 1);
      sprintf(tmp, "%03d", perform_release_mod[selected_instance_id]);
      lcd.print(tmp);
      back_from_volume = 0;
    }

    lcd.setCursor(12, 0);
    lcd.print("P");
    lcd.setCursor(13, 0);
    lcd.print(active_perform_page);
    lcd.setCursor(14, 0);
    lcd.print("/3");

  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {

    if ( LCDML.BT_checkDown() && encoderDir[ENC_R].Down() )
    {
      back_from_volume = 0;
      active_perform_page++;
      if (active_perform_page > 3)active_perform_page = 1;
    }
    else if ( LCDML.BT_checkUp() && encoderDir[ENC_R].Up() )
    {
      back_from_volume = 0;
      active_perform_page--;
      if (active_perform_page < 1)active_perform_page = 3;
    }

    if ((LCDML.BT_checkDown() && encoderDir[ENC_L].Down() ) || (LCDML.BT_checkUp() && encoderDir[ENC_L].Up() ))
    {
      if (active_perform_page == 1) {
        back_from_volume = 0;

        if (LCDML.BT_checkDown() )
        {
          configuration.sys.vol = constrain(configuration.sys.vol + ENCODER[ENC_L].speed(), VOLUME_MIN, VOLUME_MAX);
        }
        else if (LCDML.BT_checkUp() )
        {
          configuration.sys.vol = constrain(configuration.sys.vol - ENCODER[ENC_L].speed(), VOLUME_MIN, VOLUME_MAX);
        }
      }

      else if ( active_perform_page == 2)
      { //Attack

        if (LCDML.BT_checkDown()   )
        {
          if (perform_attack_mod[selected_instance_id] == 0)
            for (uint8_t i = 0; i < 6; i++) {
              orig_attack_values[selected_instance_id][i] = MicroDexed[selected_instance_id]->getOPRate(i, ATTACK);
            }
          perform_attack_mod[selected_instance_id] = constrain(perform_attack_mod[selected_instance_id] + ENCODER[ENC_L].speed(), -MAX_PERF_MOD, MAX_PERF_MOD);
          for (uint8_t i = 0; i < 6; i++)
            MicroDexed[selected_instance_id]->setOPRate(i, ATTACK, orig_attack_values[selected_instance_id][i] - perform_attack_mod[selected_instance_id] );
        }
        else if (LCDML.BT_checkUp() )
        {
          if (perform_attack_mod[selected_instance_id] == 0)  // Save initial Values
            for (uint8_t i = 0; i < 6; i++) {
              orig_attack_values[selected_instance_id][i] = MicroDexed[selected_instance_id]->getOPRate(i, ATTACK);
            }

          perform_attack_mod[selected_instance_id] = constrain(perform_attack_mod[selected_instance_id] - ENCODER[ENC_L].speed(), -MAX_PERF_MOD, MAX_PERF_MOD);
          for (uint8_t i = 0; i < 6; i++)
            MicroDexed[selected_instance_id]->setOPRate(i, ATTACK, orig_attack_values[selected_instance_id][i] - perform_attack_mod[selected_instance_id] );
        }

      }
      else if (active_perform_page == 3)
      { //Release

        if (LCDML.BT_checkDown() )
        {
          if (perform_release_mod[selected_instance_id] == 0) // Save initial Values
            for (uint8_t i = 0; i < 6; i++) {
              orig_release_values[selected_instance_id][i] = MicroDexed[selected_instance_id]->getOPRate(i, RELEASE);
            }
          perform_release_mod[selected_instance_id] = constrain(perform_release_mod[selected_instance_id] + ENCODER[ENC_L].speed(), -MAX_PERF_MOD, MAX_PERF_MOD);
          for (uint8_t i = 0; i < 6; i++)
            MicroDexed[selected_instance_id]->setOPRate(i, RELEASE, orig_release_values[selected_instance_id][i] - perform_release_mod[selected_instance_id] );
        }
        else if (LCDML.BT_checkUp() )
        {
          if (perform_release_mod[selected_instance_id] == 0)
            for (uint8_t i = 0; i < 6; i++) {
              orig_release_values[selected_instance_id][i] = MicroDexed[selected_instance_id]->getOPRate(i, RELEASE);
            }
          perform_release_mod[selected_instance_id] = constrain(perform_release_mod[selected_instance_id] - ENCODER[ENC_L].speed(), -MAX_PERF_MOD, MAX_PERF_MOD);
          for (uint8_t i = 0; i < 6; i++)
            MicroDexed[selected_instance_id]->setOPRate(i, RELEASE, orig_release_values[selected_instance_id][i] - perform_release_mod[selected_instance_id] );
        }
      }
    }

    lcd.setCursor(13, 0);
    lcd.print(active_perform_page);

    if (active_perform_page == 1) { //Master Volume
      lcd.setCursor(0, 0);
      lcd.print("Master Vol.  ");
      lcd_special_chars(BLOCKBAR);
      lcd_display_bar_int("Master Vol.", configuration.sys.vol, 1.0, VOLUME_MIN, VOLUME_MAX, 3, false, false, false);
      set_volume(configuration.sys.vol, configuration.sys.mono);
    }
    else if (active_perform_page == 2) { //Attack
      lcd.setCursor(0, 0);
      lcd.print("Live Modify");
      lcd.setCursor(0, 1);
      lcd.print("Attack =     ");
      lcd.setCursor(13, 1);
      sprintf(tmp, "%03d", perform_attack_mod[selected_instance_id]);
      lcd.print(tmp);
      back_from_volume = 0;
    }
    else if (active_perform_page == 3) { //Release
      lcd.setCursor(0, 0);
      lcd.print("Live Modify");
      lcd.setCursor(0, 1);
      lcd.print("Release =    ");
      lcd.setCursor(13, 1);
      sprintf(tmp, "%03d", perform_release_mod[selected_instance_id]);
      lcd.print(tmp);
      back_from_volume = 0;
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, sys.vol), configuration.sys.vol);
    encoderDir[ENC_L].reset();
  }

}

void UI_func_load_performance(uint8_t param)
{
  static uint8_t mode;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    char tmp[10];

    mode = 0;

    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Load Perf. SD"));
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", configuration.sys.performance_number);
    lcd.print(tmp);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.sys.performance_number = constrain(configuration.sys.performance_number + ENCODER[ENC_L].speed(), PERFORMANCE_NUM_MIN, PERFORMANCE_NUM_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.sys.performance_number = constrain(configuration.sys.performance_number - ENCODER[ENC_L].speed(), PERFORMANCE_NUM_MIN, PERFORMANCE_NUM_MAX);
      }
      else if (LCDML.BT_checkEnter())
      {
        mode = 0xff;

        lcd.setCursor(0, 1);
        if (load_sd_performance_json(configuration.sys.performance_number) == false)
          lcd.print("Does not exist.");
        else
        {
          load_sd_voiceconfig_json(configuration.performance.voiceconfig_number[0], 0, 0);
          set_voiceconfig_params(0);
#if NUM_DEXED > 1
          load_sd_voiceconfig_json(configuration.performance.voiceconfig_number[1], 1, 0);
          set_voiceconfig_params(1);
#endif
          load_sd_fx_json(configuration.performance.fx_number, 0);
          set_fx_params();

          lcd.print("Done.           ");
        }
        delay(MESSAGE_WAIT_TIME);

        LCDML.FUNC_goBackToMenu();
      }

      lcd.setCursor(0, 1);
      char tmp[10];
      sprintf(tmp, "[%2d]", configuration.sys.performance_number);
      lcd.print(tmp);
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }
    else
      eeprom_update_performance();

    encoderDir[ENC_R].reset();
  }
}

void UI_func_save_performance(uint8_t param)
{
  static bool overwrite;
  static bool yesno;
  static uint8_t mode;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    char tmp[FILENAME_LEN];

    yesno = false;
    mode = 0;

    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Save Perf. SD"));
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", configuration.sys.performance_number);
    lcd.print(tmp);

    sprintf(tmp, "/%s/%s%d.json", PERFORMANCE_CONFIG_PATH, PERFORMANCE_CONFIG_NAME, configuration.sys.performance_number);
    if (SD.exists(tmp))
      overwrite = true;
    else
      overwrite = false;
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        if (mode == 0)
          configuration.sys.performance_number = constrain(configuration.sys.performance_number + ENCODER[ENC_L].speed(), PERFORMANCE_NUM_MIN, PERFORMANCE_NUM_MAX);
        else
          yesno = true;
      }
      else if (LCDML.BT_checkUp())
      {
        if (mode == 0)
          configuration.sys.performance_number = constrain(configuration.sys.performance_number - ENCODER[ENC_L].speed(), PERFORMANCE_NUM_MIN, PERFORMANCE_NUM_MAX);
        else
          yesno = false;
      }
      else if (LCDML.BT_checkEnter())
      {
        if (mode == 0 && overwrite == true)
        {
          mode = 1;
          lcd.setCursor(0, 1);
          lcd.print(F("Overwrite: [   ]"));
        }
        else
        {
          mode = 0xff;
          if (overwrite == false || yesno == true)
          {
            if (yesno == true)
            {
              char tmp[FILENAME_LEN];
              sprintf(tmp, "/%s/%s%d.json", PERFORMANCE_CONFIG_PATH, PERFORMANCE_CONFIG_NAME, configuration.sys.performance_number);
              SD.remove(tmp);
            }
            save_sd_performance_json(configuration.sys.performance_number);
            lcd.show(1, 0, 16, "Done.");
            delay(MESSAGE_WAIT_TIME);
            LCDML.FUNC_goBackToMenu();
          }
          else if (overwrite == true && yesno == false)
          {
            char tmp[17];

            mode = 0;
            lcd.setCursor(0, 1);
            sprintf(tmp, "[%2d]            ", configuration.sys.performance_number);
            lcd.print(tmp);
          }
        }
      }

      if (mode == 0)
      {
        char tmp[FILENAME_LEN];
        sprintf(tmp, "/%s/%s%d.json", PERFORMANCE_CONFIG_PATH, PERFORMANCE_CONFIG_NAME, configuration.sys.performance_number);
        if (SD.exists(tmp))
          overwrite = true;
        else
          overwrite = false;

        lcd.setCursor(0, 1);
        sprintf(tmp, "[%2d]", configuration.sys.performance_number);
        lcd.print(tmp);
      }
      else
      {
        lcd.setCursor(12, 1);
        if (yesno == true)
          lcd.print(F("YES"));
        else
          lcd.print(F("NO "));
      }
    }
    encoderDir[ENC_R].reset();
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }

    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, sys.performance_number), configuration.sys.performance_number);

    encoderDir[ENC_R].reset();
  }
}

void UI_func_load_voiceconfig(uint8_t param)
{
#if NUM_DEXED > 1
  static int8_t selected_instance_id;
#else
  char tmp[4];
  uint8_t selected_instance_id = 0;
#endif

  static uint8_t mode;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {

    selected_instance_id = 0;

    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Load VoiceCfg SD"));
#if NUM_DEXED > 1
    mode = 0;
    lcd.setCursor(0, 1);
    lcd.print(F("Instance [0]"));
#else
    mode = 1;
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", configuration.performance.voiceconfig_number[selected_instance_id]);
    lcd.print(tmp);
#endif
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        if (mode == 0)
          selected_instance_id = 1;
        //selected_instance_id = (selected_instance_id + 1) % 2;
        else if (mode == 1)
          configuration.performance.voiceconfig_number[selected_instance_id] = constrain(configuration.performance.voiceconfig_number[selected_instance_id] + ENCODER[ENC_L].speed(), VOICECONFIG_NUM_MIN, VOICECONFIG_NUM_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        if (mode == 0)
          //selected_instance_id = (selected_instance_id - 1) % 2;
          selected_instance_id = 0;
        else if (mode == 1)
          configuration.performance.voiceconfig_number[selected_instance_id] = constrain(configuration.performance.voiceconfig_number[selected_instance_id] - ENCODER[ENC_L].speed(), VOICECONFIG_NUM_MIN, VOICECONFIG_NUM_MAX);
      }
      else if (LCDML.BT_checkEnter())
      {
        if (mode > 0) {
          mode = 0xff;
          lcd.setCursor(0, 1);
          if (load_sd_voiceconfig_json(configuration.performance.voiceconfig_number[selected_instance_id], selected_instance_id, 0) == false)
            lcd.print("Does not exist. ");
          else
            lcd.print("Done.           ");

          delay(MESSAGE_WAIT_TIME);

          LCDML.FUNC_goBackToMenu();
        } else mode = 1;
      }

      if (mode == 0)
      {
        lcd.setCursor(10, 1);
        lcd.print(selected_instance_id);
      }
      else if (mode == 1)
      {
        lcd.setCursor(0, 1);
        char tmp[10];
        sprintf(tmp, "[%2d]", configuration.performance.voiceconfig_number[selected_instance_id]);
        lcd.print(tmp);
      }
    }
    encoderDir[ENC_R].reset();
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }
    else
      eeprom_update_dexed(selected_instance_id);

#if NUM_DEXED > 1
    if (selected_instance_id > 0)
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voiceconfig_number[1]), configuration.performance.voiceconfig_number[1]);
    else
#endif
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voiceconfig_number[0]), configuration.performance.voiceconfig_number[0]);

    encoderDir[ENC_R].reset();
  }
}

void UI_func_save_voiceconfig(uint8_t param)
{
#if NUM_DEXED > 1
  static int8_t selected_instance_id;
#else
  char tmp[5];
  uint8_t selected_instance_id = 0;
#endif

  static bool overwrite;
  static bool yesno;
  static uint8_t mode;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    yesno = false;
    selected_instance_id = 0;

    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Save VoiceCfg SD"));
#if NUM_DEXED > 1
    mode = 0;
    lcd.setCursor(0, 1);
    lcd.print(F("Instance [0]"));
#else
    mode = 1;
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", configuration.performance.voiceconfig_number[selected_instance_id]);
    lcd.print(tmp);

    sprintf(tmp, "/%s/%s%d.json", VOICE_CONFIG_PATH, VOICE_CONFIG_NAME, configuration.performance.voiceconfig_number[selected_instance_id]);
    if (SD.exists(tmp))
      overwrite = true;
    else
      overwrite = false;
#endif
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        if (mode == 0)
          selected_instance_id = 1;
        // selected_instance_id = (selected_instance_id +1) % 2;
        else if (mode == 1)
          configuration.performance.voiceconfig_number[selected_instance_id] = constrain(configuration.performance.voiceconfig_number[selected_instance_id] + ENCODER[ENC_L].speed(), VOICECONFIG_NUM_MIN, VOICECONFIG_NUM_MAX);
        else
          yesno = true;
      }
      else if (LCDML.BT_checkUp())
      {
        if (mode == 0)
          selected_instance_id = 0;
        //selected_instance_id = (selected_instance_id - 1) % 2;
        else if (mode == 1)
          configuration.performance.voiceconfig_number[selected_instance_id] = constrain(configuration.performance.voiceconfig_number[selected_instance_id] - ENCODER[ENC_L].speed(), VOICECONFIG_NUM_MIN, VOICECONFIG_NUM_MAX);
        else
          yesno = false;
      }
      else if (LCDML.BT_checkEnter())
      {
        if (mode == 1 && overwrite == true)
        {
          mode = 2;
          lcd.setCursor(0, 1);
          lcd.print(F("Overwrite: [   ]"));
        }
        else if (mode > 0 )
        {
          mode = 0xff;
          if (overwrite == false || yesno == true)
          {
            if (yesno == true)
            {
              char tmp[FILENAME_LEN];
              sprintf(tmp, "/%s/%s%d.json", VOICE_CONFIG_PATH, VOICE_CONFIG_NAME, configuration.performance.voiceconfig_number[selected_instance_id]);
              SD.remove(tmp);
            }
            save_sd_voiceconfig_json(configuration.performance.voiceconfig_number[selected_instance_id], selected_instance_id, 0);
            lcd.show(1, 0, 16, "Done.");
            delay(MESSAGE_WAIT_TIME);
            LCDML.FUNC_goBackToMenu();
          }
          else if (overwrite == true && yesno == false)
          {
            char tmp[17];

            mode = 1;
            lcd.setCursor(0, 1);
            sprintf(tmp, "[%2d]            ", configuration.performance.voiceconfig_number[selected_instance_id]);
            lcd.print(tmp);
          }
        }
        else {
          mode = 1;
          lcd.setCursor(4, 1);
          lcd.print("          ");
        }
      }

      if (mode == 0)
      {
        lcd.setCursor(10, 1);
        //lcd.print(configuration.performance.voiceconfig_number[selected_instance_id]);
        lcd.print(selected_instance_id);
      }
      else if (mode == 1)
      {
        char tmp[FILENAME_LEN];

        sprintf(tmp, "/%s/%s%d.json", VOICE_CONFIG_PATH, VOICE_CONFIG_NAME, configuration.performance.voiceconfig_number[selected_instance_id]);
        if (SD.exists(tmp))
          overwrite = true;
        else
          overwrite = false;

        lcd.setCursor(0, 1);
        sprintf(tmp, "[%2d]", configuration.performance.voiceconfig_number[selected_instance_id]);
        lcd.print(tmp);
      }
      else if (mode == 2)
      {
        lcd.setCursor(12, 1);
        if (yesno == true)
          lcd.print(F("YES"));
        else
          lcd.print(F("NO "));
      }
    }
    encoderDir[ENC_R].reset();
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }

#if NUM_DEXED > 1
    if (selected_instance_id > 0)
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voiceconfig_number[1]), configuration.performance.voiceconfig_number[1]);
    else
#endif
      EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voiceconfig_number[0]), configuration.performance.voiceconfig_number[0]);

    encoderDir[ENC_R].reset();
  }
}

void UI_func_load_fx(uint8_t param)
{
  static uint8_t mode;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    char tmp[10];

    mode = 0;

    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Load FX SD"));
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", configuration.performance.fx_number);
    lcd.print(tmp);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.performance.fx_number = constrain(configuration.performance.fx_number + ENCODER[ENC_L].speed(), FX_NUM_MIN, FX_NUM_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.performance.fx_number = constrain(configuration.performance.fx_number - ENCODER[ENC_L].speed(), FX_NUM_MIN, FX_NUM_MAX);
      }
      else if (LCDML.BT_checkEnter())
      {
        mode = 0xff;

        lcd.setCursor(0, 1);
        if (load_sd_fx_json(configuration.performance.fx_number, 0) == false)
          lcd.print("Does not exist. ");
        else
          lcd.print("Done.           ");

        delay(MESSAGE_WAIT_TIME);

        LCDML.FUNC_goBackToMenu();
      }

      lcd.setCursor(0, 1);
      char tmp[10];
      sprintf(tmp, "[%2d]", configuration.performance.fx_number);
      lcd.print(tmp);
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }
    else
      eeprom_update_fx();

    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.fx_number), configuration.performance.fx_number);

    encoderDir[ENC_R].reset();
  }
}

void UI_func_save_fx(uint8_t param)
{
  static bool overwrite;
  static bool yesno;
  static uint8_t mode;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    char tmp[FILENAME_LEN];

    yesno = false;
    mode = 0;

    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Save FX"));
    lcd.setCursor(0, 1);
    sprintf(tmp, "[%2d]", configuration.performance.fx_number);
    lcd.print(tmp);

    sprintf(tmp, "/%s/%s%d.json", FX_CONFIG_PATH, FX_CONFIG_NAME, configuration.performance.fx_number);
    if (SD.exists(tmp))
      overwrite = true;
    else
      overwrite = false;
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()) || (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort()))
    {
      if (LCDML.BT_checkDown())
      {
        if (mode == 0)
          configuration.performance.fx_number = constrain(configuration.performance.fx_number + ENCODER[ENC_L].speed(), FX_NUM_MIN, FX_NUM_MAX);
        else
          yesno = true;
      }
      else if (LCDML.BT_checkUp())
      {
        if (mode == 0)
          configuration.performance.fx_number = constrain(configuration.performance.fx_number - ENCODER[ENC_L].speed(), FX_NUM_MIN, FX_NUM_MAX);
        else
          yesno = false;
      }
      else if (LCDML.BT_checkEnter())
      {
        if (mode == 0 && overwrite == true)
        {
          mode = 1;
          lcd.setCursor(0, 1);
          lcd.print(F("Overwrite: [   ]"));
        }
        else
        {
          mode = 0xff;
          if (overwrite == false || yesno == true)
          {
            if (yesno == true)
            {
              char tmp[FILENAME_LEN];
              sprintf(tmp, "/%s/%s%d.json", FX_CONFIG_PATH, FX_CONFIG_NAME, configuration.performance.fx_number);
              SD.remove(tmp);
            }
            save_sd_fx_json(configuration.performance.fx_number, 0);

            lcd.show(1, 0, 16, "Done.");
            LCDML.FUNC_goBackToMenu();
            delay(MESSAGE_WAIT_TIME);
          }
          else if (overwrite == true && yesno == false)
          {
            char tmp[17];

            mode = 0;
            lcd.setCursor(0, 1);
            sprintf(tmp, "[%2d]            ", configuration.performance.fx_number);
            lcd.print(tmp);
          }
        }
      }

      if (mode == 0)
      {
        char tmp[FILENAME_LEN];
        sprintf(tmp, "/%s/%s%d.json", FX_CONFIG_PATH, FX_CONFIG_NAME, configuration.performance.fx_number);
        if (SD.exists(tmp))
          overwrite = true;
        else
          overwrite = false;

        lcd.setCursor(0, 1);
        sprintf(tmp, "[%2d]", configuration.performance.fx_number);
        lcd.print(tmp);
      }
      else
      {
        lcd.setCursor(12, 1);
        if (yesno == true)
          lcd.print(F("YES"));
        else
          lcd.print(F("NO "));
      }
    }
    encoderDir[ENC_R].reset();
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }

    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.fx_number), configuration.performance.fx_number);

    encoderDir[ENC_R].reset();
  }
}

void UI_func_save_voice(uint8_t param)
{
  static bool yesno;
  static uint8_t mode;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    yesno = false;
#if NUM_DEXED == 1
    mode = 1;
#else
    mode = 0;
#endif

#if NUM_DEXED == 1
    char bank_name[BANK_NAME_LEN];

    if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
      strcpy(bank_name, "*ERROR*");

    lcd.setCursor(0, 0);
    lcd.print(F("Save to Bank"));
    lcd.show(1, 0, 2, configuration.performance.bank[selected_instance_id]);
    lcd.show(1, 3, 10, bank_name);
    lcd.show(1, 2, 1, "[");
    lcd.show(1, 13, 1, "]");
#else
    lcd.setCursor(0, 0);
    lcd.print(F("Save Instance"));
    lcd_active_instance_number(selected_instance_id);
    lcd.setCursor(5, 1);
    lcd.write(0);
    lcd.setCursor(10, 1);
    lcd.write(1);
#endif
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    char bank_name[BANK_NAME_LEN];
    char voice_name[VOICE_NAME_LEN];

    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      switch (mode)
      {
        case 0: // Instance selection
          if (LCDML.BT_checkDown() || LCDML.BT_checkUp())
            selected_instance_id = !selected_instance_id;

          lcd_active_instance_number(selected_instance_id);
          lcd.setCursor(5, 1);
          lcd.write(0);
          lcd.setCursor(10, 1);
          lcd.write(1);
          break;
        case 1: // Bank selection
          if (LCDML.BT_checkDown())
            configuration.performance.bank[selected_instance_id] = constrain(configuration.performance.bank[selected_instance_id] + ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);
          else if (LCDML.BT_checkUp() && configuration.performance.bank[selected_instance_id] > 0)
            configuration.performance.bank[selected_instance_id] = constrain(configuration.performance.bank[selected_instance_id] - ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);

          if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
            strcpy(bank_name, "*ERROR*");

          lcd.show(1, 0, 2, configuration.performance.bank[selected_instance_id]);
          lcd.show(1, 3, 10, bank_name);
          break;
        case 2: // Voice selection
          if (LCDML.BT_checkDown() && configuration.performance.voice[selected_instance_id] < MAX_VOICES - 1)
            configuration.performance.voice[selected_instance_id] = constrain(configuration.performance.voice[selected_instance_id] + ENCODER[ENC_R].speed(), 0, MAX_VOICES - 1);
          else if (LCDML.BT_checkUp() && configuration.performance.voice[selected_instance_id] > 0)
            configuration.performance.voice[selected_instance_id] = constrain(configuration.performance.voice[selected_instance_id] - ENCODER[ENC_R].speed(), 0, MAX_VOICES - 1);

          if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
            strncpy(bank_name, "*ERROR*", sizeof(bank_name));
          if (!get_voice_by_bank_name(configuration.performance.bank[selected_instance_id], bank_name, configuration.performance.voice[selected_instance_id], voice_name, sizeof(voice_name)))
            strncpy(voice_name, "*ERROR*", sizeof(voice_name));

          lcd.show(1, 0, 2, configuration.performance.voice[selected_instance_id] + 1);
          lcd.show(1, 3, 10, voice_name);
          break;
        case 3: // Yes/No selection
          yesno = !yesno;
          if (yesno == true)
          {
            lcd.show(1, 1, 3, "YES");
          }
          else
          {
            lcd.show(1, 1, 3, "NO");
          }
          break;
      }
    }
    else if (LCDML.BT_checkEnter())
    {
      if (encoderDir[ENC_R].ButtonShort())
        mode++;
      switch (mode)
      {
        case 1:
          if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
            strncpy(bank_name, "*ERROR*", sizeof(bank_name));
          lcd.setCursor(0, 0);
          lcd.print(F("Save to Bank"));
          lcd.show(1, 0, 2, configuration.performance.bank[selected_instance_id]);
          lcd.show(1, 3, 10, bank_name);
          lcd.show(1, 2, 2, " [");
          lcd.show(1, 14, 1, "]");
          break;
        case 2:
          if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
            strncpy(bank_name, "*ERROR*", sizeof(bank_name));
          if (!get_voice_by_bank_name(configuration.performance.bank[selected_instance_id], bank_name, configuration.performance.voice[selected_instance_id], voice_name, sizeof(voice_name)))
            strncpy(voice_name, "*ERROR*", sizeof(voice_name));

          lcd.show(0, 0, 16, "Save to Bank");
          lcd.show(0, 13, 2, configuration.performance.bank[selected_instance_id]);
          lcd.show(1, 0, 2, configuration.performance.voice[selected_instance_id] + 1);
          lcd.show(1, 3, 10, voice_name);
          break;
        case 3:
          lcd.show(0, 0, 16, "Overwrite?");
          lcd.show(1, 0, 15, "[NO");
          lcd.show(1, 4, 1, "]");
          break;
        default:
          if (yesno == true)
          {
#ifdef DEBUG
            bool ret = save_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);

            if (ret == true)
              Serial.println(F("Saving voice OK."));
            else
              Serial.println(F("Error while saving voice."));
#else
            save_sd_voice(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id], selected_instance_id);
#endif

            lcd.show(1, 0, 16, "Done.");
            delay(MESSAGE_WAIT_TIME);

            mode = 0xff;
            break;
          }

          LCDML.FUNC_goBackToMenu();
      }
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);

    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }
    else
    {
      if (selected_instance_id == 0)
      {
        EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voice[0]), configuration.performance.voice[0]);
        EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.bank[0]), configuration.performance.bank[0]);
      }
#if NUM_DEXED > 1
      else
      {
        EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.voice[0]), configuration.performance.voice[1]);
        EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, performance.bank[0]), configuration.performance.bank[1]);
      }
#endif
    }
    encoderDir[ENC_R].reset();
  }
}

void UI_func_sysex_receive_bank(uint8_t param)
{
  static bool yesno;
  static uint8_t mode;
  static uint8_t bank_number;
  static uint8_t ui_select_name_state;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    yesno = false;
    mode = 0;
    bank_number = configuration.performance.bank[selected_instance_id];
    memset(receive_bank_filename, 0, sizeof(receive_bank_filename));

    lcd.setCursor(0, 0);
    lcd.print(F("MIDI Recv Bank"));
    lcd.setCursor(2, 1);
    lcd.print(F("["));
    lcd.setCursor(14, 1);
    lcd.print(F("]"));
    if (!get_bank_name(configuration.performance.bank[selected_instance_id], receive_bank_filename, sizeof(receive_bank_filename)))
      strcpy(receive_bank_filename, "*ERROR*");
    lcd.show(1, 0, 2, bank_number);
    lcd.show(1, 3, 10, receive_bank_filename);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        switch (mode)
        {
          case 0:
            bank_number = constrain(bank_number + ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);
            if (!get_bank_name(bank_number, receive_bank_filename, sizeof(receive_bank_filename)))
              strcpy(receive_bank_filename, "*ERROR*");
            lcd.show(1, 0, 2, bank_number);
            lcd.show(1, 3, 10, receive_bank_filename);
            break;
          case 1:
            yesno = !yesno;
            if (yesno)
              lcd.show(1, 12, 3, "YES");
            else
              lcd.show(1, 12, 3, "NO");
            break;
          case 2:
            ui_select_name_state = UI_select_name(1, 1, receive_bank_filename, BANK_NAME_LEN - 1, false);
            break;
        }
      }
      else if (LCDML.BT_checkUp())
      {
        switch (mode)
        {
          case 0:
            bank_number = constrain(bank_number - ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);
            if (!get_bank_name(bank_number, receive_bank_filename, sizeof(receive_bank_filename)))
              strcpy(receive_bank_filename, "*ERROR*");
            lcd.show(1, 0, 2, bank_number);
            lcd.show(1, 3, 10, receive_bank_filename);
            break;
          case 1:
            yesno = !yesno;
            if (yesno)
              lcd.show(1, 12, 3, "YES");
            else
              lcd.show(1, 12, 3, "NO");
            break;
          case 2:
            ui_select_name_state = UI_select_name(1, 1, receive_bank_filename, BANK_NAME_LEN - 1, false);
            break;
        }
      }
    }
    else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {
      if (mode == 0)
      {
        if (!strcmp(receive_bank_filename, "*ERROR*"))
        {
          yesno = true;
          strcpy(receive_bank_filename, "NONAME");
          mode = 2;
          lcd.setCursor(0, 1);
          lcd.print(F("[          ]    "));
          ui_select_name_state = UI_select_name(1, 1, receive_bank_filename, BANK_NAME_LEN - 1, true);
          lcd.blink();
        }
        else
        {
          mode = 1;
          lcd.setCursor(0, 1);
          lcd.print(F("Overwrite: [NO ]"));
        }
      }
      else if (mode == 1 && yesno == true)
      {
        mode = 2;
        lcd.setCursor(0, 1);
        lcd.print(F("[          ]    "));
        ui_select_name_state = UI_select_name(1, 1, receive_bank_filename, BANK_NAME_LEN - 1, true);
        lcd.blink();
      }
      else if (mode == 2)
      {
        ui_select_name_state = UI_select_name(1, 1, receive_bank_filename, BANK_NAME_LEN - 1, false);
        if (ui_select_name_state == true)
        {
          if (yesno == true)
          {
#ifdef DEBUG
            Serial.print(F("Bank name: ["));
            Serial.print(receive_bank_filename);
            Serial.println(F("]"));
#endif
            char tmp[FILENAME_LEN];
            strcpy(tmp, receive_bank_filename);
            sprintf(receive_bank_filename, "/%d/%s.syx", bank_number, tmp);
#ifdef DEBUG
            Serial.print(F("Receiving into bank "));
            Serial.print(bank_number);
            Serial.print(F(" as filename "));
            Serial.print(receive_bank_filename);
            Serial.println(F("."));
#endif
            mode = 0xff;
            lcd.noBlink();
            lcd.setCursor(0, 1);
            lcd.print(F("Waiting...      "));
            /// Storing is done in SYSEX code
          }
        }
      }
      else if (mode >= 1 && yesno == false)
      {
        Serial.println(mode, DEC);
        memset(receive_bank_filename, 0, sizeof(receive_bank_filename));
        mode = 0xff;
        lcd.noBlink();
        lcd.setCursor(0, 1);
        lcd.print(F("Canceled.       "));
        delay(MESSAGE_WAIT_TIME);
        LCDML.FUNC_goBackToMenu();
      }
    }
    encoderDir[ENC_R].reset();
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();

    memset(receive_bank_filename, 0, sizeof(receive_bank_filename));
    lcd.noBlink();

    if (mode < 0xff)
    {
      lcd.setCursor(0, 1);
      lcd.print(F("Canceled.       "));
      delay(MESSAGE_WAIT_TIME);
    }
  }
}

void UI_func_set_sequence_name(uint8_t param)
{
  static uint8_t mode;
  static uint8_t ui_select_name_state;
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    mode = 0;
    lcd.setCursor(0, 0);
    lcd.print(F("Sequence Name"));
  }
  if (LCDML.FUNC_loop())    // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown()) {
        if (mode == 1)  ui_select_name_state = UI_select_name(1, 1, seq_name_temp, BANK_NAME_LEN - 1, false);
      }
      else if (LCDML.BT_checkUp()) {
        if (mode == 1)  ui_select_name_state = UI_select_name(1, 1, seq_name_temp, BANK_NAME_LEN - 1, false);
        //        if (ui_select_name_state == false) {
        //          lcd.setCursor(12, 1);
        //          lcd.print("    ");
        //          lcd.setCursor(0, 1);
        //          lcd.print("[");
        //          lcd.setCursor(11, 1);
        //          lcd.print("]");
        //          lcd.blink();
        //          ui_select_name_state = UI_select_name(1, 1, seq_name_temp, BANK_NAME_LEN - 1, true);
        //        }
      }
    }
    else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {

      if (mode == 1)
      {
        ui_select_name_state = UI_select_name(1, 1, seq_name_temp, BANK_NAME_LEN - 1, false);
        if (ui_select_name_state == true)
        {
          strcpy( seq_name, seq_name_temp);
          mode = 0xff;
          lcd.noBlink();
          lcd.setCursor(0, 1);
          lcd.print(F("OK.             "));
          delay(MESSAGE_WAIT_TIME);
          LCDML.FUNC_goBackToMenu();
        }
      }
    }
    if (mode == 0 )
    {
      mode = 1;
      strcpy(seq_name_temp, seq_name);
      lcd.setCursor(0, 1);
      lcd.print(F("[          ]    "));
      ui_select_name_state = UI_select_name(1, 1, seq_name_temp, BANK_NAME_LEN - 1, true);
      lcd.blink();
    }
  }
  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
    lcd.noBlink();
  }
}

void UI_func_sysex_send_bank(uint8_t param)
{
  char bank_name[BANK_NAME_LEN];
  static uint8_t bank_number;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    bank_number = configuration.performance.bank[selected_instance_id];
    lcd.setCursor(0, 0);
    lcd.print(F("MIDI Send Bank"));
    if (!get_bank_name(configuration.performance.bank[selected_instance_id], bank_name, sizeof(bank_name)))
      strncpy(bank_name, "*ERROR*", sizeof(bank_name));
    lcd.show(1, 2, 1, "[");
    lcd.show(1, 14, 1, "]");
    lcd.show(1, 0, 2, configuration.performance.bank[selected_instance_id]);
    lcd.show(1, 3, 10, bank_name);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        bank_number = constrain(bank_number + ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);

      }
      else if (LCDML.BT_checkUp())
      {
        bank_number = constrain(bank_number - ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);
      }
      if (!get_bank_name(bank_number, bank_name, sizeof(bank_name)))
        strcpy(bank_name, "*ERROR*");
      lcd.show(1, 0, 2, bank_number);
      lcd.show(1, 3, 10, bank_name);
    }
    else if (LCDML.BT_checkEnter() && encoderDir[ENC_R].ButtonShort())
    {
      File sysex;
      char filename[FILENAME_LEN];

      if (get_bank_name(bank_number, bank_name, sizeof(bank_name)))
      {
        sprintf(filename, "/%d/%s.syx", bank_number, bank_name);
#ifdef DEBUG
        Serial.print(F("Send bank "));
        Serial.print(filename);
        Serial.println(F(" from SD."));
#endif
        sysex = SD.open(filename);
        if (!sysex)
        {
#ifdef DEBUG
          Serial.println(F("Connot read from SD."));
#endif
          lcd.show(1, 0, 16, "Read error.");
          bank_number = 0xff;
        }
        else
        {
          uint8_t bank_data[4104];

          sysex.read(bank_data, 4104);
          sysex.close();

          lcd.show(1, 0, 16, "Sending Ch");
          if (configuration.dexed[selected_instance_id].midi_channel == MIDI_CHANNEL_OMNI)
          {
            lcd.show(1, 11, 2, "01");
            send_sysex_bank(1, bank_data);
          }
          else
          {
            lcd.show(1, 11, 2, configuration.dexed[selected_instance_id].midi_channel + 1);
            send_sysex_bank(configuration.dexed[selected_instance_id].midi_channel, bank_data);
          }
          lcd.show(1, 0, 16, "Done.");
          bank_number = 0xff;
        }
      }
      else
      {
        lcd.show(1, 0, 16, "No bank.");
        bank_number = 0xff;
      }

      delay(MESSAGE_WAIT_TIME);
      LCDML.FUNC_goBackToMenu();
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();

    if (bank_number < 0xff)
    {
      lcd.setCursor(0, 1);
      lcd.print(F("Canceled.       "));
      delay(MESSAGE_WAIT_TIME);
    }
  }
}

void UI_func_sysex_send_voice(uint8_t param)
{
  static uint8_t mode;
  static uint8_t bank_number;
  static uint8_t voice_number;

  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    mode = 0;
    bank_number = configuration.performance.bank[selected_instance_id];
    voice_number = configuration.performance.voice[selected_instance_id];

    char bank_name[BANK_NAME_LEN];

    if (!get_bank_name(bank_number, bank_name, sizeof(bank_name)))
      strcpy(bank_name, "*ERROR*");

    lcd.setCursor(0, 0);
    lcd.print(F("MIDI Send Voice"));
    lcd.show(1, 0, 2, bank_number);
    lcd.show(1, 3, 10, bank_name);
    lcd.show(1, 2, 1, "[");
    lcd.show(1, 13, 1, "]");
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    char bank_name[BANK_NAME_LEN];
    char voice_name[VOICE_NAME_LEN];

    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      switch (mode)
      {
        case 0: // Bank selection
          if (LCDML.BT_checkDown())
            bank_number = constrain(bank_number + ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);
          else if (LCDML.BT_checkUp() && bank_number > 0)
            bank_number = constrain(bank_number - ENCODER[ENC_R].speed(), 0, MAX_BANKS - 1);

          if (!get_bank_name(bank_number, bank_name, sizeof(bank_name)))
            strcpy(bank_name, "*ERROR*");

          lcd.show(1, 0, 2, bank_number);
          lcd.show(1, 3, 10, bank_name);
          break;
        case 1: // Voice selection
          if (LCDML.BT_checkDown() && voice_number < MAX_VOICES - 1)
            voice_number = constrain(voice_number + ENCODER[ENC_R].speed(), 0, MAX_VOICES - 1);
          else if (LCDML.BT_checkUp() && voice_number > 0)
            voice_number = constrain(voice_number - ENCODER[ENC_R].speed(), 0, MAX_VOICES - 1);
          if (!get_bank_name(bank_number, bank_name, sizeof(bank_name)))
            strncpy(bank_name, "*ERROR*", sizeof(bank_name));
          if (!get_voice_by_bank_name(bank_number, bank_name, voice_number, voice_name, sizeof(voice_name)))
            strncpy(voice_name, "*ERROR*", sizeof(voice_name));

          lcd.show(1, 0, 2, voice_number + 1);
          lcd.show(1, 3, 10, voice_name);
          break;
      }
    }
    else if (LCDML.BT_checkEnter())
    {
      if (encoderDir[ENC_R].ButtonShort())
        mode++;
      switch (mode)
      {
        case 1:
          if (!get_bank_name(bank_number, bank_name, sizeof(bank_name)))
            strncpy(bank_name, "*ERROR*", sizeof(bank_name));
          if (!get_voice_by_bank_name(bank_number, bank_name, voice_number, voice_name, sizeof(voice_name)))
            strncpy(voice_name, "*ERROR*", sizeof(voice_name));

          lcd.show(1, 0, 2, voice_number + 1);
          lcd.show(1, 3, 10, voice_name);
          break;
        case 2:
          File sysex;
          char filename[FILENAME_LEN];

          if (get_bank_name(bank_number, bank_name, sizeof(bank_name)))
          {
            sprintf(filename, "/%d/%s.syx", bank_number, bank_name);
#ifdef DEBUG
            Serial.print(F("Send voice "));
            Serial.print(voice_number);
            Serial.print(F(" of "));
            Serial.print(filename);
            Serial.println(F(" from SD."));
#endif
            sysex = SD.open(filename);
            if (!sysex)
            {
#ifdef DEBUG
              Serial.println(F("Connot read from SD."));
#endif
              lcd.show(1, 0, 16, "Read error.");
              bank_number = 0xff;
            }
            else
            {
              uint8_t voice_data[155];
              uint8_t encoded_voice_data[128];

              sysex.seek(6 + (voice_number * 128));
              sysex.read(encoded_voice_data, 128);

              MicroDexed[selected_instance_id]->decodeVoice(voice_data, encoded_voice_data);

              lcd.show(1, 0, 16, "Sending Ch");
              if (configuration.dexed[selected_instance_id].midi_channel == MIDI_CHANNEL_OMNI)
              {
                lcd.show(1, 11, 2, "01");
                send_sysex_voice(1, voice_data);
              }
              else
              {
                lcd.show(1, 11, 2, configuration.dexed[selected_instance_id].midi_channel + 1);
                send_sysex_voice(configuration.dexed[selected_instance_id].midi_channel, voice_data);
              }
              delay(MESSAGE_WAIT_TIME);
              lcd.show(1, 0, 16, "Done.");
              sysex.close();

              bank_number = 0xff;
            }
          }
          else
          {
            lcd.show(1, 0, 16, "No voice.");
            bank_number = 0xff;
          }

          mode = 0xff;
          delay(MESSAGE_WAIT_TIME);
          LCDML.FUNC_goBackToMenu();

          break;
      }
    }
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    if (mode < 0xff)
    {
      lcd.show(1, 0, 16, "Canceled.");
      delay(MESSAGE_WAIT_TIME);
    }
    encoderDir[ENC_R].reset();
  }
}

void UI_func_eq_1(uint8_t param)
{
#ifndef SGTL5000_AUDIO_ENHANCE
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("EQ Low-Cut"));
    lcd.setCursor(0, 1);
    lcd.print(F("Not implemented."));
  }
#else
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(METERBAR);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.eq_1 = constrain(configuration.fx.eq_1 + ENCODER[ENC_R].speed(), EQ_1_MIN, EQ_1_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.eq_1 = constrain(configuration.fx.eq_1 - ENCODER[ENC_R].speed(), EQ_1_MIN, EQ_1_MAX);
      }
    }
    lcd_display_meter_int("EQ Low-Cut [Hz]", configuration.fx.eq_1, 1.0, 0.0, EQ_1_MIN, EQ_1_MAX, 3, false, false, true);
    sgtl5000.setEQFc(1, mapfloat(configuration.fx.eq_1, EQ_1_MIN, EQ_1_MAX, 15, 250));
    sgtl5000.setEQGain(1, 6.0);
    sgtl5000.commitFilter(1);
#ifdef DEBUG
    sgtl5000.show_params(1);
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.eq_1), configuration.fx.eq_1);
  }
#endif
}

void UI_func_eq_2(uint8_t param)
{
#ifndef SGTL5000_AUDIO_ENHANCE
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("EQ 120Hz"));
    lcd.setCursor(0, 1);
    lcd.print(F("Not implemented."));
  }
#else
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(METERBAR);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.eq_2 = constrain(configuration.fx.eq_2 + ENCODER[ENC_R].speed(), EQ_2_MIN, EQ_2_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.eq_2 = constrain(configuration.fx.eq_2 - ENCODER[ENC_R].speed(), EQ_2_MIN, EQ_2_MAX);
      }
    }
    lcd_display_meter_float("EQ 120Hz", configuration.fx.eq_2, 0.1, 0.0, EQ_2_MIN, EQ_2_MAX, 1, 1, false, true, true);
    sgtl5000.setEQGain(2, mapfloat(configuration.fx.eq_2, EQ_2_MIN, EQ_2_MAX, -9.9, 9.9));
    sgtl5000.commitFilter(2);
#ifdef DEBUG
    sgtl5000.show_params(2);
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.eq_2), configuration.fx.eq_2);
  }
#endif
}

void UI_func_eq_3(uint8_t param)
{
#ifndef SGTL5000_AUDIO_ENHANCE
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("EQ 220Hz"));
    lcd.setCursor(0, 1);
    lcd.print(F("Not implemented."));
  }
#else
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(METERBAR);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.eq_3 = constrain(configuration.fx.eq_3 + ENCODER[ENC_R].speed(), EQ_3_MIN, EQ_3_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.eq_3 = constrain(configuration.fx.eq_3 - ENCODER[ENC_R].speed(), EQ_3_MIN, EQ_3_MAX);
      }
    }
    lcd_display_meter_float("EQ 220Hz", configuration.fx.eq_3, 0.1, 0.0, EQ_3_MIN, EQ_3_MAX, 1, 1, false, true, true);
    sgtl5000.setEQGain(3, mapfloat(configuration.fx.eq_3, EQ_3_MIN, EQ_3_MAX, -9.9, 9.9));
    sgtl5000.commitFilter(3);
#ifdef DEBUG
    sgtl5000.show_params(3);
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.eq_3), configuration.fx.eq_3);
  }
#endif
}

void UI_func_eq_4(uint8_t param)
{
#ifndef SGTL5000_AUDIO_ENHANCE
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("EQ 1000Hz"));
    lcd.setCursor(0, 1);
    lcd.print(F("Not implemented."));
  }
#else
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(METERBAR);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.eq_4 = constrain(configuration.fx.eq_4 + ENCODER[ENC_R].speed(), EQ_4_MIN, EQ_4_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.eq_4 = constrain(configuration.fx.eq_4 - ENCODER[ENC_R].speed(), EQ_4_MIN, EQ_4_MAX);
      }
    }
    lcd_display_meter_float("EQ 1000Hz", configuration.fx.eq_4, 0.1, 0.0, EQ_4_MIN, EQ_4_MAX, 1, 1, false, true, true);
    sgtl5000.setEQGain(4, mapfloat(configuration.fx.eq_4, EQ_4_MIN, EQ_4_MAX, -9.9, 9.9));
    sgtl5000.commitFilter(4);
#ifdef DEBUG
    sgtl5000.show_params(4);
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.eq_4), configuration.fx.eq_4);
  }
#endif
}

void UI_func_eq_5(uint8_t param)
{
#ifndef SGTL5000_AUDIO_ENHANCE
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("EQ 2000Hz"));
    lcd.setCursor(0, 1);
    lcd.print(F("Not implemented."));
  }
#else
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(METERBAR);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.eq_5 = constrain(configuration.fx.eq_5 + ENCODER[ENC_R].speed(), EQ_5_MIN, EQ_5_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.eq_5 = constrain(configuration.fx.eq_5 - ENCODER[ENC_R].speed(), EQ_5_MIN, EQ_5_MAX);
      }
    }
    lcd_display_meter_float("EQ 2000Hz", configuration.fx.eq_5, 0.1, 0.0, EQ_5_MIN, EQ_5_MAX, 1, 1, false, true, true);
    sgtl5000.setEQGain(5, mapfloat(configuration.fx.eq_5, EQ_5_MIN, EQ_5_MAX, -9.9, 9.9));
    sgtl5000.commitFilter(5);
#ifdef DEBUG
    sgtl5000.show_params(5);
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.eq_5), configuration.fx.eq_5);
  }
#endif
}

void UI_func_eq_6(uint8_t param)
{
#ifndef SGTL5000_AUDIO_ENHANCE
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("EQ 7000Hz"));
    lcd.setCursor(0, 1);
    lcd.print(F("Not implemented."));
  }
#else
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(METERBAR);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.eq_6 = constrain(configuration.fx.eq_6 + ENCODER[ENC_R].speed(), EQ_6_MIN, EQ_6_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.eq_6 = constrain(configuration.fx.eq_6 - ENCODER[ENC_R].speed(), EQ_6_MIN, EQ_6_MAX);
      }
    }
    lcd_display_meter_float("EQ 7000Hz", configuration.fx.eq_6, 0.1, 0.0, EQ_6_MIN, EQ_6_MAX, 1, 1, false, true, true);
    sgtl5000.setEQGain(6, mapfloat(configuration.fx.eq_6, EQ_6_MIN, EQ_6_MAX, -9.9, 9.9));
    sgtl5000.commitFilter(6);
#ifdef DEBUG
    sgtl5000.show_params(1);
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.eq_6), configuration.fx.eq_6);
  }
#endif
}

void UI_func_eq_7(uint8_t param)
{
#ifndef SGTL5000_AUDIO_ENHANCE
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd.setCursor(0, 0);
    lcd.print(F("EQ High-Cut"));
    lcd.setCursor(0, 1);
    lcd.print(F("Not implemented."));
  }
#else
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();
    lcd_special_chars(METERBAR);
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    if ((LCDML.BT_checkDown() && encoderDir[ENC_R].Down()) || (LCDML.BT_checkUp() && encoderDir[ENC_R].Up()))
    {
      if (LCDML.BT_checkDown())
      {
        configuration.fx.eq_7 = constrain(configuration.fx.eq_7 + ENCODER[ENC_R].speed(), EQ_7_MIN, EQ_7_MAX);
      }
      else if (LCDML.BT_checkUp())
      {
        configuration.fx.eq_7 = constrain(configuration.fx.eq_7 - ENCODER[ENC_R].speed(), EQ_7_MIN, EQ_7_MAX);
      }
    }
    lcd_display_meter_float("EQ High-Cut[kHz]", configuration.fx.eq_7, 1.0, 0.0, EQ_7_MIN, EQ_7_MAX, 3, 1, false, false, true);
    sgtl5000.setEQFc(7, mapfloat(configuration.fx.eq_7, EQ_7_MIN, EQ_7_MAX, EQ_7_MIN * 1000.0, EQ_7_MAX * 1000.0));
    sgtl5000.commitFilter(7);
#ifdef DEBUG
    sgtl5000.show_params(7);
#endif
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    lcd_special_chars(SCROLLBAR);
    encoderDir[ENC_R].reset();
    EEPROM.update(EEPROM_START_ADDRESS + offsetof(configuration_s, fx.eq_7), configuration.fx.eq_7);
  }
#endif
}

void UI_function_not_enabled(void)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Function not"));
    lcd.setCursor(0, 1);
    lcd.print(F("enbaled!"));
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    ;
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

void UI_function_not_implemented(uint8_t param)
{
  if (LCDML.FUNC_setup())         // ****** SETUP *********
  {
    encoderDir[ENC_R].reset();

    lcd.setCursor(0, 0);
    lcd.print(F("Function not"));
    lcd.setCursor(0, 1);
    lcd.print(F("implemented!"));
  }

  if (LCDML.FUNC_loop())          // ****** LOOP *********
  {
    ;
  }

  if (LCDML.FUNC_close())     // ****** STABLE END *********
  {
    encoderDir[ENC_R].reset();
  }
}

bool UI_select_name(uint8_t y, uint8_t x, char* edit_string, uint8_t len, bool init)
{
  static int8_t edit_pos;
  static bool edit_mode;
  static uint8_t edit_value;
  static uint8_t last_char_pos;

  if (init == true)
  {
    edit_mode = false;
    edit_pos = 0;
    edit_value = search_accepted_char(edit_string[edit_pos]);
    last_char_pos = strlen(edit_string);

    string_trim(edit_string); // just to be sure

    lcd.setCursor(x, y);
    lcd.print(edit_string);
    lcd.setCursor(x, y);

    return (false);
  }

  if (LCDML.BT_checkDown() || LCDML.BT_checkUp())
  {
    if (LCDML.BT_checkDown())
    {
      if (edit_mode == true)
      {
        edit_value = search_accepted_char(edit_string[edit_pos]);

        if (edit_value < sizeof(accepted_chars) - 2)
          edit_value++;
        if (edit_value == 0 && edit_string[constrain(edit_pos + 1, 0, len)] > 0)
          edit_value = 1;

        edit_string[edit_pos] = accepted_chars[edit_value];

        lcd.setCursor(x + edit_pos, y);
        lcd.print(edit_string[edit_pos]);
      }
      else
      {
        if (edit_string[edit_pos] != 0 && edit_string[edit_pos] != 32)
          edit_pos = constrain(edit_pos + 1, 0, len);
        else
        {
          if (edit_pos + 1 > last_char_pos)
            edit_pos = len;
        }

        if (edit_pos == len)
        {
          lcd.noBlink();
          lcd.setCursor(x - 1, y);
          lcd.print(F(" "));
          lcd.setCursor(x + len, y);
          lcd.print(F(" "));
          lcd.setCursor(x + len + 1, y);
          lcd.print(F("[OK]"));
        }
      }
    }
    else if (LCDML.BT_checkUp())
    {
      if (edit_mode == true)
      {
        edit_value = search_accepted_char(edit_string[edit_pos]);

        if (edit_value >= 1)
          edit_value--;
        if (edit_value == 0 && edit_string[constrain(edit_pos + 1, 0, len)] > 0)
          edit_value = 1;
        edit_string[edit_pos] = accepted_chars[edit_value];

        lcd.setCursor(x + edit_pos, y);
        lcd.print(edit_string[edit_pos]);
      }
      else
      {
        if (edit_pos - 1 > last_char_pos)
          edit_pos = last_char_pos;
        else
          edit_pos = constrain(edit_pos - 1, 0, len - 1);

        if (edit_pos == last_char_pos)
        {
          lcd.setCursor(x - 1, y);
          lcd.print(F("["));
          lcd.setCursor(x + len, y);
          lcd.print(F("]"));
          lcd.setCursor(x + len + 1, y);
          lcd.print(F("    "));
          lcd.blink();
        }
      }
    }
  }
  else if (LCDML.BT_checkEnter())
  {
    last_char_pos = strlen(edit_string);
    if (edit_pos >= len)
    {
      edit_pos = 0;
      edit_mode = false;

      return (true);
    }
    else
    {
      last_char_pos = strlen(edit_string);
      edit_mode = !edit_mode;
    }
    if (edit_mode == false && edit_pos < len && edit_string[edit_pos] != 0 && edit_string[edit_pos] != 32)
      edit_pos++;
    if (edit_mode == true)
    {
      lcd.setCursor(x + len + 1, y);
      lcd.print(F("*"));
    }
    else
    {
      lcd.setCursor(x + len + 1, y);
      lcd.print(F(" "));
    }

  }
  lcd.setCursor(x + edit_pos, y);
  encoderDir[ENC_R].reset();

  return (false);
}

uint8_t search_accepted_char(uint8_t c)
{
  //if (c == 0)
  //  c = 32;

  for (uint8_t i = 0; i < sizeof(accepted_chars) - 1; i++)
  {
    Serial.print(i, DEC);
    Serial.print(":");
    Serial.print(c);
    Serial.print("==");
    Serial.println(accepted_chars[i], DEC);
    if (c == accepted_chars[i])
      return (i);
  }
  return (0);
}

void lcd_display_int(int16_t var, uint8_t size, bool zeros, bool brackets, bool sign)
{
  lcd_display_float(float(var), size, 0, zeros, brackets, sign);
}

void lcd_display_float(float var, uint8_t size_number, uint8_t size_fraction, bool zeros, bool brackets, bool sign)
{
  char s[LCD_cols + 1];
  char f[LCD_cols + 1];

  if (size_fraction > 0)
  {
    if (zeros == true && sign == true)
      sprintf(f, "%%+0%d.%df", size_number + size_fraction + 2, size_fraction);
    else if (zeros == true && sign == false)
      sprintf(f, "%%+0%d.%df", size_number + size_fraction + 1, size_fraction);
    else if (zeros == false && sign == true)
      sprintf(f, "%%+%d.%df", size_number + size_fraction + 2, size_fraction);
    else if (zeros == false && sign == false)
      sprintf(f, "%%%d.%df", size_number + size_fraction + 1, size_fraction);

    sprintf(s, f, var);
  }
  else
  {
    if (zeros == true && sign == true)
      sprintf(f, "%%+0%dd", size_number + 1);
    else if (zeros == true && sign == false)
      sprintf(f, "%%%0dd", size_number);
    else if (zeros == false && sign == true)
      sprintf(f, "%%+%dd", size_number + 1);
    else if (zeros == false && sign == false)
      sprintf(f, "%%%dd", size_number);

    sprintf(s, f, int(var));
  }

  if (brackets == true)
  {
    char tmp[LCD_cols + 1];

    strcpy(tmp, s);
    sprintf(s, "[%s]", tmp);
  }

  lcd.print(s);
}

inline void lcd_display_bar_int(const char* title, uint32_t value, float factor, int32_t min_value, int32_t max_value, uint8_t size, bool zeros, bool sign, bool init)
{
  lcd_display_bar_float(title, float(value), factor, min_value, max_value, size, 0, zeros, sign, init);
}

void lcd_display_bar_float(const char* title, float value, float factor, int32_t min_value, int32_t max_value, uint8_t size_number, uint8_t size_fraction, bool zeros, bool sign, bool init)
{
  uint8_t size;
  float v;
  float _vi = 0.0;
  uint8_t vf;
  uint8_t vi;

  if (size_fraction == 0)
    size = size_number;
  else
    size = size_number + size_fraction + 1;
  if (sign == true)
    size++;

  v = float((value - min_value) * (LCD_cols - size)) / (max_value - min_value);
  vf = uint8_t(modff(v, &_vi) * 10.0 + 0.5);
  vi = uint8_t(_vi);

  if (sign == true)
    size += 1;

  // Title
  if (init == true)
    lcd.show(0, 0, LCD_cols - 2, title);
  //lcd.show(0, 0, LCD_cols - 3, title);

  // Value
  lcd.setCursor(LCD_cols - size, 1);
  lcd_display_float(value * factor, size_number, size_fraction, zeros, false, sign); // TBD

  // Bar
  lcd.setCursor(0, 1);

  if (vi == 0)
  {
    lcd.write((uint8_t)(vf / 2.0 - 0.5) + 2);
    for (uint8_t i = vi + 1; i < LCD_cols - size; i++)
      lcd.print(F(" ")); // empty block
  }
  else
  {
    for (uint8_t i = 0; i < vi; i++)
      lcd.write((uint8_t)4 + 2); // full block
    if (vi < LCD_cols - size)
      lcd.write((uint8_t)(vf / 2.0 - 0.5) + 2);
    for (uint8_t i = vi + 1; i < LCD_cols - size; i++)
      lcd.print(F(" ")); // empty block
  }
}

inline void lcd_display_meter_int(const char* title, uint32_t value, float factor, float offset, int32_t min_value, int32_t max_value, uint8_t size, bool zeros, bool sign, bool init)
{
  lcd_display_meter_float(title, float(value), factor, offset, min_value, max_value, size, 0, zeros, sign, init);
}

void lcd_display_meter_float(const char* title, float value, float factor, float offset, int32_t min_value, int32_t max_value, uint8_t size_number, uint8_t size_fraction, bool zeros, bool sign, bool init)
{
  uint8_t size = 0;
  float v;
  float _vi = 0.0;
  uint8_t vf;
  uint8_t vi;

  if (size_fraction == 0)
    size = size_number;
  else
    size = size_number + size_fraction + 1;
  if (sign == true)
    size++;

  v = float((value - min_value) * (LCD_cols - size)) / (max_value - min_value);
  vf = uint8_t(modff(v, &_vi) * 10.0 + 0.5);
  vi = uint8_t(_vi);

  if (init == true)
  {
    // Title
    lcd.setCursor(0, 0);
    lcd.print(title);
  }

  // Value
  lcd.setCursor(LCD_cols - size, 1);
  lcd_display_float((value + offset) * factor, size_number, size_fraction, zeros, false, sign);

  // Bar
  lcd.setCursor(0, 1);

  if (vi == 0)
  {
    lcd.write((uint8_t)(vf / 2.0) + 2);
    for (uint8_t i = 1; i < LCD_cols - size; i++)
      lcd.print(F(" ")); // empty block
  }
  else if (vi == LCD_cols - size)
  {
    for (uint8_t i = 0; i < LCD_cols - size - 1; i++)
      lcd.print(F(" ")); // empty block
    lcd.write(4 + 2);
  }
  else
  {
    for (uint8_t i = 0; i < LCD_cols - size; i++)
      lcd.print(F(" ")); // empty block
    lcd.setCursor(vi, 1);
    lcd.write((uint8_t)(vf / 2.0) + 2);
    for (uint8_t i = vi + 1; i < LCD_cols - size; i++)
      lcd.print(F(" ")); // empty block
  }
}

uint8_t bit_reverse8(uint8_t v)
{
  uint8_t result = 0;
  for ( ; v > 0; v >>= 1 )
    (result <<= 1) |= (v & 1);
  return (result);
}

void lcd_active_instance_number(uint8_t instance_id)
{
  for (uint8_t i = 0; i < 8; i++)
  {
    if (instance_id == 0)
    {
      if (configuration.dexed[instance_id].polyphony == 0)
        instance_num[0][i] = bit_reverse8(special_chars[0][i]);
      else
        instance_num[0][i] = special_chars[0][i];

      if (configuration.dexed[!instance_id].polyphony == 0)
      {
        instance_num[1][i] = bit_reverse8(special_chars[1][i]);
        instance_num[1][i] = ~instance_num[1][i];
      }
      else
        instance_num[1][i] = ~special_chars[1][i];
    }
    else
    {
      if (configuration.dexed[!instance_id].polyphony == 0)
      {
        instance_num[0][i] = bit_reverse8(special_chars[0][i]);
        instance_num[0][i] = ~instance_num[0][i];
      }
      else
        instance_num[0][i] = ~special_chars[0][i];

      if (configuration.dexed[instance_id].polyphony == 0)
        instance_num[1][i] = bit_reverse8(special_chars[1][i]);
      else
        instance_num[1][i] = special_chars[1][i];
    }
  }

#if NUM_DEXED == 1
  lcd.createChar(0, instance_num[0]);
  lcd.createChar(1, (uint8_t*)special_chars[17]);
#else
  lcd.createChar(0, instance_num[0]);
  lcd.createChar(1, instance_num[1]);
#endif
}

void lcd_OP_active_instance_number(uint8_t instance_id, uint8_t op)
{
  uint8_t i, n;

  for (n = 2; n < 8; n++)
  {
    for (i = 0; i < 8; i++)
    {
      if (bitRead(op, n - 2))
        instance_num[n][i] = special_chars[n][i];
      else
        instance_num[n][i] = ~special_chars[n][i];
    }
    lcd.createChar(n, instance_num[n]);
  }

  for (i = 0; i < 8; i++)
  {
    if (instance_id == 0)
    {
      if (configuration.dexed[instance_id].polyphony == 0)
        instance_num[0][i] = bit_reverse8(special_chars[0][i]);
      else
        instance_num[0][i] = special_chars[0][i];

      if (configuration.dexed[!instance_id].polyphony == 0)
      {
        instance_num[1][i] = bit_reverse8(special_chars[1][i]);
        instance_num[1][i] = ~instance_num[1][i];
      }
      else
        instance_num[1][i] = ~special_chars[1][i];
    }
    else
    {
      if (configuration.dexed[!instance_id].polyphony == 0)
      {
        instance_num[0][i] = bit_reverse8(special_chars[0][i]);
        instance_num[0][i] = ~instance_num[0][i];
      }
      else
        instance_num[0][i] = ~special_chars[0][i];

      if (configuration.dexed[instance_id].polyphony == 0)
        instance_num[1][i] = bit_reverse8(special_chars[1][i]);
      else
        instance_num[1][i] = special_chars[1][i];
    }
  }

  lcd.createChar(0, instance_num[0]);
#if NUM_DEXED > 1
  lcd.createChar(1, instance_num[1]);
#else
  lcd.createChar(1, (uint8_t *)special_chars[17]);
#endif
}

void lcd_special_chars(uint8_t mode)
{
  switch (mode)
  {
    case SCROLLBAR:
      // set special chars for scrollbar
      for (uint8_t i = 0; i < 5; i++)
      {
#ifdef I2C_DISPLAY
        lcd.createChar(i, (uint8_t*)scroll_bar[i]);
#else
        flipped_scroll_bar[i] = rotTile(scroll_bar[i]);
#endif
      }
      break;
    case BLOCKBAR:
      // set special chars for volume-bar
      for (uint8_t i = 0; i < 7; i++)
      {
#ifdef I2C_DISPLAY
        lcd.createChar(i + 2, (uint8_t*)block_bar[i]);
#else
        flipped_block_bar[i] = rotTile(block_bar[i]);
#endif
      }
      break;
    case METERBAR:
      // set special chars for panorama-bar
      for (uint8_t i = 0; i < 7; i++)
      {
#ifdef I2C_DISPLAY
        lcd.createChar(i + 2, (uint8_t*)meter_bar[i]);
#else
        flipped_meter_bar[i] = rotTile(meter_bar[i]);
#endif
      }
      break;
  }
}

#ifdef USE_FX
void lcd_display_delay_sync(uint8_t sync)
{
  lcd.show(0, 0, LCD_cols - 2, "Delay Sync");
  if (seq_running == false)lcd.show(1, 0, 10, "MIDI Sync "); else lcd.show(1, 0, 10, "Seq. Sync ");
  switch (sync)
  {
    case 1:
      lcd.show(1, 10, 6, "1/16");
      break;
    case 2:
      lcd.show(1, 10, 6, "1/16T");
      break;
    case 3:
      lcd.show(1, 10, 6, "1/8");
      break;
    case 4:
      lcd.show(1, 10, 6, "1/8T");
      break;
    case 5:
      lcd.show(1, 10, 6, "1/4");
      break;
    case 6:
      lcd.show(1, 10, 6, "1/4T");
      break;
    case 7:
      lcd.show(1, 10, 6, "1/2");
      break;
    case 8:
      lcd.show(1, 10, 6, "1/2T");
      break;
    case 9:
      lcd.show(1, 10, 6, "1/1");
      break;
  }
  if (seq_running == false) {

    uint16_t midi_sync_delay_time = uint16_t(60000.0 * midi_ticks_factor[sync] / midi_bpm + 0.5);
    if (midi_sync_delay_time > DELAY_MAX_TIME)
    {
#ifdef DEBUG
      Serial.println(F("Calculated MIDI-Sync delay: "));
      Serial.print(round(60000.0 * midi_ticks_factor[sync] / midi_bpm), DEC);
      Serial.println(F("ms"));
      Serial.println(F("MIDI-Sync delay: midi_sync_delay_time"));
      Serial.print(midi_sync_delay_time, DEC);
      Serial.println(F("ms"));
#endif
    }
  } else
  {
    uint16_t midi_sync_delay_time = uint16_t(60000.0 *  midi_ticks_factor[sync] / seq_bpm);
    delay_fx[selected_instance_id]->delay(0, constrain(midi_sync_delay_time, DELAY_TIME_MIN, DELAY_TIME_MAX * 10));
  }
  lcd.show(1, 15, 1, "!");
}
#endif

void eeprom_update_var(uint16_t pos, uint8_t val, const char* val_string)
{
#ifdef DEBUG
  char tmp[80];
  sprintf(tmp, "EEPROM update '%s' at position %d with value %d.", val_string, pos, val);
  Serial.println(tmp);
#endif
  EEPROM.update(EEPROM_START_ADDRESS + pos, val);
}

void string_trim(char *s)
{
  int i;

  while (isspace (*s)) s++;   // skip left side white spaces
  for (i = strlen (s) - 1; (isspace (s[i])); i--) ;   // skip right side white spaces
  s[i + 1] = '\0';
}

void locate_previous_non_favorite()
{
  //find prev. non fav in current bank
  lcd.setCursor(3, 0);
  lcd.print("<SEARCHING");
  do
  {
    if (configuration.performance.voice[selected_instance_id] == 0)
    {
      configuration.performance.voice[selected_instance_id] = 32; //+1
      if (configuration.performance.bank[selected_instance_id] < 1 )
        configuration.performance.bank[selected_instance_id] = MAX_BANKS - 1;

      do
      { //seek for previous bank
        configuration.performance.bank[selected_instance_id]--;
        if (configuration.performance.bank[selected_instance_id] < 1 )
          configuration.performance.bank[selected_instance_id] = MAX_BANKS - 1;
        favsearcher++;
      } while (quick_check_favorites_in_bank(configuration.performance.bank[selected_instance_id], selected_instance_id) == true && favsearcher < 132);
    }
    configuration.performance.voice[selected_instance_id]--;
    favsearcher++;
  } while (check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                          selected_instance_id) == true &&  favsearcher < 170);
  favsearcher = 0;
}

void locate_previous_favorite()
{

  // worst case, nothing found below voice 0 /  bank 0 - start loop at last bank
  if (  configuration.performance.voice[selected_instance_id] < 2  &&
        configuration.performance.bank[selected_instance_id] == 0 && favsearcher < 170)
  {
    configuration.performance.bank[selected_instance_id] = MAX_BANKS - 1;
    configuration.performance.voice[selected_instance_id] = 32;
  } else

    if (configuration.performance.voice[selected_instance_id] == 0 && configuration.performance.bank[selected_instance_id] < MAX_BANKS - 1 )
    { //if at begin of any other bank
      configuration.performance.bank[selected_instance_id]--;
      configuration.performance.voice[selected_instance_id] = 32;

    }

  if (configuration.performance.voice[selected_instance_id] >= 0 && configuration.performance.bank[selected_instance_id] >= 0) {

    lcd.setCursor(3, 0);
    lcd.print("<SEARCHING");

    do {  //first find previous fav in current bank

      if (configuration.performance.voice[selected_instance_id] == 0) {

        if (configuration.performance.bank[selected_instance_id] == 0) {
          configuration.performance.bank[selected_instance_id] = MAX_BANKS - 1;
          configuration.performance.voice[selected_instance_id] = 32;
        } else
          configuration.performance.bank[selected_instance_id]--;
        configuration.performance.voice[selected_instance_id] = 32;

      } else

        configuration.performance.voice[selected_instance_id]--;
      favsearcher++;

    } while ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                             selected_instance_id) == false && configuration.performance.voice[selected_instance_id] >= 1 && favsearcher < 36);

    // if found, we are done. else quick check in previous banks

    if ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                        selected_instance_id) == false && configuration.performance.voice[selected_instance_id] >= 0  &&
         configuration.performance.bank[selected_instance_id] >= 0 && favsearcher < 170)
    {
      configuration.performance.voice[selected_instance_id] = 32;

      do { //seek for previous bank
        configuration.performance.bank[selected_instance_id]--;
        favsearcher++;
      } while (quick_check_favorites_in_bank(configuration.performance.bank[selected_instance_id], selected_instance_id) == false &&
               favsearcher < 132 &&  configuration.performance.bank[selected_instance_id] >= 0);

      do {  //last try to search if a bank with fav was found

        configuration.performance.voice[selected_instance_id]--;
        favsearcher++;
      } while ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                               selected_instance_id) == false && configuration.performance.voice[selected_instance_id] >= 1 && favsearcher < 170);
    }
  }
  favsearcher = 0;
}

void locate_next_favorite()

{

  bool   RollOver = false;
  if (configuration.performance.voice[selected_instance_id] > 30 && configuration.performance.bank[selected_instance_id] >= MAX_BANKS - 1 )
  { //if at end of all banks
    configuration.performance.bank[selected_instance_id] = 0;
    configuration.performance.voice[selected_instance_id] = 0;
    RollOver = true;

  } else if (configuration.performance.voice[selected_instance_id] > 30 && configuration.performance.bank[selected_instance_id] < MAX_BANKS - 1 )
  { //if at end of any other bank
    configuration.performance.bank[selected_instance_id]++;
    configuration.performance.voice[selected_instance_id] = 0;
  }

  if (configuration.performance.voice[selected_instance_id] <= 30 && configuration.performance.bank[selected_instance_id] <= MAX_BANKS ) {

    lcd.setCursor(3, 0);
    lcd.print(">SEARCHING");

    do {  //first find next fav in current bank

      if (RollOver == false)  configuration.performance.voice[selected_instance_id]++; else RollOver = true;
      favsearcher++;

    } while ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                             selected_instance_id) == false && configuration.performance.voice[selected_instance_id] <= 32 && favsearcher < 36);

    // if found, we are done. else quick check in next banks

    if ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                        selected_instance_id) == false   &&
         configuration.performance.bank[selected_instance_id] < MAX_BANKS  && favsearcher < 170)
    {
      configuration.performance.voice[selected_instance_id] = 0;

      do { //seek in next bank

        configuration.performance.bank[selected_instance_id]++;
        if (  configuration.performance.bank[selected_instance_id] > MAX_BANKS - 1  && favsearcher < 190)
        {
          configuration.performance.bank[selected_instance_id] = 0;
          configuration.performance.voice[selected_instance_id] = 0;
        }
        favsearcher++;
      } while (quick_check_favorites_in_bank(configuration.performance.bank[selected_instance_id], selected_instance_id) == false &&
               favsearcher < 132 );

      if ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                          selected_instance_id) == false && configuration.performance.voice[selected_instance_id] <= 32 && favsearcher < 190)
      {
        do {  //last bank to search if a fav can be found

          configuration.performance.voice[selected_instance_id]++;
          favsearcher++;
        } while ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                                 selected_instance_id) == false  && favsearcher < 170);
      }
    }
  }
  favsearcher = 0;

}

void locate_next_non_favorite()
{
  //find next non-fav in current bank
  lcd.setCursor(3, 0);
  lcd.print(">SEARCHING");
  do {
    configuration.performance.voice[selected_instance_id]++;
    if (configuration.performance.voice[selected_instance_id] > 31) {
      configuration.performance.voice[selected_instance_id] = 0;
      //configuration.performance.bank[selected_instance_id]++;
      if (configuration.performance.bank[selected_instance_id] > MAX_BANKS - 1)
        configuration.performance.bank[selected_instance_id] = 0;
      do { //seek for next bank
        configuration.performance.bank[selected_instance_id]++;
        if (configuration.performance.bank[selected_instance_id] > MAX_BANKS - 1)
          configuration.performance.bank[selected_instance_id] = 0;
        favsearcher++;
      } while (quick_check_favorites_in_bank(configuration.performance.bank[selected_instance_id], selected_instance_id) == true && favsearcher < 132);
    }
    favsearcher++;
  } while ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                           selected_instance_id) == true &&  favsearcher < 170);
  favsearcher = 0;
}

void locate_random_non_favorite()
{
  //find random non-fav
  do
  {
    configuration.performance.voice[selected_instance_id] = random(32);
    configuration.performance.bank[selected_instance_id] = random(MAX_BANKS - 1);
    favsearcher++;
  } while ( check_favorite(configuration.performance.bank[selected_instance_id], configuration.performance.voice[selected_instance_id],
                           selected_instance_id) == true &&  favsearcher < 100);
  favsearcher = 0;
}

bool check_favorite(uint8_t b, uint8_t v, uint8_t instance_id)
{
  b = constrain(b, 0, MAX_BANKS - 1);
  v = constrain(v, 0, MAX_VOICES - 1);
  char tmp[18];
  File myFav;
  if (sd_card > 0)
  {
    sprintf(tmp, "/%s/%d/%d.fav", FAV_CONFIG_PATH, b, v);
#ifdef DEBUG
    Serial.print("check if Voice is a Favorite: ");
    Serial.print(tmp);
    Serial.println();
#endif
    if (SD.exists(tmp))
    { //is Favorite
#ifdef DEBUG
      Serial.println(" - It is in Favorites.");
#endif
      return true;
    }
    else
    { // it was not a favorite

#ifdef DEBUG
      Serial.println(" - It is not in Favorites.");
#endif
      return false;
    }
  }
  else
    return false;
}

void draw_favorite_icon(uint8_t b, uint8_t v, uint8_t instance_id)
{
  b = constrain(b, 0, MAX_BANKS - 1);
  v = constrain(v, 0, MAX_VOICES - 1);
  char tmp[18];
  File myFav;
  if (sd_card > 0)
  {
    sprintf(tmp, "/%s/%d/%d.fav", FAV_CONFIG_PATH, b, v);
    if (SD.exists(tmp))
    { //is Favorite
#ifdef TESTDISPLAY20x4
      lcd.setCursor(17, 0);
#else
      lcd.setCursor(13, 0);
#endif
      lcd.write(2); //fav symbol
    }
    else
    { // it was not a favorite
#ifdef TESTDISPLAY20x4
      lcd.setCursor(17, 0);
#else
      lcd.setCursor(13, 0);
#endif
      lcd.print(" ");
    }
  }
}

bool quick_check_favorites_in_bank(uint8_t b, uint8_t instance_id)
{
  b = constrain(b, 0, MAX_BANKS - 1);
  char tmp[18];

  if (sd_card > 0)
  {
    sprintf(tmp, "/%s/%d", FAV_CONFIG_PATH, b);
#ifdef DEBUG
    Serial.print("check if there is a Favorite in Bank: ");
    Serial.print(tmp);
    Serial.println();
#endif
    if (SD.exists(tmp) )
    { // this bank HAS at least 1 favorite(s)
#ifdef DEBUG
      Serial.println("quickcheck found a FAV in bank!");
#endif
      return (true);
    }
    else
    { // no favorites in bank stored
      return (false);
#ifdef DEBUG
      Serial.println(" - It is no Favorite in current Bank.");
#endif
    }
  }
  else
    return false;
}

void save_favorite(uint8_t b, uint8_t v, uint8_t instance_id)
{
#ifdef DEBUG
  Serial.println("Starting saving Favorite.");
#endif
  b = constrain(b, 0, MAX_BANKS - 1);
  v = constrain(v, 0, MAX_VOICES - 1);
  char tmp[18];
  char tmpfolder[18];
  File myFav;
  uint8_t i = 0, countfavs = 0;
  if (sd_card > 0)
  {
    sprintf(tmp, "/%s/%d/%d.fav", FAV_CONFIG_PATH, b, v);
    sprintf(tmpfolder, "/%s/%d", FAV_CONFIG_PATH, b);
#ifdef DEBUG
    Serial.println("Save Favorite to SD card...");
    Serial.println(tmp);
#endif
    if (!SD.exists(tmp))
    { //create Favorite Semaphore
      if (!SD.exists(tmpfolder))
      {
        SD.mkdir(tmpfolder);
      }
      myFav = SD.open(tmp, FILE_WRITE);
      myFav.close();
      Serial.println("Favorite saved...");
#ifdef TESTDISPLAY20x4
      lcd.setCursor(17, 0);
#else
      lcd.setCursor(13, 0);
#endif
      lcd.write(2); //fav symbol
#ifdef DEBUG
      Serial.println("Added to Favorites...");
#endif
    }
    else
    { // delete the file, is no longer a favorite
      SD.remove(tmp);
#ifdef DEBUG
      Serial.println("Removed from Favorites...");
#endif
      for (i = 0; i < 32; i++) { //if no other favs exist in current bank, remove folder
        sprintf(tmp, "/%s/%d/%d.fav", FAV_CONFIG_PATH, b, i);
        if (SD.exists(tmp)) countfavs++;
      }
      if (countfavs == 0) {
        sprintf(tmp, "/%s/%d", FAV_CONFIG_PATH, b);
        SD.rmdir(tmp);
#ifdef DEBUG
        Serial.println("Fav count in bank:");
        Serial.print(countfavs);
        Serial.println("Removed folder since no voice in bank flagged as favorite any more");
#endif
      }
#ifdef TESTDISPLAY20x4
      lcd.setCursor(17, 0);
#else
      lcd.setCursor(13, 0);
#endif
      lcd.print(" "); //remove fav symbol
#ifdef DEBUG
      Serial.println("Removed from Favorites...");
#endif
    }
  }
}

char* basename(const char* filename)
{
  char* p = strrchr (filename, '/');
  return p ? p + 1 : (char *) filename;
}
#endif
#endif