/*
MicroDexed
MicroDexed is a port of the Dexed sound engine
( https : //github.com/asb2m10/dexed) for the Teensy-3.5/3.6/4.x with audio shield.
Dexed ist heavily based on https : //github.com/google/music-synthesizer-for-android
( c ) 2018 - 2021 H . Wirtz < wirtz @ parasitstudio . de >
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software Foundation ,
Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
*/
# include <Arduino.h>
# include "config.h"
# include <Wire.h>
# include <SD.h>
# include <ArduinoJson.h>
# include <TeensyTimerTool.h>
using namespace TeensyTimerTool ;
# include "dexed_sd.h"
# include "synth_dexed.h"
# if NUM_DRUMS > 0
# include "drums.h"
extern set_drums_volume ( float vol ) ;
extern drum_config_t drum_config [ NUM_DRUMSET_CONFIG ] ;
# endif
extern void init_MIDI_send_CC ( void ) ;
extern void check_configuration_dexed ( uint8_t instance_id ) ;
extern void check_configuration_performance ( void ) ;
extern void check_configuration_fx ( void ) ;
extern void sequencer ( ) ;
extern float drums_volume ;
//extern StaticJsonDocument<JSON_BUFFER> data_json;
extern uint8_t seq_chain_lenght ;
extern uint8_t seq_data [ NUM_SEQ_PATTERN ] [ 16 ] ;
extern uint8_t seq_vel [ NUM_SEQ_PATTERN ] [ 16 ] ;
extern uint8_t seq_patternchain [ 4 ] [ NUM_SEQ_TRACKS ] ;
extern uint8_t seq_content_type [ NUM_SEQ_PATTERN ] ;
extern uint8_t seq_track_type [ NUM_SEQ_TRACKS ] ;
extern uint8_t seq_chord_key_ammount ;
extern uint8_t seq_element_shift ;
extern int seq_oct_shift ;
extern int seq_transpose ;
extern int seq_tempo_ms ;
extern int seq_bpm ;
extern bool arp_play_basenote ;
extern bool seq_running ;
extern uint8_t arp_speed ;
extern uint8_t arp_lenght ;
extern uint8_t arp_style ;
extern uint8_t seq_chord_velocity ;
extern uint8_t seq_chord_dexed_inst ;
extern uint8_t seq_inst_dexed [ NUM_SEQ_TRACKS ] ;
extern char seq_name [ FILENAME_LEN ] ;
extern char seq_name_temp [ FILENAME_LEN ] ;
extern PeriodicTimer timer1 ;
extern float midi_volume_transform ( uint8_t midi_amp ) ;
extern void set_sample_pitch ( uint8_t sample , float playbackspeed ) ;
extern void set_sample_p_offset ( uint8_t sample , float s_offset ) ;
extern void set_sample_pan ( uint8_t sample , float s_pan ) ;
extern void set_sample_vol_max ( uint8_t sample , float s_max ) ;
extern void set_sample_vol_min ( uint8_t sample , float s_min ) ;
extern void set_sample_reverb_send ( uint8_t sample , float s_reverb ) ;
extern float get_sample_pitch ( uint8_t sample ) ;
extern float get_sample_p_offset ( uint8_t sample ) ;
extern float get_sample_pan ( uint8_t sample ) ;
extern float get_sample_vol_max ( uint8_t sample ) ;
extern float get_sample_vol_min ( uint8_t sample ) ;
extern float get_sample_reverb_send ( uint8_t sample ) ;
/******************************************************************************
SD BANK / VOICE LOADING
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool load_sd_voice ( uint8_t b , uint8_t v , uint8_t instance_id )
{
v = constrain ( v , 0 , MAX_VOICES - 1 ) ;
b = constrain ( b , 0 , MAX_BANKS - 1 ) ;
if ( sd_card > 0 )
{
File sysex ;
char filename [ FILENAME_LEN ] ;
char bank_name [ BANK_NAME_LEN ] ;
uint8_t data [ 128 ] ;
get_bank_name ( b , bank_name , sizeof ( bank_name ) ) ;
sprintf ( filename , " /%d/%s.syx " , b , bank_name ) ;
AudioNoInterrupts ( ) ;
sysex = SD . open ( filename ) ;
AudioInterrupts ( ) ;
if ( ! sysex )
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
return ( false ) ;
}
if ( get_sd_voice ( sysex , v , data ) )
{
# ifdef DEBUG
char voice_name [ VOICE_NAME_LEN ] ;
get_voice_name ( b , v , voice_name , sizeof ( voice_name ) ) ;
Serial . print ( F ( " Loading voice from " ) ) ;
Serial . print ( filename ) ;
Serial . print ( F ( " [ " ) ) ;
Serial . print ( voice_name ) ;
Serial . println ( F ( " ] " ) ) ;
# endif
uint8_t tmp_data [ 156 ] ;
bool ret = MicroDexed [ instance_id ] - > decodeVoice ( tmp_data , data ) ;
MicroDexed [ instance_id ] - > loadVoiceParameters ( tmp_data ) ;
# ifdef DEBUG
show_patch ( instance_id ) ;
# endif
configuration . dexed [ instance_id ] . transpose = MicroDexed [ instance_id ] - > getTranspose ( ) ;
AudioNoInterrupts ( ) ;
sysex . close ( ) ;
AudioInterrupts ( ) ;
uint8_t data_copy [ 155 ] ;
MicroDexed [ instance_id ] - > getVoiceData ( data_copy ) ;
send_sysex_voice ( configuration . dexed [ instance_id ] . midi_channel , data_copy ) ;
init_MIDI_send_CC ( ) ;
return ( ret ) ;
}
# ifdef DEBUG
else
Serial . println ( F ( " E : Cannot load voice data " ) ) ;
# endif
AudioNoInterrupts ( ) ;
sysex . close ( ) ;
AudioInterrupts ( ) ;
}
return ( false ) ;
}
bool save_sd_voice ( uint8_t b , uint8_t v , uint8_t instance_id )
{
v = constrain ( v , 0 , MAX_VOICES - 1 ) ;
b = constrain ( b , 0 , MAX_BANKS - 1 ) ;
if ( sd_card > 0 )
{
File sysex ;
char filename [ FILENAME_LEN ] ;
char bank_name [ BANK_NAME_LEN ] ;
uint8_t data [ 128 ] ;
get_bank_name ( b , bank_name , sizeof ( bank_name ) ) ;
sprintf ( filename , " /%d/%s.syx " , b , bank_name ) ;
AudioNoInterrupts ( ) ;
sysex = SD . open ( filename , FILE_WRITE ) ;
AudioInterrupts ( ) ;
if ( ! sysex )
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
return ( false ) ;
}
MicroDexed [ instance_id ] - > encodeVoice ( data ) ;
if ( put_sd_voice ( sysex , v , data ) )
{
# ifdef DEBUG
char voice_name [ VOICE_NAME_LEN ] ;
MicroDexed [ instance_id ] - > getName ( voice_name ) ;
Serial . print ( F ( " Saving voice to " ) ) ;
Serial . print ( filename ) ;
Serial . print ( F ( " [ " ) ) ;
Serial . print ( voice_name ) ;
Serial . println ( F ( " ] " ) ) ;
# endif
AudioNoInterrupts ( ) ;
sysex . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
# ifdef DEBUG
else
Serial . println ( F ( " E : Cannot load voice data " ) ) ;
# endif
AudioNoInterrupts ( ) ;
sysex . close ( ) ;
AudioInterrupts ( ) ;
}
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 ;
AudioNoInterrupts ( ) ;
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 ( n = 0 ; n < 4096 ; n + + )
{
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 ;
AudioInterrupts ( ) ;
# 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 ) ;
}
MicroDexed [ 0 ] - > resetRenderTimeMax ( ) ;
return ( true ) ;
}
bool put_sd_voice ( File sysex , uint8_t voice_number , uint8_t * data )
{
uint16_t n ;
int32_t bulk_checksum_calc = 0 ;
AudioNoInterrupts ( ) ;
sysex . seek ( 0 ) ;
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 ( 6 + ( voice_number * 128 ) ) ;
sysex . write ( data , 128 ) ;
// checksum calculation
sysex . seek ( 6 ) ; // start of bulk data
for ( n = 0 ; n < 4096 ; n + + )
{
uint8_t d = sysex . read ( ) ;
bulk_checksum_calc - = d ;
}
sysex . seek ( 4102 ) ; // Bulk checksum
sysex . write ( bulk_checksum_calc & 0x7f ) ;
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . print ( F ( " Bulk checksum : 0x " ) ) ;
Serial . println ( bulk_checksum_calc & 0x7f , HEX ) ;
# endif
return ( true ) ;
}
bool save_sd_bank ( const char * bank_filename , uint8_t * data )
{
char tmp [ FILENAME_LEN ] ;
char tmp2 [ FILENAME_LEN ] ;
int bank_number ;
File root , entry ;
if ( sd_card > 0 )
{
# ifdef DEBUG
Serial . print ( F ( " Trying so store " ) ) ;
Serial . print ( bank_filename ) ;
Serial . println ( F ( " . " ) ) ;
# endif
// first remove old bank
sscanf ( bank_filename , " /%d/%s " , & bank_number , tmp ) ;
sprintf ( tmp , " /%d " , bank_number ) ;
AudioNoInterrupts ( ) ;
root = SD . open ( tmp ) ;
while ( 42 = = 42 )
{
entry = root . openNextFile ( ) ;
if ( entry )
{
if ( ! entry . isDirectory ( ) )
{
# ifdef DEBUG
Serial . print ( F ( " Removing " ) ) ;
Serial . print ( tmp ) ;
Serial . print ( F ( " / " ) ) ;
Serial . println ( entry . name ( ) ) ;
# endif
sprintf ( tmp2 , " %s/%s " , tmp , entry . name ( ) ) ;
entry . close ( ) ;
# ifndef DEBUG
SD . remove ( tmp2 ) ;
# else
bool r = SD . remove ( tmp2 ) ;
if ( r = = false )
{
Serial . print ( F ( " E: cannot remove " ) ) ;
Serial . print ( tmp2 ) ;
Serial . println ( F ( " . " ) ) ;
}
# endif
break ;
}
}
else
{
break ;
}
}
root . close ( ) ;
// store new bank at /<b>/<bank_name>.syx
# ifdef DEBUG
Serial . print ( F ( " Storing bank as " ) ) ;
Serial . print ( bank_filename ) ;
Serial . print ( F ( " ... " ) ) ;
# endif
root = SD . open ( bank_filename , FILE_WRITE ) ;
root . write ( data , 4104 ) ;
root . close ( ) ;
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . println ( F ( " done. " ) ) ;
# endif
}
else
return ( false ) ;
return ( true ) ;
}
/******************************************************************************
SD DRUMSETTINGS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool load_sd_drumsettings_json ( uint8_t number , uint8_t target )
{
if ( number < 0 )
return ( false ) ;
number = constrain ( number , 0 , 99 ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
char filename [ FILENAME_LEN ] ;
if ( target = = 0 )
sprintf ( filename , " /%s/%s%d.json " , DRUM_CONFIG_PATH , DRUM_CONFIG_NAME , number ) ;
else
sprintf ( filename , " /%s/%d-d.json " , SEQ_CONFIG_PATH , number ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
# ifdef DEBUG
Serial . print ( F ( " Found drums configuration [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ]... loading... " ) ) ;
# endif
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . println ( F ( " Read JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
drums_volume = data_json [ " drums_volume " ] ;
set_drums_volume ( drums_volume ) ;
for ( uint8_t i = 0 ; i < NUM_DRUMSET_CONFIG ; i + + )
{
set_sample_pitch ( i , data_json [ " pitch " ] [ i ] ) ;
set_sample_p_offset ( i , data_json [ " p_offset " ] [ i ] ) ;
set_sample_pan ( i , data_json [ " pan " ] [ i ] ) ;
set_sample_vol_max ( i , data_json [ " vol_max " ] [ i ] ) ;
set_sample_vol_min ( i , data_json [ " vol_min " ] [ i ] ) ;
set_sample_reverb_send ( i , data_json [ " reverb_send " ] [ i ] ) ;
}
return ( true ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
}
}
else
{
Serial . print ( F ( " No " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " available. " ) ) ;
# endif
}
}
return ( false ) ;
}
bool save_sd_drumsettings_json ( uint8_t number , uint8_t target )
{
char filename [ FILENAME_LEN ] ;
number = constrain ( number , 0 , 99 ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
if ( target = = 0 )
sprintf ( filename , " /%s/%s%d.json " , DRUM_CONFIG_PATH , DRUM_CONFIG_NAME , number ) ;
else
sprintf ( filename , " /%s/%d-d.json " , SEQ_CONFIG_PATH , number ) ;
# ifdef DEBUG
Serial . print ( F ( " Saving drums config " ) ) ;
Serial . print ( number ) ;
Serial . print ( F ( " to " ) ) ;
Serial . println ( filename ) ;
# endif
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) ) {
Serial . println ( " remove old drumsettings file " ) ;
SD . begin ( ) ;
SD . remove ( filename ) ;
}
json = SD . open ( filename , FILE_WRITE ) ;
if ( json )
{
data_json [ " drums_volume " ] = drums_volume ;
for ( uint8_t i = 0 ; i < NUM_DRUMSET_CONFIG ; i + + )
{
data_json [ " pitch " ] [ i ] = get_sample_pitch ( i ) ;
data_json [ " p_offset " ] [ i ] = get_sample_p_offset ( i ) ;
data_json [ " pan " ] [ i ] = get_sample_pan ( i ) ;
data_json [ " vol_max " ] [ i ] = get_sample_vol_max ( i ) ;
data_json [ " vol_min " ] [ i ] = get_sample_vol_min ( i ) ;
data_json [ " reverb_send " ] [ i ] = get_sample_reverb_send ( i ) ;
}
# ifdef DEBUG
Serial . println ( F ( " Write JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
serializeJsonPretty ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
json . close ( ) ;
AudioInterrupts ( ) ;
}
else
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
}
return ( false ) ;
}
/******************************************************************************
SD VOICECONFIG
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool load_sd_voiceconfig_json ( uint8_t vc , uint8_t instance_id , uint8_t target )
{
char filename [ FILENAME_LEN ] ;
vc = constrain ( vc , 0 , MAX_VOICECONFIG ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
if ( target = = 0 )
sprintf ( filename , " /%s/%s%d.json " , VOICE_CONFIG_PATH , VOICE_CONFIG_NAME , vc ) ;
else
sprintf ( filename , " /%s/%d-v%d.json " , SEQ_CONFIG_PATH , vc , instance_id ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
# ifdef DEBUG
Serial . print ( F ( " Found voice configuration [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ]... loading... " ) ) ;
# endif
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
check_configuration_dexed ( instance_id ) ;
# ifdef DEBUG
Serial . println ( F ( " Read JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
configuration . dexed [ instance_id ] . lowest_note = data_json [ " lowest_note " ] ;
configuration . dexed [ instance_id ] . highest_note = data_json [ " highest_note " ] ;
configuration . dexed [ instance_id ] . transpose = data_json [ " transpose " ] ;
configuration . dexed [ instance_id ] . tune = data_json [ " tune " ] ;
configuration . dexed [ instance_id ] . sound_intensity = data_json [ " sound_intensity " ] ;
configuration . dexed [ instance_id ] . pan = data_json [ " pan " ] ;
configuration . dexed [ instance_id ] . polyphony = data_json [ " polyphony " ] ;
configuration . dexed [ instance_id ] . velocity_level = data_json [ " velocity_level " ] ;
configuration . dexed [ instance_id ] . monopoly = data_json [ " monopoly " ] ;
configuration . dexed [ instance_id ] . note_refresh = data_json [ " note_refresh " ] ;
configuration . dexed [ instance_id ] . pb_range = data_json [ " pb_range " ] ;
configuration . dexed [ instance_id ] . pb_step = data_json [ " pb_step " ] ;
configuration . dexed [ instance_id ] . mw_range = data_json [ " mw_range " ] ;
configuration . dexed [ instance_id ] . mw_assign = data_json [ " mw_assign " ] ;
configuration . dexed [ instance_id ] . mw_mode = data_json [ " mw_mode " ] ;
configuration . dexed [ instance_id ] . fc_range = data_json [ " fc_range " ] ;
configuration . dexed [ instance_id ] . fc_assign = data_json [ " fc_assign " ] ;
configuration . dexed [ instance_id ] . fc_mode = data_json [ " fc_mode " ] ;
configuration . dexed [ instance_id ] . bc_range = data_json [ " bc_range " ] ;
configuration . dexed [ instance_id ] . bc_assign = data_json [ " bc_assign " ] ;
configuration . dexed [ instance_id ] . bc_mode = data_json [ " bc_mode " ] ;
configuration . dexed [ instance_id ] . at_range = data_json [ " at_range " ] ;
configuration . dexed [ instance_id ] . at_assign = data_json [ " at_assign " ] ;
configuration . dexed [ instance_id ] . at_mode = data_json [ " at_mode " ] ;
configuration . dexed [ instance_id ] . portamento_mode = data_json [ " portamento_mode " ] ;
configuration . dexed [ instance_id ] . portamento_glissando = data_json [ " portamento_glissando " ] ;
configuration . dexed [ instance_id ] . portamento_time = data_json [ " portamento_time " ] ;
configuration . dexed [ instance_id ] . op_enabled = data_json [ " op_enabled " ] ;
configuration . dexed [ instance_id ] . midi_channel = data_json [ " midi_channel " ] ;
set_voiceconfig_params ( instance_id ) ;
return ( true ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
}
}
else
{
Serial . print ( F ( " No " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " available. " ) ) ;
# endif
}
}
AudioInterrupts ( ) ;
return ( false ) ;
}
bool save_sd_voiceconfig_json ( uint8_t vc , uint8_t instance_id , uint8_t target )
{
char filename [ FILENAME_LEN ] ;
vc = constrain ( vc , 0 , MAX_VOICECONFIG ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
if ( target = = 0 )
sprintf ( filename , " /%s/%s%d.json " , VOICE_CONFIG_PATH , VOICE_CONFIG_NAME , vc ) ;
else
sprintf ( filename , " /%s/%d-v%d.json " , SEQ_CONFIG_PATH , vc , instance_id ) ;
# ifdef DEBUG
Serial . print ( F ( " Saving voice config " ) ) ;
Serial . print ( vc ) ;
Serial . print ( F ( " [ " ) ) ;
Serial . print ( instance_id ) ;
Serial . print ( F ( " ] " ) ) ;
Serial . print ( F ( " to " ) ) ;
Serial . println ( filename ) ;
# endif
AudioNoInterrupts ( ) ;
SD . begin ( ) ;
SD . remove ( filename ) ;
json = SD . open ( filename , FILE_WRITE ) ;
if ( json )
{
data_json [ " lowest_note " ] = configuration . dexed [ instance_id ] . lowest_note ;
data_json [ " highest_note " ] = configuration . dexed [ instance_id ] . highest_note ;
data_json [ " transpose " ] = configuration . dexed [ instance_id ] . transpose ;
data_json [ " tune " ] = configuration . dexed [ instance_id ] . tune ;
data_json [ " sound_intensity " ] = configuration . dexed [ instance_id ] . sound_intensity ;
data_json [ " pan " ] = configuration . dexed [ instance_id ] . pan ;
data_json [ " polyphony " ] = configuration . dexed [ instance_id ] . polyphony ;
data_json [ " velocity_level " ] = configuration . dexed [ instance_id ] . velocity_level ;
data_json [ " monopoly " ] = configuration . dexed [ instance_id ] . monopoly ;
data_json [ " monopoly " ] = configuration . dexed [ instance_id ] . monopoly ;
data_json [ " note_refresh " ] = configuration . dexed [ instance_id ] . note_refresh ;
data_json [ " pb_range " ] = configuration . dexed [ instance_id ] . pb_range ;
data_json [ " pb_step " ] = configuration . dexed [ instance_id ] . pb_step ;
data_json [ " mw_range " ] = configuration . dexed [ instance_id ] . mw_range ;
data_json [ " mw_assign " ] = configuration . dexed [ instance_id ] . mw_assign ;
data_json [ " mw_mode " ] = configuration . dexed [ instance_id ] . mw_mode ;
data_json [ " fc_range " ] = configuration . dexed [ instance_id ] . fc_range ;
data_json [ " fc_assign " ] = configuration . dexed [ instance_id ] . fc_assign ;
data_json [ " fc_mode " ] = configuration . dexed [ instance_id ] . fc_mode ;
data_json [ " bc_range " ] = configuration . dexed [ instance_id ] . bc_range ;
data_json [ " bc_assign " ] = configuration . dexed [ instance_id ] . bc_assign ;
data_json [ " bc_mode " ] = configuration . dexed [ instance_id ] . bc_mode ;
data_json [ " at_range " ] = configuration . dexed [ instance_id ] . at_range ;
data_json [ " at_assign " ] = configuration . dexed [ instance_id ] . at_assign ;
data_json [ " at_mode " ] = configuration . dexed [ instance_id ] . at_mode ;
data_json [ " portamento_mode " ] = configuration . dexed [ instance_id ] . portamento_mode ;
data_json [ " portamento_glissando " ] = configuration . dexed [ instance_id ] . portamento_glissando ;
data_json [ " portamento_time " ] = configuration . dexed [ instance_id ] . portamento_time ;
data_json [ " op_enabled " ] = configuration . dexed [ instance_id ] . op_enabled ;
data_json [ " midi_channel " ] = configuration . dexed [ instance_id ] . midi_channel ;
# ifdef DEBUG
Serial . println ( F ( " Write JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
serializeJsonPretty ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
json . close ( ) ;
}
else
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
}
AudioInterrupts ( ) ;
return ( false ) ;
}
/******************************************************************************
SD FX
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool load_sd_fx_json ( uint8_t fx , uint8_t target )
{
if ( fx < 0 )
return ( false ) ;
load_sd_drumsettings_json ( fx , target ) ;
fx = constrain ( fx , 0 , MAX_FX ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
char filename [ FILENAME_LEN ] ;
if ( target = = 0 )
sprintf ( filename , " /%s/%s%d.json " , FX_CONFIG_PATH , FX_CONFIG_NAME , fx ) ;
else
sprintf ( filename , " /%s/%d-fx.json " , SEQ_CONFIG_PATH , fx ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
# ifdef DEBUG
Serial . print ( F ( " Found fx configuration [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ]... loading... " ) ) ;
# endif
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
check_configuration_fx ( ) ;
# ifdef DEBUG
Serial . println ( F ( " Read JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
for ( uint8_t i = 0 ; i < MAX_DEXED ; i + + )
{
configuration . fx . filter_cutoff [ i ] = data_json [ " filter_cutoff " ] [ i ] ;
configuration . fx . filter_resonance [ i ] = data_json [ " filter_resonance " ] [ i ] ;
configuration . fx . chorus_frequency [ i ] = data_json [ " chorus_frequency " ] [ i ] ;
configuration . fx . chorus_waveform [ i ] = data_json [ " chorus_waveform " ] [ i ] ;
configuration . fx . chorus_depth [ i ] = data_json [ " chorus_depth " ] [ i ] ;
configuration . fx . chorus_level [ i ] = data_json [ " chorus_level " ] [ i ] ;
configuration . fx . delay_time [ i ] = data_json [ " delay_time " ] [ i ] ;
configuration . fx . delay_feedback [ i ] = data_json [ " delay_feedback " ] [ i ] ;
configuration . fx . delay_level [ i ] = data_json [ " delay_level " ] [ i ] ;
configuration . fx . delay_sync [ i ] = data_json [ " delay_sync " ] [ i ] ;
configuration . fx . reverb_send [ i ] = data_json [ " reverb_send " ] [ i ] ;
if ( configuration . fx . delay_sync [ i ] > 0 )
{
configuration . fx . delay_time [ i ] = 0 ;
}
}
configuration . fx . reverb_roomsize = data_json [ " reverb_roomsize " ] ;
configuration . fx . reverb_damping = data_json [ " reverb_damping " ] ;
configuration . fx . reverb_lowpass = data_json [ " reverb_lowpass " ] ;
configuration . fx . reverb_lodamp = data_json [ " reverb_lodamp " ] ;
configuration . fx . reverb_hidamp = data_json [ " reverb_hidamp " ] ;
configuration . fx . reverb_diffusion = data_json [ " reverb_diffusion " ] ;
configuration . fx . reverb_level = data_json [ " reverb_level " ] ;
configuration . fx . eq_1 = data_json [ " eq_1 " ] ;
configuration . fx . eq_2 = data_json [ " eq_2 " ] ;
configuration . fx . eq_3 = data_json [ " eq_3 " ] ;
configuration . fx . eq_4 = data_json [ " eq_4 " ] ;
configuration . fx . eq_5 = data_json [ " eq_5 " ] ;
configuration . fx . eq_6 = data_json [ " eq_6 " ] ;
configuration . fx . eq_7 = data_json [ " eq_7 " ] ;
set_fx_params ( ) ;
return ( true ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
}
}
else
{
Serial . print ( F ( " No " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " available. " ) ) ;
# endif
}
}
AudioInterrupts ( ) ;
return ( false ) ;
}
bool save_sd_fx_json ( uint8_t fx , uint8_t target )
{
char filename [ FILENAME_LEN ] ;
fx = constrain ( fx , 0 , MAX_FX ) ;
save_sd_drumsettings_json ( fx , target ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
if ( target = = 0 )
sprintf ( filename , " /%s/%s%d.json " , FX_CONFIG_PATH , FX_CONFIG_NAME , fx ) ;
else
sprintf ( filename , " /%s/%d-fx.json " , SEQ_CONFIG_PATH , fx ) ;
# ifdef DEBUG
Serial . print ( F ( " Saving fx config " ) ) ;
Serial . print ( fx ) ;
Serial . print ( F ( " to " ) ) ;
Serial . println ( filename ) ;
# endif
AudioNoInterrupts ( ) ;
SD . begin ( ) ;
SD . remove ( filename ) ;
json = SD . open ( filename , FILE_WRITE ) ;
if ( json )
{
for ( uint8_t i = 0 ; i < MAX_DEXED ; i + + )
{
data_json [ " filter_cutoff " ] [ i ] = configuration . fx . filter_cutoff [ i ] ;
data_json [ " filter_resonance " ] [ i ] = configuration . fx . filter_resonance [ i ] ;
data_json [ " chorus_frequency " ] [ i ] = configuration . fx . chorus_frequency [ i ] ;
data_json [ " chorus_waveform " ] [ i ] = configuration . fx . chorus_waveform [ i ] ;
data_json [ " chorus_depth " ] [ i ] = configuration . fx . chorus_depth [ i ] ;
data_json [ " chorus_level " ] [ i ] = configuration . fx . chorus_level [ i ] ;
data_json [ " delay_time " ] [ i ] = configuration . fx . delay_time [ i ] ;
data_json [ " delay_feedback " ] [ i ] = configuration . fx . delay_feedback [ i ] ;
data_json [ " delay_level " ] [ i ] = configuration . fx . delay_level [ i ] ;
data_json [ " delay_sync " ] [ i ] = configuration . fx . delay_sync [ i ] ;
data_json [ " reverb_send " ] [ i ] = configuration . fx . reverb_send [ i ] ;
}
data_json [ " reverb_roomsize " ] = configuration . fx . reverb_roomsize ;
data_json [ " reverb_damping " ] = configuration . fx . reverb_damping ;
data_json [ " reverb_lowpass " ] = configuration . fx . reverb_lowpass ;
data_json [ " reverb_lodamp " ] = configuration . fx . reverb_lodamp ;
data_json [ " reverb_hidamp " ] = configuration . fx . reverb_hidamp ;
data_json [ " reverb_diffusion " ] = configuration . fx . reverb_diffusion ;
data_json [ " reverb_level " ] = configuration . fx . reverb_level ;
data_json [ " eq_1 " ] = configuration . fx . eq_1 ;
data_json [ " eq_2 " ] = configuration . fx . eq_2 ;
data_json [ " eq_3 " ] = configuration . fx . eq_3 ;
data_json [ " eq_4 " ] = configuration . fx . eq_4 ;
data_json [ " eq_5 " ] = configuration . fx . eq_5 ;
data_json [ " eq_6 " ] = configuration . fx . eq_6 ;
data_json [ " eq_7 " ] = configuration . fx . eq_7 ;
# ifdef DEBUG
Serial . println ( F ( " Write JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
serializeJsonPretty ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
json . close ( ) ;
}
else
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
}
AudioInterrupts ( ) ;
return ( false ) ;
}
bool save_sd_seq_sub_vel_json ( uint8_t seq_number )
{
char filename [ FILENAME_LEN ] ;
int count = 0 ;
seq_number = constrain ( seq_number , 0 , 99 ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
sprintf ( filename , " /%s/%d-vel.json " , SEQ_CONFIG_PATH , seq_number ) ;
# ifdef DEBUG
Serial . print ( F ( " Saving sequencer velocity " ) ) ;
Serial . print ( seq_number ) ;
Serial . print ( F ( " to " ) ) ;
Serial . println ( filename ) ;
# endif
int total = sizeof ( seq_vel ) ;
int columns = sizeof ( seq_vel [ 0 ] ) ;
int rows = total / columns ;
AudioNoInterrupts ( ) ;
SD . begin ( ) ;
SD . remove ( filename ) ;
json = SD . open ( filename , FILE_WRITE ) ;
if ( json )
{
for ( uint8_t i = 0 ; i < rows ; i + + )
{
for ( uint8_t j = 0 ; j < columns ; j + + ) {
data_json [ " seq_velocity " ] [ count ] = seq_vel [ i ] [ j ] ;
count + + ;
}
}
# ifdef DEBUG
Serial . println ( F ( " Write JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
serializeJsonPretty ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
json . close ( ) ;
}
else
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
}
return ( false ) ;
}
bool save_sd_seq_sub_patterns_json ( uint8_t seq_number )
{
char filename [ FILENAME_LEN ] ;
int count = 0 ;
seq_number = constrain ( seq_number , 0 , 99 ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
sprintf ( filename , " /%s/%d-pat.json " , SEQ_CONFIG_PATH , seq_number ) ;
# ifdef DEBUG
Serial . print ( F ( " Saving sequencer patterns " ) ) ;
Serial . print ( seq_number ) ;
Serial . print ( F ( " to " ) ) ;
Serial . println ( filename ) ;
# endif
int total = sizeof ( seq_data ) ;
int columns = sizeof ( seq_data [ 0 ] ) ;
int rows = total / columns ;
AudioNoInterrupts ( ) ;
SD . begin ( ) ;
SD . remove ( filename ) ;
json = SD . open ( filename , FILE_WRITE ) ;
if ( json )
{
for ( uint8_t i = 0 ; i < rows ; i + + )
{
for ( uint8_t j = 0 ; j < columns ; j + + ) {
data_json [ " seq_data " ] [ count ] = seq_data [ i ] [ j ] ;
count + + ;
}
}
# ifdef DEBUG
Serial . println ( F ( " Write JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
serializeJsonPretty ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
json . close ( ) ;
}
else
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
}
return ( false ) ;
}
bool save_sd_seq_json ( uint8_t seq_number )
{
char filename [ FILENAME_LEN ] ;
int count = 0 ;
seq_number = constrain ( seq_number , 0 , 99 ) ;
save_sd_seq_sub_vel_json ( seq_number ) ;
save_sd_seq_sub_patterns_json ( seq_number ) ;
sprintf ( filename , " /%s/%d-fx.json " , SEQ_CONFIG_PATH , seq_number ) ;
# ifdef DEBUG
Serial . print ( F ( " write SEQ-FX-Config " ) ) ;
Serial . print ( seq_number ) ;
Serial . print ( F ( " " ) ) ;
# endif
save_sd_fx_json ( seq_number , 1 ) ;
for ( uint8_t i = 0 ; i < MAX_DEXED ; i + + )
{ sprintf ( filename , " /%s/%d-v%d.json " , SEQ_CONFIG_PATH , seq_number , i ) ;
# ifdef DEBUG
Serial . print ( F ( " Write Voice-Config for sequencer " ) ) ;
Serial . print ( filename ) ;
Serial . print ( F ( " " ) ) ;
# endif
save_sd_voiceconfig_json ( seq_number , i , 1 ) ;
}
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
sprintf ( filename , " /%s/%d-S.json " , SEQ_CONFIG_PATH , seq_number ) ;
# ifdef DEBUG
Serial . print ( F ( " Saving sequencer config " ) ) ;
Serial . print ( seq_number ) ;
Serial . print ( F ( " to " ) ) ;
Serial . println ( filename ) ;
# endif
int total = sizeof ( seq_patternchain ) ;
int columns = sizeof ( seq_patternchain [ 0 ] ) ;
int rows = total / columns ;
Serial . print ( F ( " " ) ) ;
AudioNoInterrupts ( ) ;
SD . begin ( ) ;
SD . remove ( filename ) ;
json = SD . open ( filename , FILE_WRITE ) ;
if ( json )
{
Serial . print ( F ( " Chain Rows: " ) ) ;
Serial . print ( rows ) ;
Serial . print ( " Chain Columns: " ) ;
Serial . print ( columns ) ;
Serial . print ( F ( " " ) ) ;
count = 0 ;
for ( uint8_t i = 0 ; i < rows ; i + + )
{
for ( uint8_t j = 0 ; j < columns ; j + + ) {
data_json [ " seq_patternchain " ] [ count ] = seq_patternchain [ i ] [ j ] ;
count + + ;
}
}
count = 0 ;
data_json [ " seq_tempo_ms " ] = seq_tempo_ms ;
data_json [ " seq_bpm " ] = seq_bpm ;
data_json [ " arp_play_basenote " ] = arp_play_basenote ;
data_json [ " arp_speed " ] = arp_speed ;
data_json [ " arp_lenght " ] = arp_lenght ;
data_json [ " arp_style " ] = arp_style ;
data_json [ " seq_chord_velocity " ] = seq_chord_velocity ;
data_json [ " seq_chord_dexed_inst " ] = seq_chord_dexed_inst ;
data_json [ " seq_chain_lenght " ] = seq_chain_lenght ;
data_json [ " seq_transpose " ] = seq_transpose ;
data_json [ " chord_key_ammount " ] = seq_chord_key_ammount ;
data_json [ " seq_oct_shift " ] = seq_oct_shift ;
data_json [ " seq_element_shift " ] = seq_element_shift ;
for ( uint8_t i = 0 ; i < MAX_DEXED ; i + + )
{
data_json [ " bank " ] [ i ] = configuration . performance . bank [ i ] ;
data_json [ " voice " ] [ i ] = configuration . performance . voice [ i ] ;
}
for ( uint8_t i = 0 ; i < sizeof ( seq_track_type ) ; i + + ) {
data_json [ " track_type " ] [ i ] = seq_track_type [ i ] ;
}
for ( uint8_t i = 0 ; i < sizeof ( seq_content_type ) ; i + + ) {
data_json [ " content_type " ] [ i ] = seq_content_type [ i ] ;
}
for ( uint8_t i = 0 ; i < sizeof ( seq_inst_dexed ) ; i + + ) {
data_json [ " seq_inst_dexed " ] [ i ] = seq_inst_dexed [ i ] ;
}
for ( uint8_t i = 0 ; i < FILENAME_LEN ; i + + ) {
data_json [ " seq_name " ] [ i ] = seq_name [ i ] ;
}
# ifdef DEBUG
Serial . println ( F ( " Write JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
serializeJsonPretty ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
json . close ( ) ;
}
else
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
}
return ( false ) ;
}
void get_sd_seq_name_json ( uint8_t seq_number )
{
seq_number = constrain ( seq_number , 0 , 99 ) ;
memset ( seq_name_temp , 0 , FILENAME_LEN ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
char filename [ FILENAME_LEN ] ;
sprintf ( filename , " /%s/%d-S.json " , SEQ_CONFIG_PATH , seq_number ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
}
if ( data_json [ " seq_name " ] [ 0 ] ! = 0 ) {
for ( uint8_t i = 0 ; i < FILENAME_LEN ; i + + ) {
seq_name_temp [ i ] = data_json [ " seq_name " ] [ i ] ;
}
}
}
}
}
bool load_sd_seq_sub_vel_json ( uint8_t seq_number )
{
if ( seq_number < 0 )
return ( false ) ;
seq_number = constrain ( seq_number , 0 , 99 ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
char filename [ FILENAME_LEN ] ;
sprintf ( filename , " /%s/%d-vel.json " , SEQ_CONFIG_PATH , seq_number ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
# ifdef DEBUG
Serial . print ( F ( " Found velocity data [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ]... loading... " ) ) ;
Serial . println ( F ( " " ) ) ;
# endif
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . println ( F ( " Read JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
int total = sizeof ( seq_vel ) ;
int columns = sizeof ( seq_vel [ 0 ] ) ;
int rows = total / columns ;
int count = 0 ;
for ( uint8_t i = 0 ; i < rows ; i + + )
{
for ( uint8_t j = 0 ; j < columns ; j + + ) {
seq_vel [ i ] [ j ] = data_json [ " seq_velocity " ] [ count ] ;
count + + ;
}
}
return ( true ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
}
}
else
{
Serial . print ( F ( " No " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " available. " ) ) ;
# endif
}
}
return ( false ) ;
}
bool load_sd_seq_sub_patterns_json ( uint8_t seq_number )
{
if ( seq_number < 0 )
return ( false ) ;
seq_number = constrain ( seq_number , 0 , 99 ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
char filename [ FILENAME_LEN ] ;
sprintf ( filename , " /%s/%d-pat.json " , SEQ_CONFIG_PATH , seq_number ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
# ifdef DEBUG
Serial . print ( F ( " Found pattern data [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ]... loading... " ) ) ;
Serial . println ( F ( " " ) ) ;
# endif
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . println ( F ( " Read JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
int total = sizeof ( seq_data ) ;
int columns = sizeof ( seq_data [ 0 ] ) ;
int rows = total / columns ;
int count = 0 ;
for ( uint8_t i = 0 ; i < rows ; i + + )
{
for ( uint8_t j = 0 ; j < columns ; j + + ) {
seq_data [ i ] [ j ] = data_json [ " seq_data " ] [ count ] ;
count + + ;
}
}
return ( true ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
}
}
else
{
Serial . print ( F ( " No " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " available. " ) ) ;
# endif
}
}
return ( false ) ;
}
bool load_sd_seq_json ( uint8_t seq_number )
{
if ( seq_number < 0 )
return ( false ) ;
seq_number = constrain ( seq_number , 0 , 99 ) ;
load_sd_seq_sub_patterns_json ( seq_number ) ;
load_sd_seq_sub_vel_json ( seq_number ) ;
load_sd_fx_json ( seq_number , 1 ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
char filename [ FILENAME_LEN ] ;
sprintf ( filename , " /%s/%d-S.json " , SEQ_CONFIG_PATH , seq_number ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
# ifdef DEBUG
Serial . print ( F ( " Found Sequencer configuration [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ]... loading... " ) ) ;
Serial . println ( F ( " " ) ) ;
# endif
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . println ( F ( " Read JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
int total = sizeof ( seq_patternchain ) ;
int columns = sizeof ( seq_patternchain [ 0 ] ) ;
int rows = total / columns ;
int count = 0 ;
for ( uint8_t i = 0 ; i < rows ; i + + )
{
for ( uint8_t j = 0 ; j < columns ; j + + )
{
seq_patternchain [ i ] [ j ] = data_json [ " seq_patternchain " ] [ count ] ;
count + + ;
}
}
for ( uint8_t i = 0 ; i < sizeof ( seq_track_type ) ; i + + )
{
seq_track_type [ i ] = data_json [ " track_type " ] [ i ] ;
}
for ( uint8_t i = 0 ; i < sizeof ( seq_content_type ) ; i + + )
{
seq_content_type [ i ] = data_json [ " content_type " ] [ i ] ;
}
for ( uint8_t i = 0 ; i < sizeof ( seq_inst_dexed ) ; i + + )
{
seq_inst_dexed [ i ] = data_json [ " seq_inst_dexed " ] [ i ] ;
}
if ( data_json [ " seq_name " ] [ 0 ] ! = 0 )
{
for ( uint8_t i = 0 ; i < FILENAME_LEN ; i + + )
{
seq_name [ i ] = data_json [ " seq_name " ] [ i ] ;
}
}
count = 0 ;
seq_tempo_ms = data_json [ " seq_tempo_ms " ] ;
seq_bpm = data_json [ " seq_bpm " ] ;
arp_play_basenote = data_json [ " arp_play_basenote " ] ;
arp_speed = data_json [ " arp_speed " ] ;
arp_lenght = data_json [ " arp_lenght " ] ;
arp_style = data_json [ " arp_style " ] ;
seq_chord_velocity = data_json [ " seq_chord_velocity " ] ;
seq_chord_dexed_inst = data_json [ " seq_chord_dexed_inst " ] ;
seq_chain_lenght = data_json [ " seq_chain_lenght " ] ;
seq_transpose = data_json [ " seq_transpose " ] ;
seq_chord_key_ammount = data_json [ " chord_key_ammount " ] ;
seq_oct_shift = data_json [ " seq_oct_shift " ] ;
seq_element_shift = data_json [ " seq_element_shift " ] ;
for ( uint8_t instance_id = 0 ; instance_id < NUM_DEXED ; instance_id + + )
{
configuration . performance . bank [ instance_id ] = data_json [ " bank " ] [ instance_id ] ;
configuration . performance . voice [ instance_id ] = data_json [ " voice " ] [ instance_id ] ;
load_sd_voice ( configuration . performance . bank [ instance_id ] , configuration . performance . voice [ instance_id ] , instance_id ) ;
load_sd_voiceconfig_json ( seq_number , instance_id , 1 ) ;
MicroDexed [ instance_id ] - > setGain ( midi_volume_transform ( map ( configuration . dexed [ instance_id ] . sound_intensity , SOUND_INTENSITY_MIN , SOUND_INTENSITY_MAX , 0 , 127 ) ) ) ;
MicroDexed [ instance_id ] - > panic ( ) ;
# ifdef DEBUG
Serial . print ( F ( " " ) ) ;
Serial . print ( F ( " Load Voice-Config for sequencer " ) ) ;
Serial . print ( instance_id ) ;
Serial . print ( F ( " " ) ) ;
# endif
}
for ( uint8_t instance_id = 0 ; instance_id < NUM_DEXED ; instance_id + + )
set_voiceconfig_params ( instance_id ) ;
set_fx_params ( ) ;
if ( seq_running )
timer1 . begin ( sequencer , seq_tempo_ms / 2 ) ;
else
timer1 . begin ( sequencer , seq_tempo_ms / 2 , false ) ;
return ( true ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
}
}
else
{
Serial . print ( F ( " No " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " available. " ) ) ;
# endif
}
}
return ( false ) ;
}
bool check_sd_seq_exists ( uint8_t number )
{
if ( number < 0 )
return ( false ) ;
number = constrain ( number , 0 , 99 ) ;
AudioNoInterrupts ( ) ;
if ( sd_card > 0 )
{
char filename [ FILENAME_LEN ] ;
sprintf ( filename , " /%s/%d-S.json " , SEQ_CONFIG_PATH , number ) ;
// check if file exists...
if ( SD . exists ( filename ) )
{
AudioInterrupts ( ) ;
return ( true ) ;
} else
{
AudioInterrupts ( ) ;
return ( false ) ;
}
} else
{ AudioInterrupts ( ) ;
return ( false ) ;
}
}
/******************************************************************************
SD PERFORMANCE
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool load_sd_performance_json ( uint8_t p )
{
if ( p < 0 )
return ( false ) ;
p = constrain ( p , 0 , MAX_PERFORMANCE ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
char filename [ FILENAME_LEN ] ;
sprintf ( filename , " /%s/%s%d.json " , PERFORMANCE_CONFIG_PATH , PERFORMANCE_CONFIG_NAME , p ) ;
// first check if file exists...
AudioNoInterrupts ( ) ;
if ( SD . exists ( filename ) )
{
// ... and if: load
# ifdef DEBUG
Serial . print ( F ( " Found performance configuration [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ]... loading... " ) ) ;
# endif
json = SD . open ( filename ) ;
if ( json )
{
deserializeJson ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
check_configuration_performance ( ) ;
# ifdef DEBUG
Serial . println ( F ( " Read JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
for ( uint8_t i = 0 ; i < MAX_DEXED ; i + + )
{
configuration . performance . bank [ i ] = data_json [ " bank " ] [ i ] ;
configuration . performance . voice [ i ] = data_json [ " voice " ] [ i ] ;
configuration . performance . voiceconfig_number [ i ] = data_json [ " voiceconfig_number " ] [ i ] ;
}
configuration . performance . fx_number = data_json [ " fx_number " ] ;
for ( uint8_t instance_id = 0 ; instance_id < NUM_DEXED ; instance_id + + )
{
load_sd_voice ( configuration . performance . bank [ instance_id ] , configuration . performance . voice [ instance_id ] , instance_id ) ;
load_sd_voiceconfig_json ( configuration . performance . voiceconfig_number [ instance_id ] , instance_id , 0 ) ;
MicroDexed [ instance_id ] - > ControllersRefresh ( ) ;
MicroDexed [ instance_id ] - > panic ( ) ;
}
load_sd_fx_json ( configuration . performance . fx_number , 0 ) ;
return ( true ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
}
}
else
{
Serial . print ( F ( " No " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " available. " ) ) ;
# endif
}
}
AudioInterrupts ( ) ;
return ( false ) ;
}
bool save_sd_performance_json ( uint8_t p )
{
char filename [ FILENAME_LEN ] ;
p = constrain ( p , 0 , MAX_PERFORMANCE ) ;
sprintf ( filename , " /%s/%s%d.json " , PERFORMANCE_CONFIG_PATH , PERFORMANCE_CONFIG_NAME , p ) ;
if ( sd_card > 0 )
{
File json ;
StaticJsonDocument < JSON_BUFFER_SIZE > data_json ;
# ifdef DEBUG
Serial . print ( F ( " Saving performance config as JSON " ) ) ;
Serial . print ( p ) ;
Serial . print ( F ( " to " ) ) ;
Serial . println ( filename ) ;
# endif
AudioNoInterrupts ( ) ;
// Check if voice- and fx-config exist. If not, save the actual state
sprintf ( filename , " /%s/%s%d.json " , FX_CONFIG_PATH , FX_CONFIG_NAME , configuration . performance . fx_number ) ;
if ( ! SD . exists ( filename ) )
{
# ifdef DEBUG
Serial . print ( F ( " FX-Config " ) ) ;
Serial . print ( configuration . performance . fx_number ) ;
Serial . println ( F ( " does not exists, creating one. " ) ) ;
# endif
save_sd_fx_json ( configuration . performance . fx_number , 0 ) ;
}
for ( uint8_t i = 0 ; i < MAX_DEXED ; i + + )
{ sprintf ( filename , " /%s/%s%d.json " , VOICE_CONFIG_PATH , VOICE_CONFIG_NAME , configuration . performance . voiceconfig_number [ i ] ) ;
if ( ! SD . exists ( filename ) )
{
# ifdef DEBUG
Serial . print ( F ( " Voice-Config " ) ) ;
Serial . print ( configuration . performance . voiceconfig_number [ i ] ) ;
Serial . println ( F ( " does not exists, creating one. " ) ) ;
# endif
save_sd_voiceconfig_json ( configuration . performance . voiceconfig_number [ i ] , i , 0 ) ;
}
}
sprintf ( filename , " /%s/%s%d.json " , PERFORMANCE_CONFIG_PATH , PERFORMANCE_CONFIG_NAME , p ) ;
json = SD . open ( filename , FILE_WRITE ) ;
if ( json )
{
for ( uint8_t i = 0 ; i < MAX_DEXED ; i + + )
{
data_json [ " bank " ] [ i ] = configuration . performance . bank [ i ] ;
data_json [ " voice " ] [ i ] = configuration . performance . voice [ i ] ;
data_json [ " voiceconfig_number " ] [ i ] = configuration . performance . voiceconfig_number [ i ] ;
}
data_json [ " fx_number " ] = configuration . performance . fx_number ;
# ifdef DEBUG
Serial . println ( F ( " Write JSON data: " ) ) ;
serializeJsonPretty ( data_json , Serial ) ;
Serial . println ( ) ;
# endif
serializeJsonPretty ( data_json , json ) ;
json . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
json . close ( ) ;
}
else
{
# ifdef DEBUG
Serial . print ( F ( " E : Cannot open " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " on SD. " ) ) ;
# endif
}
AudioInterrupts ( ) ;
return ( false ) ;
}
/******************************************************************************
HELPER FUNCTIONS
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool get_sd_data ( File sysex , uint8_t format , uint8_t * conf )
{
uint16_t n ;
int32_t bulk_checksum_calc = 0 ;
int8_t bulk_checksum ;
# ifdef DEBUG
Serial . print ( F ( " Reading " ) ) ;
Serial . print ( sysex . size ( ) ) ;
Serial . println ( F ( " bytes. " ) ) ;
# endif
AudioNoInterrupts ( ) ;
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 ( ) ! = 0x67 ) // check sysex vendor is unofficial SYSEX-ID for MicroDexed
{
# ifdef DEBUG
Serial . println ( F ( " E : SysEx vendor not unofficial SYSEX-ID for MicroDexed. " ) ) ;
# endif
return ( false ) ;
}
if ( sysex . read ( ) ! = format ) // check for sysex type
{
# ifdef DEBUG
Serial . println ( F ( " E : SysEx type not found. " ) ) ;
# endif
return ( false ) ;
}
sysex . seek ( sysex . size ( ) - 1 ) ;
if ( sysex . read ( ) ! = 0xf7 ) // check sysex end-byte
{
# ifdef DEBUG
Serial . println ( F ( " E : SysEx end byte not found. " ) ) ;
# endif
return ( false ) ;
}
sysex . seek ( sysex . size ( ) - 2 ) ; // Bulk checksum
bulk_checksum = sysex . read ( ) ;
sysex . seek ( 3 ) ; // start of bulk data
for ( n = 0 ; n < sysex . size ( ) - 6 ; n + + )
{
uint8_t d = sysex . read ( ) ;
bulk_checksum_calc - = d ;
# ifdef DEBUG
Serial . print ( F ( " SYSEX data read: 0x " ) ) ;
Serial . println ( d , HEX ) ;
# endif
}
bulk_checksum_calc & = 0x7f ;
if ( int8_t ( bulk_checksum_calc ) ! = bulk_checksum )
{
# ifdef DEBUG
Serial . print ( F ( " E : Bulk checksum mismatch : 0x " ) ) ;
Serial . print ( int8_t ( bulk_checksum_calc ) , HEX ) ;
Serial . print ( F ( " != 0x " ) ) ;
Serial . println ( bulk_checksum , HEX ) ;
# endif
return ( false ) ;
}
# ifdef DEBUG
else
{
Serial . print ( F ( " Bulk checksum : 0x " ) ) ;
Serial . print ( int8_t ( bulk_checksum_calc ) , HEX ) ;
Serial . print ( F ( " [0x " ) ) ;
Serial . print ( bulk_checksum , HEX ) ;
Serial . println ( F ( " ] " ) ) ;
}
# endif
sysex . seek ( 3 ) ; // start of bulk data
for ( n = 0 ; n < sysex . size ( ) - 6 ; n + + )
{
uint8_t d = sysex . read ( ) ;
* ( conf + + ) = d ;
}
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . println ( F ( " SD data loaded. " ) ) ;
# endif
return ( true ) ;
}
bool write_sd_data ( File sysex , uint8_t format , uint8_t * data , uint16_t len )
{
# ifdef DEBUG
Serial . print ( F ( " Storing SYSEX format 0x " ) ) ;
Serial . print ( format , HEX ) ;
Serial . print ( F ( " with length of " ) ) ;
Serial . print ( len , DEC ) ;
Serial . println ( F ( " bytes. " ) ) ;
# endif
// write sysex start
AudioNoInterrupts ( ) ;
sysex . write ( 0xf0 ) ;
# ifdef DEBUG
Serial . println ( F ( " Write SYSEX start: 0xf0 " ) ) ;
# endif
// write sysex vendor is unofficial SYSEX-ID for MicroDexed
sysex . write ( 0x67 ) ;
# ifdef DEBUG
Serial . println ( F ( " Write SYSEX vendor: 0x67 " ) ) ;
# endif
// write sysex format number
sysex . write ( format ) ;
# ifdef DEBUG
Serial . print ( F ( " Write SYSEX format: 0x " ) ) ;
Serial . println ( format , HEX ) ;
# endif
// write data
sysex . write ( data , len ) ;
# ifdef DEBUG
for ( uint16_t i = 0 ; i < len ; i + + )
{
Serial . print ( F ( " Write SYSEX data: 0x " ) ) ;
Serial . println ( data [ i ] , HEX ) ;
}
# endif
// write checksum
sysex . write ( calc_checksum ( data , len ) ) ;
# ifdef DEBUG
uint8_t checksum = calc_checksum ( data , len ) ;
sysex . write ( checksum ) ;
Serial . print ( F ( " Write SYSEX checksum: 0x " ) ) ;
Serial . println ( checksum , HEX ) ;
# endif
// write sysex end
sysex . write ( 0xf7 ) ;
AudioInterrupts ( ) ;
# ifdef DEBUG
Serial . println ( F ( " Write SYSEX end: 0xf7 " ) ) ;
# endif
return ( true ) ;
}
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 strip_extension ( const char * s , char * target , uint8_t len )
{
char tmp [ FILENAME_LEN ] ;
char * token ;
strcpy ( tmp , s ) ;
token = strtok ( tmp , " . " ) ;
if ( token = = NULL )
strcpy ( target , " *ERROR* " ) ;
else
strcpy ( target , token ) ;
target [ len ] = ' \0 ' ;
}
bool get_bank_name ( uint8_t b , char * name , uint8_t len )
{
File sysex ;
if ( sd_card > 0 )
{
char bankdir [ 4 ] ;
File entry ;
memset ( name , 0 , len ) ;
sprintf ( bankdir , " /%d " , b ) ;
// try to open directory
sysex = SD . open ( bankdir ) ;
if ( ! sysex )
return ( false ) ;
do
{
entry = sysex . openNextFile ( ) ;
} while ( entry . isDirectory ( ) ) ;
if ( entry . isDirectory ( ) )
{
entry . close ( ) ;
sysex . close ( ) ;
return ( false ) ;
}
strip_extension ( entry . name ( ) , name , len ) ;
# ifdef DEBUG
Serial . print ( F ( " Found bank-name [ " ) ) ;
Serial . print ( name ) ;
Serial . print ( F ( " ] for bank [ " ) ) ;
Serial . print ( b ) ;
Serial . println ( F ( " ] " ) ) ;
# endif
entry . close ( ) ;
sysex . close ( ) ;
return ( true ) ;
}
return ( false ) ;
}
bool get_voice_name ( uint8_t b , uint8_t v , char * name , uint8_t len )
{
File sysex ;
if ( sd_card > 0 )
{
char bank_name [ BANK_NAME_LEN ] ;
char filename [ FILENAME_LEN ] ;
b = constrain ( b , 0 , MAX_BANKS - 1 ) ;
v = constrain ( v , 0 , MAX_VOICES - 1 ) ;
get_bank_name ( b , bank_name , sizeof ( bank_name ) ) ;
sprintf ( filename , " /%d/%s.syx " , b , bank_name ) ;
# ifdef DEBUG
Serial . print ( F ( " Reading voice-name from [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ] " ) ) ;
# endif
// try to open directory
AudioNoInterrupts ( ) ;
sysex = SD . open ( filename ) ;
if ( ! sysex )
return ( false ) ;
memset ( name , 0 , len ) ;
sysex . seek ( 124 + ( v * 128 ) ) ;
sysex . read ( name , min ( len , 10 ) ) ;
# ifdef DEBUG
Serial . print ( F ( " Found voice-name [ " ) ) ;
Serial . print ( name ) ;
Serial . print ( F ( " ] for bank [ " ) ) ;
Serial . print ( b ) ;
Serial . print ( F ( " ] and voice [ " ) ) ;
Serial . print ( v ) ;
Serial . println ( F ( " ] " ) ) ;
# endif
sysex . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
return ( false ) ;
}
bool get_voice_by_bank_name ( uint8_t b , const char * bank_name , uint8_t v , char * voice_name , uint8_t len )
{
File sysex ;
if ( sd_card > 0 )
{
char filename [ FILENAME_LEN ] ;
sprintf ( filename , " /%d/%s.syx " , b , bank_name ) ;
# ifdef DEBUG
Serial . print ( F ( " Reading voice-name from [ " ) ) ;
Serial . print ( filename ) ;
Serial . println ( F ( " ] " ) ) ;
# endif
// try to open directory
AudioNoInterrupts ( ) ;
sysex = SD . open ( filename ) ;
if ( ! sysex )
return ( false ) ;
memset ( voice_name , 0 , len ) ;
sysex . seek ( 124 + ( v * 128 ) ) ;
sysex . read ( voice_name , min ( len , 10 ) ) ;
# ifdef DEBUG
Serial . print ( F ( " Found voice-name [ " ) ) ;
Serial . print ( voice_name ) ;
Serial . print ( F ( " ] for bank [ " ) ) ;
Serial . print ( b ) ;
Serial . print ( F ( " | " ) ) ;
Serial . print ( bank_name ) ;
Serial . print ( F ( " ] and voice [ " ) ) ;
Serial . print ( v ) ;
Serial . println ( F ( " ] " ) ) ;
# endif
sysex . close ( ) ;
AudioInterrupts ( ) ;
return ( true ) ;
}
return ( false ) ;
}
void string_toupper ( char * s )
{
while ( * s )
{
* s = toupper ( ( unsigned char ) * s ) ;
s + + ;
}
}