From 1d4c19e49cdad0c072f332e6e17b95ba1724def8 Mon Sep 17 00:00:00 2001 From: Holger Wirtz Date: Fri, 11 Oct 2019 17:10:00 +0200 Subject: [PATCH] Added switching logic between voice and menu. Added autotmatic volume screen for left encoder (right/left). --- LiquidCrystalPlus_I2C.h | 88 +++++++++++++++++++++ MicroDexed.ino | 10 ++- UI.hpp | 167 +++++++++++++++++++++++++++++++--------- config.h | 3 + dexed.h | 1 - 5 files changed, 230 insertions(+), 39 deletions(-) create mode 100644 LiquidCrystalPlus_I2C.h diff --git a/LiquidCrystalPlus_I2C.h b/LiquidCrystalPlus_I2C.h new file mode 100644 index 0000000..6c8cd33 --- /dev/null +++ b/LiquidCrystalPlus_I2C.h @@ -0,0 +1,88 @@ +/* + MicroDexed + + MicroDexed is a port of the Dexed sound engine + (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6 with audio shield. + Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android + + (c)2018 H. Wirtz + + 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 + +*/ + +#include // https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c + +#ifndef LIQUIDCRYSTALPLUS_I2C_H_INCLUDED +#define LIQUIDCRYSTALPLUS_I2C_H_INCLUDED + +#define STRING_BUFFER_SIZE 21 + +class LiquidCrystalPlus_I2C : public LiquidCrystal_I2C +{ + public: + + using LiquidCrystal_I2C::LiquidCrystal_I2C; + + void show(uint8_t y, uint8_t x, uint8_t fs, char *str) + { + _show(y, x, fs, str, false, false); + } + + void show(uint8_t y, uint8_t x, uint8_t fs, long num) + { + char _buf10[STRING_BUFFER_SIZE]; + + _show(y, x, fs, itoa(num, _buf10, 10), true, true); + } + + private: + void _show(uint8_t pos_y, uint8_t pos_x, uint8_t field_size, char *str, bool justify_right, bool fill_zero) + { + { + char tmp[STRING_BUFFER_SIZE]; + char *s = tmp; + uint8_t l = strlen(str); + + memset(tmp, 0, sizeof(tmp)); + if (fill_zero == true) + memset(tmp, '0', field_size); + else + memset(tmp, 0x20, field_size - 1); // blank + + if (l > field_size) + l = field_size; + + if (justify_right == true) + s += field_size - l; + + strncpy(s, str, l); + + setCursor(pos_x, pos_y); + print(tmp); + +#ifdef DEBUG + Serial.print(pos_y, DEC); + Serial.print(F("/")); + Serial.print(pos_x, DEC); + Serial.print(F(": [")); + Serial.print(tmp); + Serial.println(F("]")); +#endif + } + } +}; + +#endif diff --git a/MicroDexed.ino b/MicroDexed.ino index 7452e66..86dfbc0 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -110,8 +110,9 @@ value_change_t soften_filter_cut = {0.0, 0}; /*********************************************************************** LCDMenuLib2 ***********************************************************************/ -extern LiquidCrystal_I2C lcd; +extern LiquidCrystalPlus_I2C lcd; extern LCDMenuLib2 LCDML; +extern uint8_t menu_state; #endif void setup() @@ -338,6 +339,13 @@ void loop() #ifdef ENABLE_LCD_UI // LCD Menu LCDML.loop(); + + // initial starts voice selection menu as default + if (menu_state == MENU_START) + { + menu_state = MENU_VOICE; + UI_func_voice_selection(0); + } #endif control_rate = 0; diff --git a/UI.hpp b/UI.hpp index 5027110..05e5e21 100644 --- a/UI.hpp +++ b/UI.hpp @@ -27,7 +27,7 @@ #define _UI_HPP_ #include "config.h" -#include +#include "LiquidCrystalPlus_I2C.h" #include #include @@ -37,10 +37,20 @@ #define _LCDML_DISP_cfg_cursor 0x7E // cursor Symbol #define _LCDML_DISP_cfg_scrollbar 1 // enable a scrollbar +extern config_t configuration; +void set_volume(float v, float p); +extern char bank_names[MAX_BANKS][BANK_NAME_LEN]; +extern char bank_name[BANK_NAME_LEN]; +extern char voice_name[VOICE_NAME_LEN]; +extern char voice_names[MAX_VOICES][VOICE_NAME_LEN]; +extern void strip_extension(char* s, char *target); + /*********************************************************************** GLOBAL - ***********************************************************************/ -LiquidCrystal_I2C lcd(0x27, _LCDML_DISP_cols, _LCDML_DISP_rows); +************************************************************************/ +elapsedMillis back_from_volume; + +LiquidCrystalPlus_I2C lcd(LCD_I2C_ADDRESS, _LCDML_DISP_cols, _LCDML_DISP_rows); const uint8_t scroll_bar[5][8] = { {B10001, B10001, B10001, B10001, B10001, B10001, B10001, B10001}, // scrollbar top @@ -51,8 +61,8 @@ const uint8_t scroll_bar[5][8] = { }; enum { ENC_R, ENC_L }; -enum { MENU_VOICE, MENU_EDIT}; -uint8_t menu_state = MENU_EDIT; +enum { MENU_START, MENU_VOICE, MENU_EDIT, MENU_VOLUME }; +uint8_t menu_state = MENU_START; void lcdml_menu_display(void); void lcdml_voice_menu_display(void); @@ -79,6 +89,7 @@ void UI_func_polyphony(uint8_t param); void UI_func_engine(uint8_t param); void UI_func_information(uint8_t param); void UI_func_voice_selection(uint8_t param); +void UI_func_volume(uint8_t param); void UI_func_back(uint8_t param); void UI_func_goToRootMenu(uint8_t param); @@ -121,8 +132,8 @@ LCDML_createMenu(_LCDML_DISP_cnt); /*********************************************************************** CONTROL ***********************************************************************/ -#define g_LCDML_CONTROL_button_long_press 800 // ms -#define g_LCDML_CONTROL_button_short_press 40 // ms +#define g_LCDML_CONTROL_button_long_press LONG_BUTTON_PRESS +#define g_LCDML_CONTROL_button_short_press BUT_DEBOUNCE_MS //#define ENCODER_OPTIMIZE_INTERRUPTS //Only when using pin2/3 (or 20/21 on mega) Encoder ENCODER[NUM_ENCODER] = {Encoder(ENC_R_PIN_B, ENC_R_PIN_A), Encoder(ENC_L_PIN_B, ENC_L_PIN_A)}; @@ -140,11 +151,19 @@ void lcdml_menu_control(void) pinMode(BUT_L_PIN, INPUT_PULLUP); } + if (back_from_volume > BACK_FROM_VOLUME_MS && menu_state == MENU_VOLUME) + { + UI_func_voice_selection(0); + return; + } + //Volatile Variable 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)}; - // Right encoder + /************************************************************************************ + Right encoder + ************************************************************************************/ if (g_LCDML_CONTROL_Encoder_position[ENC_R] <= -3) { if (!button[ENC_R]) @@ -155,8 +174,19 @@ void lcdml_menu_control(void) } else { - if (menu_state == MENU_EDIT) - LCDML.BT_down(); + switch (menu_state) + { + case MENU_EDIT: + case MENU_VOLUME: + menu_state = MENU_EDIT; + LCDML.BT_down(); + break; + case MENU_VOICE: +#ifdef DEBUG + Serial.println(F("State: MENU_VOICE, Encoder left down")); + break; +#endif + } } ENCODER[ENC_R].write(g_LCDML_CONTROL_Encoder_position[ENC_R] + 4); } @@ -171,8 +201,19 @@ void lcdml_menu_control(void) } else { - if (menu_state == MENU_EDIT) - LCDML.BT_up(); + switch (menu_state) + { + case MENU_EDIT: + case MENU_VOLUME: + menu_state = MENU_EDIT; + LCDML.BT_up(); + break; + case MENU_VOICE: +#ifdef DEBUG + Serial.println(F("State: MENU_VOICE, Encoder left up")); + break; +#endif + } } ENCODER[ENC_R].write(g_LCDML_CONTROL_Encoder_position[ENC_R] - 4); } @@ -195,15 +236,36 @@ void lcdml_menu_control(void) else if ((millis() - g_LCDML_CONTROL_button_press_time[ENC_R]) >= g_LCDML_CONTROL_button_long_press) { LCDML.BT_quit(); + if (menu_state == MENU_EDIT) + { + LCDML.BT_quit(); + } + else if (menu_state == MENU_VOICE) + { +#ifdef DEBUG + Serial.println(F("State: MENU_VOICE, button long press")); +#endif + } } else if ((millis() - g_LCDML_CONTROL_button_press_time[ENC_R]) >= g_LCDML_CONTROL_button_short_press) { - LCDML.BT_enter(); + if (menu_state == MENU_EDIT) + { + LCDML.BT_enter(); + } + else if (menu_state == MENU_VOICE) + { +#ifdef DEBUG + Serial.println(F("State: MENU_VOICE, button short press")); +#endif + } } } } - // Left encoder + /************************************************************************************ + Left encoder + ************************************************************************************/ if (g_LCDML_CONTROL_Encoder_position[ENC_L] <= -3) { if (!button[ENC_L]) @@ -214,10 +276,14 @@ void lcdml_menu_control(void) } else { - #ifdef DEBUG Serial.println(F("Volume +")); #endif + if (configuration.vol < 1.0) + { + set_volume(configuration.vol + 0.05, configuration.pan); + UI_func_volume(0); + } } ENCODER[ENC_L].write(g_LCDML_CONTROL_Encoder_position[ENC_L] + 4); } @@ -235,6 +301,11 @@ void lcdml_menu_control(void) #ifdef DEBUG Serial.println(F("Volume -")); #endif + if (configuration.vol > 0.0) + { + set_volume(configuration.vol - 0.05, configuration.pan); + UI_func_volume(0); + } } ENCODER[ENC_L].write(g_LCDML_CONTROL_Encoder_position[ENC_L] - 4); } @@ -270,7 +341,7 @@ void lcdml_menu_control(void) if (menu_state == MENU_EDIT) { menu_state = MENU_VOICE; - LCDML.OTHER_jumpToFunc(UI_func_voice_selection,0); + UI_func_voice_selection(0); } else if (menu_state == MENU_VOICE) { @@ -542,31 +613,53 @@ void UI_func_information(uint8_t param) void UI_func_voice_selection(uint8_t param) { - if (LCDML.FUNC_setup()) // ****** SETUP ********* - { - // update LCD content - lcd.clear(); - lcd.setCursor(0, 0); // set cursor - lcd.print("screensaver"); // print change content - lcd.setCursor(0, 1); // set cursor - lcd.print("press any key"); - LCDML.FUNC_setLoopInterval(100); // starts a trigger event for the loop function every 100 milliseconds - } +#ifdef DEBUG + Serial.println(F("UI_func_voice_selection()")); +#endif - if (LCDML.FUNC_loop()) - { - if (LCDML.BT_checkEnter()) - { - LCDML.MENU_goRoot(); - } - } + menu_state = MENU_VOICE; + + // update LCD content + LCDML.DISP_clear(); + + lcd.show(0, 0, 2, configuration.bank); + lcd.show(0, 2, 1, " "); + strip_extension(bank_names[configuration.bank], bank_name); + + lcd.show(0, 2, 1, " "); + lcd.show(0, 3, 8, bank_name); + lcd.show(0, 11, 1, " "); - if (LCDML.FUNC_close()) + lcd.show(1, 0, 2, configuration.voice + 1); + lcd.show(1, 2, 1, " "); + + lcd.show(1, 2, 1, "["); + lcd.show(1, 3, 10, voice_names[configuration.voice]); + lcd.show(1, 14, 1, "]"); +} + +void UI_func_volume(uint8_t param) +{ +#ifdef DEBUG + Serial.println(F("UI_func_volume()")); +#endif + + menu_state = MENU_VOLUME; + back_from_volume = 0; + + // update LCD content + LCDML.DISP_clear(); + lcd.setCursor(0, 0); // set cursor + lcd.print("Volume"); // print change content + lcd.setCursor(0, 1); // set cursor + + for (uint8_t i = 0; i < LCD_cols; i++) { - // The screensaver go to the root menu - LCDML.MENU_goRoot(); + if (i < int(LCD_cols * configuration.vol + 0.5)) + lcd.print("*"); + else + lcd.print(" "); } } - #endif #endif diff --git a/config.h b/config.h index 92352a4..68410a4 100644 --- a/config.h +++ b/config.h @@ -102,10 +102,13 @@ //* UI //************************************************************************************************* #define ENABLE_LCD_UI 1 +#define LCD_I2C_ADDRESS 0x27 #define LCD_cols 16 #define LCD_rows 2 #define CONTROL_RATE_MS 50 +#define BACK_FROM_VOLUME_MS 1000 + //************************************************************************************************* //* DEBUG OUTPUT SETTINGS //************************************************************************************************* diff --git a/dexed.h b/dexed.h index deeb2bb..a3bdaf0 100644 --- a/dexed.h +++ b/dexed.h @@ -44,7 +44,6 @@ extern AudioControlSGTL5000 sgtl5000_1; extern float vol; extern float vol_right; extern float vol_left; -extern void set_volume(float master_volume, float volume_right, float volume_left); extern char voice_name[11]; struct ProcessorVoice {