diff --git a/MicroDexed.ino b/MicroDexed.ino index bb8a8fa..c9005e3 100644 --- a/MicroDexed.ino +++ b/MicroDexed.ino @@ -32,7 +32,7 @@ #include #include "midi_devices.hpp" #include "dexed.h" -#include "dexed_sysex.h" +#include "dexed_sd.h" #include "effect_modulated_delay.h" #include "effect_stereo_mono.h" #include "effect_mono_stereo.h" @@ -413,7 +413,7 @@ void setup() #endif // load default SYSEX data - load_sysex(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); + load_sd_voice(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); } } @@ -872,7 +872,7 @@ void handleProgramChange(byte inChannel, byte inProgram) #ifdef DISPLAY_LCD_SPI change_disp_sd(false); #endif - load_sysex(configuration.dexed[instance_id].bank, inProgram, instance_id); + load_sd_voice(configuration.dexed[instance_id].bank, inProgram, instance_id); #ifdef DISPLAY_LCD_SPI change_disp_sd(true); #endif diff --git a/UI.hpp b/UI.hpp index d3e4b49..4bc8490 100644 --- a/UI.hpp +++ b/UI.hpp @@ -558,6 +558,7 @@ void lcdml_menu_control(void) LCDML.FUNC_setGBAToLastFunc(); else LCDML.FUNC_setGBAToLastCursorPos(); + //LCDML.FUNC_setGBA(); LCDML.OTHER_jumpToFunc(UI_func_voice_select); } @@ -3048,7 +3049,7 @@ void UI_func_voice_select(uint8_t param) #ifdef DISPLAY_LCD_SPI change_disp_sd(false); #endif - load_sysex(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); + load_sd_voice(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id); #ifdef DISPLAY_LCD_SPI change_disp_sd(true); @@ -3073,7 +3074,7 @@ void UI_func_voice_select(uint8_t param) #ifdef DISPLAY_LCD_SPI change_disp_sd(false); #endif - load_sysex(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); + load_sd_voice(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id); #ifdef DISPLAY_LCD_SPI change_disp_sd(true); @@ -3091,7 +3092,7 @@ void UI_func_voice_select(uint8_t param) #ifdef DISPLAY_LCD_SPI change_disp_sd(false); #endif - load_sysex(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); + load_sd_voice(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id); #ifdef DISPLAY_LCD_SPI change_disp_sd(true); @@ -3115,7 +3116,7 @@ void UI_func_voice_select(uint8_t param) #ifdef DISPLAY_LCD_SPI change_disp_sd(false); #endif - load_sysex(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); + load_sd_voice(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id); #ifdef DISPLAY_LCD_SPI change_disp_sd(true); @@ -3360,7 +3361,7 @@ void UI_func_save_config(uint8_t param) LCDML.DISP_clear(); lcd.print(F("Storing config")); - save_sysex_config(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); + save_sd_voiceconfig(configuration.dexed[instance_id].bank, configuration.dexed[instance_id].voice, instance_id); lcd.setCursor(0, 1); lcd.print(F("Done.")); diff --git a/config.h b/config.h index 92aa202..2b8a1bf 100644 --- a/config.h +++ b/config.h @@ -224,6 +224,7 @@ #define BANK_NAME_LEN 13 // FAT12 filenames (plus '\0') #define VOICE_NAME_LEN 11 // 10 (plus '\0') #define FILENAME_LEN BANK_NAME_LEN + VOICE_NAME_LEN +#define VOICE_CONFIG_NAME "VCFG" //************************************************************************************************* //* DO NO CHANGE ANYTHING BEYOND IF YOU DON'T KNOW WHAT YOU ARE DOING !!! @@ -502,9 +503,6 @@ enum { DEXED, CHORUS, DELAY, REVERB}; // typedef struct { - uint8_t midi_channel; - uint8_t bank; - uint8_t voice; uint8_t lowest_note; uint8_t highest_note; uint8_t reverb_send; @@ -539,6 +537,9 @@ typedef struct { uint8_t portamento_glissando; uint8_t portamento_time; uint8_t op_enabled; + uint8_t midi_channel; + uint8_t bank; + uint8_t voice; } dexed_t; // struct for holding the current configuration diff --git a/dexed_sysex.cpp b/dexed_sd.cpp similarity index 72% rename from dexed_sysex.cpp rename to dexed_sd.cpp index aca4a81..523e0e0 100644 --- a/dexed_sysex.cpp +++ b/dexed_sd.cpp @@ -28,220 +28,13 @@ #include #include #include "dexed.h" -#include "dexed_sysex.h" +#include "dexed_sd.h" -extern AudioSourceMicroDexed * MicroDexed[NUM_DEXED]; -extern void show_patch(uint8_t instance_id); +/****************************************************************************** + SD BANK/VOICE LOADING + ******************************************************************************/ -/* - void create_sysex_filename(uint8_t b, char* sysex_file_name, uint8_t instance_id) - { - // init and set name for actual bank - memset(sysex_file_name, 0, 4 + VOICE_NAME_LEN); - sysex_file_name[0] = '/'; - itoa(b, &sysex_file_name[1], 10); - strcat(sysex_file_name, "/"); - strcat(sysex_file_name, bank_names[instance_id][b]); - #ifdef DEBUG - Serial.print(F("Created sysex_filename from bank ")); - Serial.print(b, DEC); - Serial.print(F(" and name ")); - Serial.print(bank_names[instance_id][b]); - Serial.print(F(": [")); - Serial.print(sysex_file_name); - Serial.println(F("]")); - #endif - } -*/ - -void create_sysex_bankfilename(char* filename, uint8_t b, uint8_t instance_id) -{ - // init and set name for actual bank - memset(filename, 0, FILENAME_LEN); - sprintf(filename, "/%d/%s", b, bank_names[instance_id][b]); -#ifdef DEBUG - Serial.print(F("Created sysex_filename from bank ")); - Serial.print(b, DEC); - Serial.print(F(" and name ")); - Serial.print(bank_names[instance_id][b]); - Serial.print(F(": [")); - Serial.print(filename); - Serial.println(F("]")); -#endif -} - -void strip_extension(char* s, char* target) -{ - char tmp[BANK_NAME_LEN]; - char* token; - - strcpy(tmp, s); - token = strtok(tmp, "."); - if (token == NULL) - strcpy(target, "*ERROR*"); - else - strcpy(target, token); -} - -bool get_voice_names_from_bank(uint8_t b, uint8_t instance_id) -{ - File sysex; - uint8_t voice_counter = 0; - int32_t bulk_checksum_calc = 0; - int8_t bulk_checksum; - - b = constrain(b, 0, MAX_BANKS - 1); - - // erase all data for voice names - memset(voice_names[instance_id], 0, MAX_VOICES * VOICE_NAME_LEN); - - if (sd_card > 0) - { - char sysex_file_name[FILENAME_LEN]; - - // init and set name for actual bank - create_sysex_bankfilename(sysex_file_name, b, instance_id); - -#ifdef DEBUG - Serial.print(F("Reading voice names for bank [")); - Serial.print(sysex_file_name); - Serial.println(F("]")); -#endif - - // try to open bank directory - sysex = SD.open(sysex_file_name); - if (!sysex) - return (false); - - if (sysex.size() != 4104) // check sysex size - { -#ifdef DEBUG - Serial.println(F("E : SysEx file size wrong.")); -#endif - return (false); - } - if (sysex.read() != 0xf0) // check sysex start-byte - { -#ifdef DEBUG - Serial.println(F("E : SysEx start byte not found.")); -#endif - return (false); - } - if (sysex.read() != 0x43) // check sysex vendor is Yamaha - { -#ifdef DEBUG - Serial.println(F("E : SysEx vendor not Yamaha.")); -#endif - return (false); - } - sysex.seek(4103); - if (sysex.read() != 0xf7) // check sysex end-byte - { -#ifdef DEBUG - Serial.println(F("E : SysEx end byte not found.")); -#endif - return (false); - } - sysex.seek(3); - if (sysex.read() != 0x09) // check for sysex type (0x09=32 voices) - { -#ifdef DEBUG - Serial.println(F("E : SysEx type not 32 voices.")); -#endif - return (false); - } - sysex.seek(4102); // Bulk checksum - bulk_checksum = sysex.read(); - - sysex.seek(6); // start of bulk data - - for (uint16_t n = 0; n < 4096; n++) - { - uint8_t d = sysex.read(); - - if ((n % 128) >= 118 && (n % 128) < 128) // found the start of the voicename - { - voice_names[instance_id][voice_counter][(n % 128) - 118] = d; - } - if (n % 128 == 127) - voice_counter++; - bulk_checksum_calc -= d; - } - bulk_checksum_calc &= 0x7f; - -#ifdef DEBUG - Serial.print(F("Bulk checksum : 0x")); - Serial.print(bulk_checksum_calc, HEX); - Serial.print(F(" [0x")); - Serial.print(bulk_checksum, HEX); - Serial.println(F("]")); -#endif - - if (bulk_checksum_calc != bulk_checksum) - { -#ifdef DEBUG - Serial.print(F("E : Bulk checksum mismatch : 0x")); - Serial.print(bulk_checksum_calc, HEX); - Serial.print(F(" != 0x")); - Serial.println(bulk_checksum, HEX); -#endif - return (false); - } - } - - return (false); -} - -uint8_t get_bank_names(uint8_t instance_id) -{ - File root; - uint8_t bank_counter = 0; - - // erase all data for bank names - memset(bank_names[instance_id], 0, MAX_BANKS * BANK_NAME_LEN); - - if (sd_card > 0) - { - char bankdir[4]; - - do - { - // init and set name for actual bank directory - memset(bankdir, 0, sizeof(bankdir)); - bankdir[0] = '/'; - itoa(bank_counter, &bankdir[1], 10); - - // try to open directory - root = SD.open(bankdir); - if (!root) - break; - - // read filenames - File entry = root.openNextFile(); - if (!entry.isDirectory()) - { - while (strncmp(entry.name(), "CONFIG", 6) == 0) - { - entry = root.openNextFile(); - } - - strcpy(bank_names[instance_id][bank_counter], entry.name()); -#ifdef DEBUG - Serial.print(F("Found bank [")); - Serial.print(bank_names[instance_id][bank_counter]); - Serial.println(F("]")); -#endif - bank_counter++; - } - } while (root); - - return (bank_counter); - } - else - return (0); -} - -bool load_sysex(uint8_t b, uint8_t v, uint8_t instance_id) +bool load_sd_voice(uint8_t b, uint8_t v, uint8_t instance_id) { #if DEBUG bool found = false; @@ -252,27 +45,27 @@ bool load_sysex(uint8_t b, uint8_t v, uint8_t instance_id) if (sd_card > 0) { File sysex; - char sysex_file_name[FILENAME_LEN]; + char filename[FILENAME_LEN]; uint8_t data[128]; - create_sysex_bankfilename(sysex_file_name, b, instance_id); + sprintf(filename, "/%d/%s", b, bank_names[instance_id][b]); - sysex = SD.open(sysex_file_name); + sysex = SD.open(filename); if (!sysex) { #ifdef DEBUG Serial.print(F("E : Cannot open ")); - Serial.print(sysex_file_name); - Serial.println(F("from SD.")); + Serial.print(filename); + Serial.println(F(" on SD.")); #endif return (false); } - if (get_sysex_voice(sysex, v, data)) + if (get_sd_voice(sysex, v, data)) { #ifdef DEBUG - Serial.print(F("Loading sysex ")); - Serial.print(sysex_file_name); + Serial.print(F("Loading voice from ")); + Serial.print(filename); Serial.print(F(" [")); Serial.print(voice_names[instance_id][v]); Serial.println(F("]")); @@ -282,6 +75,9 @@ bool load_sysex(uint8_t b, uint8_t v, uint8_t instance_id) show_patch(instance_id); #endif configuration.dexed[instance_id].transpose = MicroDexed[instance_id]->data[DEXED_VOICE_OFFSET + DEXED_TRANSPOSE]; + + load_sd_voiceconfig(b, v, instance_id); // check for coice config to load + return (ret); } #ifdef DEBUG @@ -297,7 +93,7 @@ bool load_sysex(uint8_t b, uint8_t v, uint8_t instance_id) return (false); } -bool get_sysex_voice(File sysex, uint8_t voice_number, uint8_t* data) +bool get_sd_voice(File sysex, uint8_t voice_number, uint8_t* data) { uint16_t n; int32_t bulk_checksum_calc = 0; @@ -348,9 +144,7 @@ bool get_sysex_voice(File sysex, uint8_t voice_number, uint8_t* data) { uint8_t d = sysex.read(); if (n >= voice_number * 128 && n < (voice_number + 1) * 128) - { data[n - (voice_number * 128)] = d; - } bulk_checksum_calc -= d; } bulk_checksum_calc &= 0x7f; @@ -379,41 +173,57 @@ bool get_sysex_voice(File sysex, uint8_t voice_number, uint8_t* data) return (true); } -bool write_sysex_config(File sysex, config_t configuration) +/****************************************************************************** + SD VOICE CONFIG + ******************************************************************************/ +bool load_sd_voiceconfig(uint8_t b, uint8_t v, uint8_t instance_id) { - uint16_t n; - char* c = (char*)&configuration; - - // write sysex start - sysex.write(0xf0); - // write sysex vendor is unofficial SYSEX-ID for MicroDexed - sysex.write(0x67); - // write sysex format number (f=66..69; 1..4 MicroDexed setup(s)) - sysex.write(0x66); + if (sd_card > 0) + { + File sysex; + char filename[FILENAME_LEN]; - // write configuration data - for (n = 0; n < sizeof(configuration); n++) - sysex.write(c + n); + sprintf(filename, "/%d/%s%d.syx", b, VOICE_CONFIG_NAME, v); - // write sysex end - sysex.write(0xf7); + // first check if file exists... + if (SD.exists(filename)) + { + // ... and if: load +#ifdef DEBUG + Serial.print(F("Found voice configuration [")); + Serial.print(filename); + Serial.println(F("]... loading...")); +#endif + sysex = SD.open(filename); + if (!sysex) + { +#ifdef DEBUG + Serial.print(F("E : Cannot open ")); + Serial.print(filename); + Serial.println(F(" on SD.")); +#endif + return (get_sd_voiceconfig(sysex, (uint8_t*)&configuration.dexed[instance_id])); + } + } + else + { +#ifdef DEBUG + Serial.print(F("No ")); + Serial.print(filename); + Serial.println(F(" available.")); +#endif + } + } - return (true); + return (false); } -bool read_sysex_config(File sysex, config_t configuration) +bool get_sd_voiceconfig(File sysex, uint8_t* conf) { uint16_t n; int32_t bulk_checksum_calc = 0; int8_t bulk_checksum; - if (sysex.size() != 47 || sysex.size() != 78) // check sysex size - { -#ifdef DEBUG - Serial.println(F("E : SysEx file size wrong.")); -#endif - return (false); - } if (sysex.read() != 0xf0) // check sysex start-byte { #ifdef DEBUG @@ -428,36 +238,31 @@ bool read_sysex_config(File sysex, config_t configuration) #endif return (false); } - sysex.seek(sysex.size()); - if (sysex.read() != 0xf7) // check sysex end-byte + if (sysex.read() != 0x42) // check for sysex type (66 = MicroDexed voice setup) { #ifdef DEBUG - Serial.println(F("E : SysEx end byte not found.")); + Serial.println(F("E : SysEx type not MicroDexed setup.")); #endif return (false); } - - sysex.seek(3); - uint8_t dexed_sysex_setup_type = sysex.read(); - if (dexed_sysex_setup_type >= 0x42 && dexed_sysex_setup_type < 0x45) // check for sysex type (0x75=MicroDexed setup for one to four instances) + sysex.seek(sysex.size()); + if (sysex.read() != 0xf7) // check sysex end-byte { #ifdef DEBUG - Serial.println(F("E : SysEx type not MicroDexed setup.")); + Serial.println(F("E : SysEx end byte not found.")); #endif return (false); } - sysex.seek(4); - uint8_t sysex_config_size = sysex.read(); - sysex.seek(sysex.size() - 1); // Bulk checksum bulk_checksum = sysex.read(); - sysex.seek(6); // start of bulk data - for (n = 0; n < sysex.size() - 2; n++) + sysex.seek(3); // start of bulk data + for (n = 0; n < sysex.size() - 3; n++) { uint8_t d = sysex.read(); bulk_checksum_calc -= d; + *conf++ = d; } bulk_checksum_calc &= 0x7f; @@ -479,26 +284,15 @@ bool read_sysex_config(File sysex, config_t configuration) #endif return (false); } - - // Now everything is ok and we can read the data - sysex.seek(6); // start of bulk data - uint8_t* conf = (uint8_t*)&configuration + 4; - for (n = 0; n < sysex_config_size; n++) - { - uint8_t d = sysex.read(); #ifdef DEBUG - Serial.print(n + 6, DEC); - Serial.print(F("=")); - Serial.println(d); + Serial.println(F("Voice config loaded.")); #endif - *(conf + n) = d; - } configuration.checksum = crc32((uint8_t*)&configuration + 4, sizeof(configuration) - 4); return (true); } -bool save_sysex_config(uint8_t b, uint8_t v, uint8_t instance_id) +bool save_sd_voiceconfig(uint8_t b, uint8_t v, uint8_t instance_id) { v = constrain(v, 0, MAX_VOICES - 1); b = constrain(b, 0, MAX_BANKS - 1); @@ -506,12 +300,12 @@ bool save_sysex_config(uint8_t b, uint8_t v, uint8_t instance_id) if (sd_card > 0) { File sysex; - char sysex_config_filename[FILENAME_LEN]; + char filename[FILENAME_LEN]; - sprintf(sysex_config_filename, "/%d/config%d.syx", b, v); + sprintf(filename, "/%d/%s%d.syx", b, VOICE_CONFIG_NAME, v); #ifdef DEBUG - Serial.print(F("saving config ")); + Serial.print(F("Saving config ")); Serial.print(b); Serial.print(F("/")); Serial.print(v); @@ -519,30 +313,246 @@ bool save_sysex_config(uint8_t b, uint8_t v, uint8_t instance_id) Serial.print(instance_id); Serial.print(F("]")); Serial.print(F(" to ")); - Serial.println(sysex_config_filename); + Serial.println(filename); #endif - sysex = SD.open(sysex_config_filename, FILE_WRITE); + sysex = SD.open(filename, FILE_WRITE); if (!sysex) { #ifdef DEBUG Serial.print(F("E : Cannot open ")); - Serial.print(sysex_config_filename); + Serial.print(filename); Serial.println(F(" on SD.")); #endif return (false); } - if (write_sysex_config(sysex, configuration)) + if (write_sd_voiceconfig(sysex, (uint8_t*)&configuration.dexed[instance_id], sizeof(configuration.dexed[instance_id]))) { sysex.close(); return (true); } #ifdef DEBUG else - Serial.println(F("E : Cannot load setup data")); + Serial.println(F("E : Cannot save voice data")); +#endif + } + + return (false); +} + +bool write_sd_voiceconfig(File sysex, uint8_t* data, uint16_t len) +{ + // write sysex start + sysex.write(0xf0); + // write sysex vendor is unofficial SYSEX-ID for MicroDexed + sysex.write(0x67); + // write sysex format number (f=66 MicroDexed voice config) + sysex.write(0x42); + // write data + sysex.write(data, len - 2); // minus 2 because at the end of the struct are voice and ban number - not very interesting here... + // write checksum + sysex.write(calc_checksum(data, len - 2)); + // write sysex end + sysex.write(0xf7); + + return (true); +} + +/****************************************************************************** + HELPER FUNCTIONS + ******************************************************************************/ + +uint8_t calc_checksum(uint8_t* data, uint16_t len) +{ + int32_t bulk_checksum_calc = 0; + + for (uint16_t n = 0; n < len; n++) + bulk_checksum_calc -= data[n]; + + return (bulk_checksum_calc & 0x7f); +} + +void create_sd_voiceconfig_filename(char* filename, uint8_t b, uint8_t instance_id) +{ + // init and set name for actual bank + memset(filename, 0, FILENAME_LEN); + sprintf(filename, "/%d/%s", b, bank_names[instance_id][b]); +#ifdef DEBUG + Serial.print(F("Created filename from bank ")); + Serial.print(b, DEC); + Serial.print(F(" and name ")); + Serial.print(bank_names[instance_id][b]); + Serial.print(F(": [")); + Serial.print(filename); + Serial.println(F("]")); +#endif +} + +void strip_extension(char* s, char* target) +{ + char tmp[BANK_NAME_LEN]; + char* token; + + strcpy(tmp, s); + token = strtok(tmp, "."); + if (token == NULL) + strcpy(target, "*ERROR*"); + else + strcpy(target, token); +} + +bool get_voice_names_from_bank(uint8_t b, uint8_t instance_id) +{ + File sysex; + uint8_t voice_counter = 0; + int32_t bulk_checksum_calc = 0; + int8_t bulk_checksum; + + b = constrain(b, 0, MAX_BANKS - 1); + + // erase all data for voice names + memset(voice_names[instance_id], 0, MAX_VOICES * VOICE_NAME_LEN); + + if (sd_card > 0) + { + char filename[FILENAME_LEN]; + + sprintf(filename, "/%d/%s", b, bank_names[instance_id][b]); + +#ifdef DEBUG + Serial.print(F("Reading voice names for bank [")); + Serial.print(filename); + Serial.println(F("]")); +#endif + + // try to open bank directory + sysex = SD.open(filename); + if (!sysex) + return (false); + + if (sysex.size() != 4104) // check sysex size + { +#ifdef DEBUG + Serial.println(F("E : SysEx file size wrong.")); +#endif + return (false); + } + if (sysex.read() != 0xf0) // check sysex start-byte + { +#ifdef DEBUG + Serial.println(F("E : SysEx start byte not found.")); +#endif + return (false); + } + if (sysex.read() != 0x43) // check sysex vendor is Yamaha + { +#ifdef DEBUG + Serial.println(F("E : SysEx vendor not Yamaha.")); +#endif + return (false); + } + sysex.seek(4103); + if (sysex.read() != 0xf7) // check sysex end-byte + { +#ifdef DEBUG + Serial.println(F("E : SysEx end byte not found.")); +#endif + return (false); + } + sysex.seek(3); + if (sysex.read() != 0x09) // check for sysex type (0x09=32 voices) + { +#ifdef DEBUG + Serial.println(F("E : SysEx type not 32 voices.")); #endif + return (false); + } + sysex.seek(4102); // Bulk checksum + bulk_checksum = sysex.read(); + + sysex.seek(6); // start of bulk data + + for (uint16_t n = 0; n < 4096; n++) + { + uint8_t d = sysex.read(); + + if ((n % 128) >= 118 && (n % 128) < 128) // found the start of the voicename + { + voice_names[instance_id][voice_counter][(n % 128) - 118] = d; + } + if (n % 128 == 127) + voice_counter++; + bulk_checksum_calc -= d; + } + bulk_checksum_calc &= 0x7f; + +#ifdef DEBUG + Serial.print(F("Bulk checksum : 0x")); + Serial.print(bulk_checksum_calc, HEX); + Serial.print(F(" [0x")); + Serial.print(bulk_checksum, HEX); + Serial.println(F("]")); +#endif + + if (bulk_checksum_calc != bulk_checksum) + { +#ifdef DEBUG + Serial.print(F("E : Bulk checksum mismatch : 0x")); + Serial.print(bulk_checksum_calc, HEX); + Serial.print(F(" != 0x")); + Serial.println(bulk_checksum, HEX); +#endif + return (false); + } } return (false); } + +uint8_t get_bank_names(uint8_t instance_id) +{ + File root; + uint8_t bank_counter = 0; + + // erase all data for bank names + memset(bank_names[instance_id], 0, MAX_BANKS * BANK_NAME_LEN); + + if (sd_card > 0) + { + char bankdir[4]; + + do + { + // init and set name for actual bank directory + sprintf(bankdir, "/%d", bank_counter); + + // try to open directory + root = SD.open(bankdir); + if (!root) + break; + + // read filenames + File entry = root.openNextFile(); + if (!entry.isDirectory()) + { + while ((strncmp(entry.name(), VOICE_CONFIG_NAME, sizeof(VOICE_CONFIG_NAME) - 1) == 0)) + { + entry = root.openNextFile(); + } + + strcpy(bank_names[instance_id][bank_counter], entry.name()); +#ifdef DEBUG + Serial.print(F("Found bank [")); + Serial.print(bank_names[instance_id][bank_counter]); + Serial.println(F("]")); +#endif + bank_counter++; + } + } while (root); + + return (bank_counter); + } + else + return (0); +} diff --git a/dexed_sysex.h b/dexed_sd.h similarity index 75% rename from dexed_sysex.h rename to dexed_sd.h index 3622906..0b0c6fb 100644 --- a/dexed_sysex.h +++ b/dexed_sd.h @@ -32,7 +32,8 @@ extern uint8_t sd_card; extern Dexed* dexed; extern AudioSourceMicroDexed * MicroDexed[NUM_DEXED]; -//extern uint16_t render_time_max; +extern void show_patch(uint8_t instance_id); + extern uint8_t bank; extern uint8_t voice; extern char bank_name[NUM_DEXED][BANK_NAME_LEN]; @@ -44,16 +45,16 @@ extern uint8_t ui_main_state; extern config_t configuration; extern uint32_t crc32(byte * calc_start, uint16_t calc_bytes); -void create_sysex_bankfilename(char* filename, uint8_t b, uint8_t instance_id); +bool load_sd_voice(uint8_t b, uint8_t v, uint8_t instance_id); +bool get_sd_voice(File sysex, uint8_t voice_number, uint8_t* data); + +bool load_sd_voiceconfig(uint8_t b, uint8_t v, uint8_t instance_id); +bool get_sd_voiceconfig(File sysex, uint8_t* conf); +bool save_sd_voiceconfig(uint8_t b, uint8_t v, uint8_t instance_id); +bool write_sd_voiceconfig(File sysex, uint8_t* data, uint16_t len); + +uint8_t calc_checksum(uint8_t* data, uint16_t len); void strip_extension(char* s, char *target); bool get_voice_names_from_bank(uint8_t b, uint8_t i); uint8_t get_bank_names(uint8_t instance_id); -bool get_bank_voice_name(uint8_t b, uint8_t v); -bool load_sysex(uint8_t b, uint8_t v, uint8_t instance_id); -bool get_sysex_voice(File sysex, uint8_t voice_number, uint8_t* data); -void create_sysex_setup_filename(uint8_t b, uint8_t v, char* sysex_setup_file_name); -bool write_sysex_config(File sysex, config_t configuration); -bool load_sysex_config(uint8_t b, uint8_t v, uint8_t instance_id); -bool save_sysex_config(uint8_t b, uint8_t v, uint8_t instance_id); - #endif diff --git a/doc/sysex-format_dexed-setup.txt b/doc/sysex-format_dexed-setup.txt index f2851b7..fa58702 100644 --- a/doc/sysex-format_dexed-setup.txt +++ b/doc/sysex-format_dexed-setup.txt @@ -6,53 +6,54 @@ SYSEX Message Controller-Set 11110000 F0 Status byte - start sysex 0iiiiiii 67 ID # (i=103; unofficial SYSEX-ID for MicroDexed) - 0fffffff ** format number (f=66 MicroDexed config) + 0fffffff ** format number (f=66 MicroDexed voice config) 0ddddddd ** data byte 1 setup config | | | - 0ddddddd ** data byte 13 - 0ddddddd ** data byte 14 instance config 0 - - | | | - - 0ddddddd ** data byte 44 + 0ddddddd ** data byte 35 + 0eeeeeee ** checksum (masked 2's complement of sum of 155 bytes) + 11110111 F7 Status - end sysex Data Structure: MicroDexed Setup Dump ------------------------------------- Number Parameter Value Range --------- --------- ----------- -11 DEXED 1 MIDI-CHANNEL 0-16 (0=OMNI) -12 DEXED 1 BANK 0-99 -13 DEXED 1 VOICE 0-31 -14 DEXED 1 LOWEST NOTE 21-108 -15 DEXED 1 HIGHEST NOTE 21-108 -16 DEXED 1 REVERB SEND 0-100 -17 DEXED 1 CHORUS SEND 0-100 -18 DEXED 1 DELAY SEND 0-100 -19 DEXED 2 FILTER-CUTOFF 0-100 -20 DEXED 2 FILTER-RESONANCE 0-100 -21 DEXED 2 TRANSPOSE 0-48 -22 DEXED 2 TUNE 0-100 -23 DEXED 2 SOUND INTENSITY 0-100 -24 DEXED 2 PANORAMA 0-40 (20 is middle) -25 DEXED 1 POLYPHONY 0-32 (depends on CPU and FX) -26 DEXED 1 ENGINE 0-2 (1=Modern, 2=Mark1, 2=OPL) -27 DEXED 1 MONO/POLY MODE CHANGE 0-1 O=POLY -28 DEXED 1 PITCH BEND RANGE 0-12 -29 DEXED 1 " " STEP 0-12 -30 DEXED 1 MOD WHEEL RANGE 0-99 -31 DEXED 1 " " ASSIGN 0-7 b0: pitch, b1:amp, b2: EG bias -32 DEXED 1 FOOT CONTROL RANGE 0-99 -33 DEXED 1 " " ASSIGN 0-7 " -34 DEXED 1 BREATH CONT RANGE 0-99 -35 DEXED 1 " " ASSIGN 0-7 " -36 DEXED 1 AFTERTOUCH RANGE 0-99 -37 DEXED 1 " ASSIGN 0-7 " -38 DEXED 1 PORTAMENTO MODE 0-1 0=RETAIN 1=FOLLOW -39 DEXED 1 " GLISS 0-1 -40 DEXED 1 " TIME 0-99 -41 DEXED 1 OP_ENABLE 0-31 +01 DEXED LOWEST NOTE 21-108 +02 DEXED HIGHEST NOTE 21-108 +03 DEXED REVERB SEND 0-100 +04 DEXED CHORUS SEND 0-100 +05 DEXED DELAY SEND 0-100 +06 DEXED FILTER-CUTOFF 0-100 +07 DEXED FILTER-RESONANCE 0-100 +08 DEXED TRANSPOSE 0-48 +09 DEXED TUNE 0-100 +10 DEXED SOUND INTENSITY 0-100 +11 DEXED PANORAMA 0-40 (20 is middle) +12 DEXED POLYPHONY 0-32 (depends on CPU and FX) +13 DEXED VELOCITY LEVEL 100-127 +14 DEXED ENGINE 0-2 1=Modern, 2=Mark1, 2=OPL +15 DEXED MONO/POLY MODE CHANGE 0-1 O=POLY +16 DEXED NOTE REFRESH MODE 0-2 +17 DEXED PITCH BEND RANGE 0-12 +18 DEXED " " STEP 0-12 +19 DEXED MOD WHEEL RANGE 0-99 +20 DEXED " " ASSIGN 0-7 b0: pitch, b1:amp, b2: EG bias +21 DEXED " " MODE 0-2 1=linear, 2=reverse, 3=direct +22 DEXED FOOT CONTROL RANGE 0-99 +23 DEXED " " ASSIGN 0-7 " +24 DEXED " " MODE 0-2 1=linear, 2=reverse, 3=direct +25 DEXED BREATH CONT RANGE 0-99 +26 DEXED " " ASSIGN 0-7 " +27 DEXED " " MODE 0-2 1=linear, 2=reverse, 3=direct +28 DEXED AFTERTOUCH RANGE 0-99 +29 DEXED " ASSIGN 0-7 " +30 DEXED " MODE 0-2 1=linear, 2=reverse, 3=direct +31 DEXED PORTAMENTO MODE 0-1 0=RETAIN 1=FOLLOW +32 DEXED " GLISS 0-1 +33 DEXED " TIME 0-99 +34 DEXED OP_ENABLE 0-31 +35 DEXED MIDI-CHANNEL 0-16 0=OMNI --------- Number Parameter Value Range