|
|
@ -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); |
|
|
|
} |
|
|
|
} |