KORG microX utility.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

342 lines
11 KiB

/*
* kmxtool - KORG microX utility
* Copyright (C) 2012 Damien Goutte-Gattat
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <microx.h>
#include <sysex.h>
int
kmx_microx_identify(midi_io_t *midi,
struct kmx_microx_version *version)
{
int n;
midi_device_id_t devid;
if ( (n = sysex_identify(midi, &devid)) < 0 )
return KMX_IO_ERROR;
else if ( n == 0 )
return KMX_INVALID_REPLY;
if ( devid.manufacturer != KORG_ID )
return KMX_NOT_KORG;
if ( devid.family != MICROX_ID || devid.model != MICROX_SUB_ID )
return KMX_NOT_MICROX;
if ( version ) {
version->major = devid.version & 0xFF;
version->minor = (devid.version >> 8) & 0xFF;
version->release = (devid.version >> 16) & 0xFF;
version->build = (devid.version >> 24) & 0xFF;
}
return 0;
}
int
kmx_microx_query_status(midi_io_t *midi,
struct kmx_microx_status *s)
{
ssize_t n;
unsigned char reply[64];
unsigned char query[] = { 0xF0, /* SysEx message begin */
0x42, /* KORG manufacturer ID */
0x30, /* SysEx channel (1) */
0x7A, /* End of SysEx header */
0x12, /* Mode and state request */
0xF7 /* SysEx message end */ };
if ( (n = sysex_query(midi, query, sizeof(query),
reply, sizeof(reply))) < 0 )
return KMX_IO_ERROR;
/*
* Expected Mode and state Reply (27 bytes):
* Byte # Value Meaning
* 0 0xF0 SysEx message begin
* 1 0x42 KORG manufacturer ID
* 2 0x3n Global channel (n = 0x0 ~ 0xF)
* 3 0x7A End of SysEx header
* 4 0x42 Mode and state reply
* 5 0xnn Current mode (one of kmx_microx_mode)
* 6 0x06 Status of Global mode
* 7 0xnn Global mode setting data 1
* bit 0: Local Control Off/On
* bits 1,2: MIDI Clock (one of kmx_microx_clock)
* bit 3: (Reserved)
* bit 4: Bank MAP KORG/GM
* bit 5: Power On Mode Reset/Memorize
* bit 6: Receive Ext.Realtime Command Off/On
* 8 0xnn Global mode setting data 2
* bit 0: Enable Program Change Off/On
* bit 1: Enable Bank Change Off/On
* bit 2: Enable Combination Change Off/On
* bit 3: Enable Aftertouch Off/On
* bit 4: Enable Control Change Off/On
* bits 5,6: Note Filter All/Even/Odd
* 9 0xnn Global mode setting data 3 (Memory Protection)
* bit 0: Program
* bit 1: Combination
* bit 2: Multi
* bit 3: Drums
* bit 4: ArpeggioPattern
* bit 5: Ext.Control
* 10 0x02 Status of Program mode
* 11 0x0n Current Bank (n = 0x0 ~ 0x4, bank A ~ E)
* 12 0xnn Current Program (n = 0x0 ~ 0x7F)
* 13 0x00 (Reserved)
* 14 0x00 Status of Combination mode
* 15 0x0n Current Bank (n = 0x0 ~ 0x2, bank A ~ C)
* 16 0xnn Current Combination (n = 0x0 ~ 0x7F)
* 17 0x00 (Reserved)
* 18 0x04 Status of Multi mode
* 19 0x00 (Reserved)
* 20 0xnn Current Multi (n = 0x0 ~ 0x7F)
* 21 0x0n Control Track (n = 0x0 ~ 0xF, track 1 ~ 16)
* 22 0x21 Status of Ext.Controller
* 23 0x0n Ext.Controller Off/On
* 24 0xnn Current Controller (n = 0x0 ~ 0x3F)
* 25 0x00 (Reserved)
* 26 0xF7 SysEx message end
*/
if ( n != 27 || reply[4] != 0x42 )
return KMX_INVALID_REPLY;
if ( reply[1] != KORG_ID )
return KMX_NOT_KORG;
if ( s ) {
s->global_channel = reply[2] & 0xF;
s->active_mode = reply[5];
s->global.local_control = reply[7] & 0x1;
s->global.clock_source = reply[7] >> 1 & 0x3;
s->global.bank_map = reply[7] >> 4 & 0x1;
s->global.memorize_mode = reply[7] >> 5 & 0x1;
s->global.rec_ext_rt_cmd = reply[7] >> 6 & 0x1;
s->global.prgm_change = reply[8] & 0x1;
s->global.bank_change = reply[8] >> 1 & 0x1;
s->global.comb_change = reply[8] >> 2 & 0x1;
s->global.aftertouch = reply[8] >> 3 & 0x1;
s->global.ctrl_change = reply[8] >> 4 & 0x1;
s->global.note_filter = reply[8] >> 5 & 0x3;
s->global.protected_memory.program = reply[9] & 0x1;
s->global.protected_memory.bank = reply[9] >> 1 & 0x1;
s->global.protected_memory.multi = reply[9] >> 2 & 0x1;
s->global.protected_memory.drum = reply[9] >> 3 & 0x1;
s->global.protected_memory.arpeggio = reply[9] >> 4 & 0x1;
s->global.protected_memory.ext_ctrl = reply[9] >> 5 & 0x1;
s->program.current_bank = reply[11];
s->program.current_program = reply[12];
s->combination.current_bank = reply[15];
s->combination.current_combination = reply[16];
s->multi.current_multi = reply[20];
s->multi.control_track = reply[21];
s->ext_ctrl.enabled = reply[23];
s->ext_ctrl.current = reply[24];
}
return 0;
}
const char *
kmx_microx_error(midi_io_t *midi, int e)
{
const char *msg;
switch ( e ) {
case KMX_IO_ERROR:
msg = midi_error(midi);
break;
case KMX_INVALID_REPLY:
msg = "invalid Device Inquiry Reply";
break;
case KMX_NOT_KORG:
msg = "not a KORG device";
break;
case KMX_NOT_MICROX:
msg = "not a microX device";
case KMX_INVALID_QUERY:
msg = "invalid query";
break;
case KMX_BUFFER_TOO_SMALL:
msg = "output buffer too small";
break;
default: /* Should not happen. */
msg = "unknown error";
break;
}
return msg;
}
struct dump {
unsigned char query_code;
unsigned char reply_code;
size_t data_size;
};
static struct dump dumps[] = {
{ 0x1C, 0x4C, MICROX_PROGRAM_SIZE },
{ 0x1D, 0x4D, MICROX_COMBINATION_SIZE },
{ 0x18, 0x48, MICROX_MULTI_SIZE },
{ 0x0D, 0x52, MICROX_DRUMKIT_SIZE },
{ 0x34, 0x69, MICROX_ARPEGGIO_SIZE },
{ 0x1E, 0x6E, MICROX_EXTSET_SIZE }
};
static int
get_dump_parameters(enum kmx_microx_data_type type,
const char *what,
unsigned char *buffer)
{
unsigned char program, bank;
switch ( type ) {
case PROGRAM_DATA:
if ( sscanf(what, "%c%03hhd", &bank, &program) != 2 )
return KMX_INVALID_QUERY;
if ( (bank < 'A' || bank > 'E') || program > 127 )
return KMX_INVALID_QUERY;
buffer[0] = 0x20 + (bank - 'A');
buffer[1] = program;
break;
case COMBINATION_DATA:
if ( sscanf(what, "%c%03hhd", &bank, &program) != 2 )
return KMX_INVALID_QUERY;
if ( (bank < 'A' || bank > 'C') || program > 127 )
return KMX_INVALID_QUERY;
buffer[0] = 0x20 + (bank - 'A');
buffer[1] = program;
break;
case MULTI_DATA:
if ( sscanf(what, "%02hhd", &program) != 1 || program > 127 )
return KMX_INVALID_QUERY;
buffer[0] = 1;
buffer[1] = program;
break;
case DRUMKIT_DATA:
if ( sscanf(what, "%03hhd", &program) != 1 || program > 39 )
return KMX_INVALID_QUERY;
buffer[0] = 1;
buffer[1] = program;
break;
case ARPEGGIO_DATA:
if ( sscanf(what, "%03hhd", &program) != 1 || program > 250 )
return KMX_INVALID_QUERY;
buffer[0] = 1;
buffer[1] = program >> 7;
buffer[2] = program & 0x7F;
break;
case EXTSET_DATA:
if ( sscanf(what, "%02hhd", &program) != 1 || program > 63 )
return KMX_INVALID_QUERY;
buffer[0] = 1;
buffer[1] = program;
break;
}
return 0;
}
int
kmx_microx_dump(midi_io_t *midi,
enum kmx_microx_data_type type,
const char *what,
unsigned char *data,
size_t len)
{
size_t n;
int i, j, k;
unsigned char reply[1024];
unsigned char query[] = { 0xF0, /* SysEx message begin */
0x42, /* KORG manufacturer ID */
0x30, /* Global channel */
0x7A, /* End of SysEx header */
0x00, /* Dump type */
0x00, /* Dump parameter 1 */
0x00, /* Dump parameter 2 */
0x00, /* Dump parameter 3 */
0xF7 /* SysEx message end */ };
if ( len < dumps[type].data_size )
return KMX_BUFFER_TOO_SMALL;
query[4] = dumps[type].query_code;
if ( get_dump_parameters(type, what, &(query[5])) < 0 )
return KMX_INVALID_QUERY;
if ( (n = midi_write(midi, query, sizeof(query))) < 0 )
return KMX_IO_ERROR;
i = j = k = 0;
do {
if ( (n = sysex_read(midi, reply, sizeof(reply))) < 0 )
return KMX_IO_ERROR;
i = 0;
if ( j == 0 ) {
/* Check reply header. */
if ( reply[0] != 0xF0 || reply[4] != dumps[type].reply_code )
return KMX_INVALID_REPLY;
if ( reply[1] != KORG_ID )
return KMX_NOT_KORG;
if ( reply[3] != MICROX_ID )
return KMX_NOT_MICROX;
/* Skip reply header. */
i = 8;
}
/*
* Convert data back to the microX internal representation
* and copy it to the caller's buffer. The conversion
* consists simply in skipping the null byte inserted
* every 7 data bytes.
*/
for ( ; i < n; i++, k++ ) {
if ( k % 8 != 0 )
data[j++] = reply[i];
}
} while ( reply[n-1] != 0xF7 );
return j;
}