Reoganized SD card read/write.

Added reading and writing of dexed-configurations.
pull/32/head
Holger Wirtz 5 years ago
parent eeaf8b6f46
commit 448b3c4bd8
  1. 6
      MicroDexed.ino
  2. 11
      UI.hpp
  3. 7
      config.h
  4. 522
      dexed_sd.cpp
  5. 21
      dexed_sd.h
  6. 77
      doc/sysex-format_dexed-setup.txt

@ -32,7 +32,7 @@
#include <SPI.h> #include <SPI.h>
#include "midi_devices.hpp" #include "midi_devices.hpp"
#include "dexed.h" #include "dexed.h"
#include "dexed_sysex.h" #include "dexed_sd.h"
#include "effect_modulated_delay.h" #include "effect_modulated_delay.h"
#include "effect_stereo_mono.h" #include "effect_stereo_mono.h"
#include "effect_mono_stereo.h" #include "effect_mono_stereo.h"
@ -413,7 +413,7 @@ void setup()
#endif #endif
// load default SYSEX data // 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 #ifdef DISPLAY_LCD_SPI
change_disp_sd(false); change_disp_sd(false);
#endif #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 #ifdef DISPLAY_LCD_SPI
change_disp_sd(true); change_disp_sd(true);
#endif #endif

@ -558,6 +558,7 @@ void lcdml_menu_control(void)
LCDML.FUNC_setGBAToLastFunc(); LCDML.FUNC_setGBAToLastFunc();
else else
LCDML.FUNC_setGBAToLastCursorPos(); LCDML.FUNC_setGBAToLastCursorPos();
//LCDML.FUNC_setGBA();
LCDML.OTHER_jumpToFunc(UI_func_voice_select); LCDML.OTHER_jumpToFunc(UI_func_voice_select);
} }
@ -3048,7 +3049,7 @@ void UI_func_voice_select(uint8_t param)
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(false); change_disp_sd(false);
#endif #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); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id);
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(true); change_disp_sd(true);
@ -3073,7 +3074,7 @@ void UI_func_voice_select(uint8_t param)
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(false); change_disp_sd(false);
#endif #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); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id);
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(true); change_disp_sd(true);
@ -3091,7 +3092,7 @@ void UI_func_voice_select(uint8_t param)
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(false); change_disp_sd(false);
#endif #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); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id);
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(true); change_disp_sd(true);
@ -3115,7 +3116,7 @@ void UI_func_voice_select(uint8_t param)
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(false); change_disp_sd(false);
#endif #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); get_voice_names_from_bank(configuration.dexed[instance_id].bank, instance_id);
#ifdef DISPLAY_LCD_SPI #ifdef DISPLAY_LCD_SPI
change_disp_sd(true); change_disp_sd(true);
@ -3360,7 +3361,7 @@ void UI_func_save_config(uint8_t param)
LCDML.DISP_clear(); LCDML.DISP_clear();
lcd.print(F("Storing config")); 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.setCursor(0, 1);
lcd.print(F("Done.")); lcd.print(F("Done."));

@ -224,6 +224,7 @@
#define BANK_NAME_LEN 13 // FAT12 filenames (plus '\0') #define BANK_NAME_LEN 13 // FAT12 filenames (plus '\0')
#define VOICE_NAME_LEN 11 // 10 (plus '\0') #define VOICE_NAME_LEN 11 // 10 (plus '\0')
#define FILENAME_LEN BANK_NAME_LEN + VOICE_NAME_LEN #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 !!! //* 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 { typedef struct {
uint8_t midi_channel;
uint8_t bank;
uint8_t voice;
uint8_t lowest_note; uint8_t lowest_note;
uint8_t highest_note; uint8_t highest_note;
uint8_t reverb_send; uint8_t reverb_send;
@ -539,6 +537,9 @@ typedef struct {
uint8_t portamento_glissando; uint8_t portamento_glissando;
uint8_t portamento_time; uint8_t portamento_time;
uint8_t op_enabled; uint8_t op_enabled;
uint8_t midi_channel;
uint8_t bank;
uint8_t voice;
} dexed_t; } dexed_t;
// struct for holding the current configuration // struct for holding the current configuration

@ -28,90 +28,76 @@
#include <Wire.h> #include <Wire.h>
#include <SD.h> #include <SD.h>
#include "dexed.h" #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
******************************************************************************/
/* bool load_sd_voice(uint8_t b, uint8_t v, uint8_t instance_id)
void create_sysex_filename(uint8_t b, char* sysex_file_name, uint8_t instance_id)
{ {
// init and set name for actual bank #if DEBUG
memset(sysex_file_name, 0, 4 + VOICE_NAME_LEN); bool found = false;
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 #endif
} v = constrain(v, 0, MAX_VOICES - 1);
*/ b = constrain(b, 0, MAX_BANKS - 1);
void create_sysex_bankfilename(char* filename, uint8_t b, uint8_t instance_id) if (sd_card > 0)
{ {
// init and set name for actual bank File sysex;
memset(filename, 0, FILENAME_LEN); char filename[FILENAME_LEN];
uint8_t data[128];
sprintf(filename, "/%d/%s", b, bank_names[instance_id][b]); sprintf(filename, "/%d/%s", b, bank_names[instance_id][b]);
sysex = SD.open(filename);
if (!sysex)
{
#ifdef DEBUG #ifdef DEBUG
Serial.print(F("Created sysex_filename from bank ")); Serial.print(F("E : Cannot open "));
Serial.print(b, DEC);
Serial.print(F(" and name "));
Serial.print(bank_names[instance_id][b]);
Serial.print(F(": ["));
Serial.print(filename); Serial.print(filename);
Serial.println(F("]")); Serial.println(F(" on SD."));
#endif #endif
return (false);
} }
void strip_extension(char* s, char* target) if (get_sd_voice(sysex, v, data))
{ {
char tmp[BANK_NAME_LEN]; #ifdef DEBUG
char* token; Serial.print(F("Loading voice from "));
Serial.print(filename);
Serial.print(F(" ["));
Serial.print(voice_names[instance_id][v]);
Serial.println(F("]"));
#endif
bool ret = MicroDexed[instance_id]->decodeVoice(data);
#ifdef DEBUG
show_patch(instance_id);
#endif
configuration.dexed[instance_id].transpose = MicroDexed[instance_id]->data[DEXED_VOICE_OFFSET + DEXED_TRANSPOSE];
strcpy(tmp, s); load_sd_voiceconfig(b, v, instance_id); // check for coice config to load
token = strtok(tmp, ".");
if (token == NULL) return (ret);
strcpy(target, "*ERROR*"); }
#ifdef DEBUG
else else
strcpy(target, token); Serial.println(F("E : Cannot load voice data"));
#endif
} }
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 #ifdef DEBUG
Serial.print(F("Reading voice names for bank [")); if (found == false)
Serial.print(sysex_file_name); Serial.println(F("E : File not found."));
Serial.println(F("]"));
#endif #endif
// try to open bank directory
sysex = SD.open(sysex_file_name);
if (!sysex)
return (false); return (false);
}
bool get_sd_voice(File sysex, uint8_t voice_number, uint8_t* data)
{
uint16_t n;
int32_t bulk_checksum_calc = 0;
int8_t bulk_checksum;
if (sysex.size() != 4104) // check sysex size if (sysex.size() != 4104) // check sysex size
{ {
@ -154,17 +140,11 @@ bool get_voice_names_from_bank(uint8_t b, uint8_t instance_id)
bulk_checksum = sysex.read(); bulk_checksum = sysex.read();
sysex.seek(6); // start of bulk data sysex.seek(6); // start of bulk data
for (n = 0; n < 4096; n++)
for (uint16_t n = 0; n < 4096; n++)
{ {
uint8_t d = sysex.read(); uint8_t d = sysex.read();
if (n >= voice_number * 128 && n < (voice_number + 1) * 128)
if ((n % 128) >= 118 && (n % 128) < 128) // found the start of the voicename data[n - (voice_number * 128)] = d;
{
voice_names[instance_id][voice_counter][(n % 128) - 118] = d;
}
if (n % 128 == 127)
voice_counter++;
bulk_checksum_calc -= d; bulk_checksum_calc -= d;
} }
bulk_checksum_calc &= 0x7f; bulk_checksum_calc &= 0x7f;
@ -187,129 +167,63 @@ bool get_voice_names_from_bank(uint8_t b, uint8_t instance_id)
#endif #endif
return (false); return (false);
} }
}
return (false); MicroDexed[0]->render_time_max = 0;
return (true);
} }
uint8_t get_bank_names(uint8_t instance_id) /******************************************************************************
SD VOICE CONFIG
******************************************************************************/
bool load_sd_voiceconfig(uint8_t b, uint8_t v, 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) if (sd_card > 0)
{ {
char bankdir[4]; File sysex;
char filename[FILENAME_LEN];
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 sprintf(filename, "/%d/%s%d.syx", b, VOICE_CONFIG_NAME, v);
root = SD.open(bankdir);
if (!root)
break;
// read filenames // first check if file exists...
File entry = root.openNextFile(); if (SD.exists(filename))
if (!entry.isDirectory())
{ {
while (strncmp(entry.name(), "CONFIG", 6) == 0) // ... and if: load
{
entry = root.openNextFile();
}
strcpy(bank_names[instance_id][bank_counter], entry.name());
#ifdef DEBUG #ifdef DEBUG
Serial.print(F("Found bank [")); Serial.print(F("Found voice configuration ["));
Serial.print(bank_names[instance_id][bank_counter]); Serial.print(filename);
Serial.println(F("]")); Serial.println(F("]... loading..."));
#endif
bank_counter++;
}
} while (root);
return (bank_counter);
}
else
return (0);
}
bool load_sysex(uint8_t b, uint8_t v, uint8_t instance_id)
{
#if DEBUG
bool found = false;
#endif #endif
v = constrain(v, 0, MAX_VOICES - 1); sysex = SD.open(filename);
b = constrain(b, 0, MAX_BANKS - 1);
if (sd_card > 0)
{
File sysex;
char sysex_file_name[FILENAME_LEN];
uint8_t data[128];
create_sysex_bankfilename(sysex_file_name, b, instance_id);
sysex = SD.open(sysex_file_name);
if (!sysex) if (!sysex)
{ {
#ifdef DEBUG #ifdef DEBUG
Serial.print(F("E : Cannot open ")); Serial.print(F("E : Cannot open "));
Serial.print(sysex_file_name); Serial.print(filename);
Serial.println(F("from SD.")); Serial.println(F(" on SD."));
#endif #endif
return (false); return (get_sd_voiceconfig(sysex, (uint8_t*)&configuration.dexed[instance_id]));
} }
}
if (get_sysex_voice(sysex, v, data)) else
{ {
#ifdef DEBUG #ifdef DEBUG
Serial.print(F("Loading sysex ")); Serial.print(F("No "));
Serial.print(sysex_file_name); Serial.print(filename);
Serial.print(F(" [")); Serial.println(F(" available."));
Serial.print(voice_names[instance_id][v]);
Serial.println(F("]"));
#endif
bool ret = MicroDexed[instance_id]->decodeVoice(data);
#ifdef DEBUG
show_patch(instance_id);
#endif #endif
configuration.dexed[instance_id].transpose = MicroDexed[instance_id]->data[DEXED_VOICE_OFFSET + DEXED_TRANSPOSE];
return (ret);
} }
#ifdef DEBUG
else
Serial.println(F("E : Cannot load voice data"));
#endif
} }
#ifdef DEBUG
if (found == false)
Serial.println(F("E : File not found."));
#endif
return (false); return (false);
} }
bool get_sysex_voice(File sysex, uint8_t voice_number, uint8_t* data) bool get_sd_voiceconfig(File sysex, uint8_t* conf)
{ {
uint16_t n; uint16_t n;
int32_t bulk_checksum_calc = 0; int32_t bulk_checksum_calc = 0;
int8_t bulk_checksum; int8_t bulk_checksum;
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 if (sysex.read() != 0xf0) // check sysex start-byte
{ {
#ifdef DEBUG #ifdef DEBUG
@ -317,41 +231,38 @@ bool get_sysex_voice(File sysex, uint8_t voice_number, uint8_t* data)
#endif #endif
return (false); return (false);
} }
if (sysex.read() != 0x43) // check sysex vendor is Yamaha if (sysex.read() != 0x67) // check sysex vendor is unofficial SYSEX-ID for MicroDexed
{ {
#ifdef DEBUG #ifdef DEBUG
Serial.println(F("E : SysEx vendor not Yamaha.")); Serial.println(F("E : SysEx vendor not unofficial SYSEX-ID for MicroDexed."));
#endif #endif
return (false); return (false);
} }
sysex.seek(4103); if (sysex.read() != 0x42) // check for sysex type (66 = MicroDexed voice setup)
if (sysex.read() != 0xf7) // check sysex end-byte
{ {
#ifdef DEBUG #ifdef DEBUG
Serial.println(F("E : SysEx end byte not found.")); Serial.println(F("E : SysEx type not MicroDexed setup."));
#endif #endif
return (false); return (false);
} }
sysex.seek(3); sysex.seek(sysex.size());
if (sysex.read() != 0x09) // check for sysex type (0x09=32 voices) if (sysex.read() != 0xf7) // check sysex end-byte
{ {
#ifdef DEBUG #ifdef DEBUG
Serial.println(F("E : SysEx type not 32 voices.")); Serial.println(F("E : SysEx end byte not found."));
#endif #endif
return (false); return (false);
} }
sysex.seek(4102); // Bulk checksum
sysex.seek(sysex.size() - 1); // Bulk checksum
bulk_checksum = sysex.read(); bulk_checksum = sysex.read();
sysex.seek(6); // start of bulk data sysex.seek(3); // start of bulk data
for (n = 0; n < 4096; n++) for (n = 0; n < sysex.size() - 3; n++)
{ {
uint8_t d = sysex.read(); 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 -= d;
*conf++ = d;
} }
bulk_checksum_calc &= 0x7f; bulk_checksum_calc &= 0x7f;
@ -373,41 +284,154 @@ bool get_sysex_voice(File sysex, uint8_t voice_number, uint8_t* data)
#endif #endif
return (false); return (false);
} }
#ifdef DEBUG
MicroDexed[0]->render_time_max = 0; Serial.println(F("Voice config loaded."));
#endif
configuration.checksum = crc32((uint8_t*)&configuration + 4, sizeof(configuration) - 4);
return (true); return (true);
} }
bool write_sysex_config(File sysex, config_t configuration) bool save_sd_voiceconfig(uint8_t b, uint8_t v, uint8_t instance_id)
{ {
uint16_t n; v = constrain(v, 0, MAX_VOICES - 1);
char* c = (char*)&configuration; b = constrain(b, 0, MAX_BANKS - 1);
if (sd_card > 0)
{
File sysex;
char filename[FILENAME_LEN];
sprintf(filename, "/%d/%s%d.syx", b, VOICE_CONFIG_NAME, v);
#ifdef DEBUG
Serial.print(F("Saving config "));
Serial.print(b);
Serial.print(F("/"));
Serial.print(v);
Serial.print(F("["));
Serial.print(instance_id);
Serial.print(F("]"));
Serial.print(F(" to "));
Serial.println(filename);
#endif
sysex = SD.open(filename, FILE_WRITE);
if (!sysex)
{
#ifdef DEBUG
Serial.print(F("E : Cannot open "));
Serial.print(filename);
Serial.println(F(" on SD."));
#endif
return (false);
}
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 save voice data"));
#endif
}
return (false);
}
bool write_sd_voiceconfig(File sysex, uint8_t* data, uint16_t len)
{
// write sysex start // write sysex start
sysex.write(0xf0); sysex.write(0xf0);
// write sysex vendor is unofficial SYSEX-ID for MicroDexed // write sysex vendor is unofficial SYSEX-ID for MicroDexed
sysex.write(0x67); sysex.write(0x67);
// write sysex format number (f=66..69; 1..4 MicroDexed setup(s)) // write sysex format number (f=66 MicroDexed voice config)
sysex.write(0x66); sysex.write(0x42);
// write data
// write configuration data sysex.write(data, len - 2); // minus 2 because at the end of the struct are voice and ban number - not very interesting here...
for (n = 0; n < sizeof(configuration); n++) // write checksum
sysex.write(c + n); sysex.write(calc_checksum(data, len - 2));
// write sysex end // write sysex end
sysex.write(0xf7); sysex.write(0xf7);
return (true); return (true);
} }
bool read_sysex_config(File sysex, config_t configuration) /******************************************************************************
HELPER FUNCTIONS
******************************************************************************/
uint8_t calc_checksum(uint8_t* data, uint16_t len)
{ {
uint16_t n; 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; int32_t bulk_checksum_calc = 0;
int8_t bulk_checksum; int8_t bulk_checksum;
if (sysex.size() != 47 || sysex.size() != 78) // check sysex size 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 #ifdef DEBUG
Serial.println(F("E : SysEx file size wrong.")); Serial.println(F("E : SysEx file size wrong."));
@ -421,14 +445,14 @@ bool read_sysex_config(File sysex, config_t configuration)
#endif #endif
return (false); return (false);
} }
if (sysex.read() != 0x67) // check sysex vendor is unofficial SYSEX-ID for MicroDexed if (sysex.read() != 0x43) // check sysex vendor is Yamaha
{ {
#ifdef DEBUG #ifdef DEBUG
Serial.println(F("E : SysEx vendor not unofficial SYSEX-ID for MicroDexed.")); Serial.println(F("E : SysEx vendor not Yamaha."));
#endif #endif
return (false); return (false);
} }
sysex.seek(sysex.size()); sysex.seek(4103);
if (sysex.read() != 0xf7) // check sysex end-byte if (sysex.read() != 0xf7) // check sysex end-byte
{ {
#ifdef DEBUG #ifdef DEBUG
@ -436,27 +460,29 @@ bool read_sysex_config(File sysex, config_t configuration)
#endif #endif
return (false); return (false);
} }
sysex.seek(3); sysex.seek(3);
uint8_t dexed_sysex_setup_type = sysex.read(); if (sysex.read() != 0x09) // check for sysex type (0x09=32 voices)
if (dexed_sysex_setup_type >= 0x42 && dexed_sysex_setup_type < 0x45) // check for sysex type (0x75=MicroDexed setup for one to four instances)
{ {
#ifdef DEBUG #ifdef DEBUG
Serial.println(F("E : SysEx type not MicroDexed setup.")); Serial.println(F("E : SysEx type not 32 voices."));
#endif #endif
return (false); return (false);
} }
sysex.seek(4102); // Bulk checksum
sysex.seek(4);
uint8_t sysex_config_size = sysex.read();
sysex.seek(sysex.size() - 1); // Bulk checksum
bulk_checksum = sysex.read(); bulk_checksum = sysex.read();
sysex.seek(6); // start of bulk data sysex.seek(6); // start of bulk data
for (n = 0; n < sysex.size() - 2; n++)
for (uint16_t n = 0; n < 4096; n++)
{ {
uint8_t d = sysex.read(); 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 -= d;
} }
bulk_checksum_calc &= 0x7f; bulk_checksum_calc &= 0x7f;
@ -479,70 +505,54 @@ bool read_sysex_config(File sysex, config_t configuration)
#endif #endif
return (false); 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);
#endif
*(conf + n) = d;
} }
configuration.checksum = crc32((uint8_t*)&configuration + 4, sizeof(configuration) - 4);
return (true); return (false);
} }
bool save_sysex_config(uint8_t b, uint8_t v, uint8_t instance_id) uint8_t get_bank_names(uint8_t instance_id)
{ {
v = constrain(v, 0, MAX_VOICES - 1); File root;
b = constrain(b, 0, MAX_BANKS - 1); 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) if (sd_card > 0)
{ {
File sysex; char bankdir[4];
char sysex_config_filename[FILENAME_LEN];
sprintf(sysex_config_filename, "/%d/config%d.syx", b, v); do
{
// init and set name for actual bank directory
sprintf(bankdir, "/%d", bank_counter);
#ifdef DEBUG // try to open directory
Serial.print(F("saving config ")); root = SD.open(bankdir);
Serial.print(b); if (!root)
Serial.print(F("/")); break;
Serial.print(v);
Serial.print(F("["));
Serial.print(instance_id);
Serial.print(F("]"));
Serial.print(F(" to "));
Serial.println(sysex_config_filename);
#endif
sysex = SD.open(sysex_config_filename, FILE_WRITE); // read filenames
if (!sysex) File entry = root.openNextFile();
if (!entry.isDirectory())
{ {
#ifdef DEBUG while ((strncmp(entry.name(), VOICE_CONFIG_NAME, sizeof(VOICE_CONFIG_NAME) - 1) == 0))
Serial.print(F("E : Cannot open "));
Serial.print(sysex_config_filename);
Serial.println(F(" on SD."));
#endif
return (false);
}
if (write_sysex_config(sysex, configuration))
{ {
sysex.close(); entry = root.openNextFile();
return (true);
} }
strcpy(bank_names[instance_id][bank_counter], entry.name());
#ifdef DEBUG #ifdef DEBUG
else Serial.print(F("Found bank ["));
Serial.println(F("E : Cannot load setup data")); Serial.print(bank_names[instance_id][bank_counter]);
Serial.println(F("]"));
#endif #endif
bank_counter++;
} }
} while (root);
return (false); return (bank_counter);
}
else
return (0);
} }

@ -32,7 +32,8 @@
extern uint8_t sd_card; extern uint8_t sd_card;
extern Dexed* dexed; extern Dexed* dexed;
extern AudioSourceMicroDexed * MicroDexed[NUM_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 bank;
extern uint8_t voice; extern uint8_t voice;
extern char bank_name[NUM_DEXED][BANK_NAME_LEN]; extern char bank_name[NUM_DEXED][BANK_NAME_LEN];
@ -44,16 +45,16 @@ extern uint8_t ui_main_state;
extern config_t configuration; extern config_t configuration;
extern uint32_t crc32(byte * calc_start, uint16_t calc_bytes); 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); void strip_extension(char* s, char *target);
bool get_voice_names_from_bank(uint8_t b, uint8_t i); bool get_voice_names_from_bank(uint8_t b, uint8_t i);
uint8_t get_bank_names(uint8_t instance_id); 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 #endif

@ -6,53 +6,54 @@ SYSEX Message Controller-Set
11110000 F0 Status byte - start sysex 11110000 F0 Status byte - start sysex
0iiiiiii 67 ID # (i=103; unofficial SYSEX-ID for MicroDexed) 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 1 setup config
| | | | | |
0ddddddd ** data byte 13 0ddddddd ** data byte 35
0ddddddd ** data byte 14 instance config 0 0eeeeeee ** checksum (masked 2's complement of sum of 155 bytes)
11110111 F7 Status - end sysex
| | |
0ddddddd ** data byte 44
Data Structure: MicroDexed Setup Dump Data Structure: MicroDexed Setup Dump
------------------------------------- -------------------------------------
Number Parameter Value Range Number Parameter Value Range
--------- --------- ----------- --------- --------- -----------
11 DEXED 1 MIDI-CHANNEL 0-16 (0=OMNI) 01 DEXED LOWEST NOTE 21-108
12 DEXED 1 BANK 0-99 02 DEXED HIGHEST NOTE 21-108
13 DEXED 1 VOICE 0-31 03 DEXED REVERB SEND 0-100
14 DEXED 1 LOWEST NOTE 21-108 04 DEXED CHORUS SEND 0-100
15 DEXED 1 HIGHEST NOTE 21-108 05 DEXED DELAY SEND 0-100
16 DEXED 1 REVERB SEND 0-100 06 DEXED FILTER-CUTOFF 0-100
17 DEXED 1 CHORUS SEND 0-100 07 DEXED FILTER-RESONANCE 0-100
18 DEXED 1 DELAY SEND 0-100 08 DEXED TRANSPOSE 0-48
19 DEXED 2 FILTER-CUTOFF 0-100 09 DEXED TUNE 0-100
20 DEXED 2 FILTER-RESONANCE 0-100 10 DEXED SOUND INTENSITY 0-100
21 DEXED 2 TRANSPOSE 0-48 11 DEXED PANORAMA 0-40 (20 is middle)
22 DEXED 2 TUNE 0-100 12 DEXED POLYPHONY 0-32 (depends on CPU and FX)
23 DEXED 2 SOUND INTENSITY 0-100 13 DEXED VELOCITY LEVEL 100-127
24 DEXED 2 PANORAMA 0-40 (20 is middle) 14 DEXED ENGINE 0-2 1=Modern, 2=Mark1, 2=OPL
25 DEXED 1 POLYPHONY 0-32 (depends on CPU and FX) 15 DEXED MONO/POLY MODE CHANGE 0-1 O=POLY
26 DEXED 1 ENGINE 0-2 (1=Modern, 2=Mark1, 2=OPL) 16 DEXED NOTE REFRESH MODE 0-2
27 DEXED 1 MONO/POLY MODE CHANGE 0-1 O=POLY 17 DEXED PITCH BEND RANGE 0-12
28 DEXED 1 PITCH BEND RANGE 0-12 18 DEXED " " STEP 0-12
29 DEXED 1 " " STEP 0-12 19 DEXED MOD WHEEL RANGE 0-99
30 DEXED 1 MOD WHEEL RANGE 0-99 20 DEXED " " ASSIGN 0-7 b0: pitch, b1:amp, b2: EG bias
31 DEXED 1 " " ASSIGN 0-7 b0: pitch, b1:amp, b2: EG bias 21 DEXED " " MODE 0-2 1=linear, 2=reverse, 3=direct
32 DEXED 1 FOOT CONTROL RANGE 0-99 22 DEXED FOOT CONTROL RANGE 0-99
33 DEXED 1 " " ASSIGN 0-7 " 23 DEXED " " ASSIGN 0-7 "
34 DEXED 1 BREATH CONT RANGE 0-99 24 DEXED " " MODE 0-2 1=linear, 2=reverse, 3=direct
35 DEXED 1 " " ASSIGN 0-7 " 25 DEXED BREATH CONT RANGE 0-99
36 DEXED 1 AFTERTOUCH RANGE 0-99 26 DEXED " " ASSIGN 0-7 "
37 DEXED 1 " ASSIGN 0-7 " 27 DEXED " " MODE 0-2 1=linear, 2=reverse, 3=direct
38 DEXED 1 PORTAMENTO MODE 0-1 0=RETAIN 1=FOLLOW 28 DEXED AFTERTOUCH RANGE 0-99
39 DEXED 1 " GLISS 0-1 29 DEXED " ASSIGN 0-7 "
40 DEXED 1 " TIME 0-99 30 DEXED " MODE 0-2 1=linear, 2=reverse, 3=direct
41 DEXED 1 OP_ENABLE 0-31 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 Number Parameter Value Range

Loading…
Cancel
Save