/* * GLIB - a Generic LIBrarian and editor for synths * * Kawai K-5 Librarian - handles SINGLE and MULTI patches. * Functions are annotated if they work for SINGLE, MULTI, or both kinds. * Full editing not implemented - there are zillions of parameters, * and the huge LCD on the K-5 is actually quite easy to read and use. * * Alan Bland - att!druwy!mab */ #define OVERLAY2 #include "glib.h" #define K5SINGLE 0 #define K5MULTI 1 #define K5MAGIC 0x5a3c #define NERRS 5 extern char *visnum(); /* This array contains arbitrary screen labels (SINGLE) */ struct labelinfo Lk5sin[] = { 3,10,"Sorry, no edit capability implemented for the K-5 yet.", 15,2,"+-------------------------+--------------+", 16,2,"|Space = Play Note",16,28,"| Auto-Note |", 17,2,"|",17,28,"|",17,43,"|", 18,2,"|h = left q = quit |Pitch",18,43,"|", 19,2,"|j = down N = Set Name |Volume",19,43,"|", 20,2,"|k = up J = Decrement |Duration |", 21,2,"|l = right K = Increment |Channel |", 22,2,"|",22,28,"|",22,43,"|", 23,2,"+-------------------------+--------------+", -1,-1,NULL }; /* This array defines all the editable parameters. (SINGLE) */ struct paraminfo Pk5sin[] = { "autopitch", NULL, -1,-1, 18, 38, visnum, 0, 127, 60, 0, "autovol", NULL, -1,-1, 19, 38, visnum, 0, 127, 63, 0, "autodur", NULL, -1,-1, 20, 38, visnum, 1, 20, 5, 0, "autochan", NULL, -1,-1, 21, 38, visnum, 1, 16, 1, 0, NULL, NULL, -1,-1, -1, -1, visnum, 0, 0, 0, 0 }; /* This array contains arbitrary screen labels (MULTI) */ struct labelinfo Lk5mul[] = { 3,10,"Sorry, no edit capability implemented for the K-5 yet.", 15,2,"+-------------------------+--------------+", 16,2,"|Space = Play Note",16,28,"| Auto-Note |", 17,2,"|",17,28,"|",17,43,"|", 18,2,"|h = left q = quit |Pitch",18,43,"|", 19,2,"|j = down N = Set Name |Volume",19,43,"|", 20,2,"|k = up J = Decrement |Duration |", 21,2,"|l = right K = Increment |Channel |", 22,2,"|",22,28,"|",22,43,"|", 23,2,"+-------------------------+--------------+", -1,-1,NULL }; /* This array defines all the editable parameters. (MULTI) */ struct paraminfo Pk5mul[] = { "autopitch", NULL, -1,-1, 18, 38, visnum, 0, 127, 60, 0, "autovol", NULL, -1,-1, 19, 38, visnum, 0, 127, 63, 0, "autodur", NULL, -1,-1, 20, 38, visnum, 1, 20, 5, 0, "autochan", NULL, -1,-1, 21, 38, visnum, 1, 16, 1, 0, NULL, NULL, -1,-1, -1, -1, visnum, 0, 0, 0, 0 }; /* * k5vnum * * Convert a voice number (0 to 47) to the string displayed in the * librarian (A1-D12) (SINGLE and MULTI) */ char * k5vnum(n) register int n; { static char v[4]; if ( n < 0 || n > 47 ) return("??"); sprintf(v, "%c%d", n/12 + 'A', n%12 + 1); return(v); } /* * k5numv * * Convert an alphanumeric voice number (A1-D12) to internal * format (0 - 47). (SINGLE and MULTI) */ k5numv(n) register char *n; { register int v,j; /* first better be [a-dA-D] */ *n = toupper(*n); if (*n == 'A' || *n == 'B' || *n == 'C' || *n == 'D') { v = 12 * (*n - 'A'); } else { return(-1); } /* followed by 1-12 */ j = atoi(++n); if (j<1 || j>12) return(-1); return v + j - 1; } /* * k5sindin * * Take library bank 'data' and stuff values in the P array, by using * the setval function. */ k5sindin(data) char *data; { /* We set the 'auto-note' channel upon entry */ setval("autochan",Channel); } /* * k5sindout * * Take (possibly changed) parameters values out of the P array and * put them back into the library bank 'data'. */ k5sindout(data) char *data; { /* If the autochan parameter has changed, update Channel */ Channel = getval("autochan"); } /* * k5muldin * * Take library bank 'data' and stuff values in the P array, by using * the setval function. */ k5muldin(data) char *data; { /* We set the 'auto-note' channel upon entry */ setval("autochan",Channel); } /* * k5muldout * * Take (possibly changed) parameters values out of the P array and * put them back into the library bank 'data'. */ k5muldout(data) char *data; { /* If the autochan parameter has changed, update Channel */ Channel = getval("autochan"); } /* * k5sinnof * * Return a pointer to the voice name buried in library bank data. (SINGLE) */ char * k5sinnof(data) register char *data; { static char currbuff[9]; register char *p; register int m; p = currbuff; for ( m=0; m<16; m+=2 ) *p++ = (data[m]<<4) | data[m+1]; *p = '\0'; return(currbuff); } /* * k5nameok * * Convert an ascii string to the ascii subset used for K5 names. */ char* k5nameok(name) char *name; { static char okname[9]; register int m; for (m=0; m<9 && name[m]; ++m) { okname[m] = toupper(name[m]); if (!isalnum(okname[m])) { switch (okname[m]) { case '-': case ':': case '/': case '*': case '?': case '!': case '#': case '&': case '(': case ')': case '"': case '+': case '.': case '=': case ' ': break; default: okname[m] = ' '; break; } } } okname[m] = '\0'; return okname; } /* * k5sinsnof * * Set the voice name buried in data to name. (SINGLE) */ k5sinsnof(data,name) char *data; char *name; { register char *p; register int m; for ( p=k5nameok(name),m=0; *p!='\0' && m<17; p++,m+=2 ) { data[m] = (*p & 0xf0) >> 4; data[m+1] = *p & 0x0f; } for ( ; m<17; m+=2 ) { data[m] = (' ' & 0xf0) >> 4; data[m+1] = ' ' & 0x0f; } } /* * k5mulnof * * Return a pointer to the voice name buried in library bank data. (MULTI) */ char * k5mulnof(data) char *data; { static char currbuff[9]; register char *p; register int m; p = currbuff; for ( m=0; m<16; m+=2 ) *p++ = (data[m+330]<<4) | data[m+331]; *p = '\0'; return(currbuff); } /* * k5mulsnof * * Set the voice name buried in data to name. (MULTI) */ k5mulsnof(data,name) char *data; char *name; { char *p; int m; for ( p=k5nameok(name),m=0; *p!='\0' && m<17; p++,m+=2 ) { data[m+330] = (*p & 0xf0) >> 4; data[m+331] = *p & 0x0f; } for ( ; m<17; m+=2 ) { data[m+330] = (' ' & 0xf0) >> 4; data[m+331] = ' ' & 0x0f; } } /* * k5sbulk * * common function to send all voices to the K-5 (SINGLE and MULTI) */ k5sbulk(data, which) char *data; int which; /* K5SINGLE or K5MULTI */ { int n, err = 0; message(""); for (n=0; n> 4; data[length-3] = (sum & 0x000f); data[length-2] = (sum & 0xf000) >> 12; data[length-1] = (sum & 0x0f00) >> 8; sendmidi(0xf0); sendmidi(0x40); sendmidi(Channel-1); sendmidi(0x20); sendmidi(0x00); sendmidi(0x02); sendmidi(which); sendmidi(iv); for (i=0; i toolong ) { Reason = "Timeout waiting for K-5 response"; goto getout; } } /* ignore active sensing */ if ((c = getmidi() & 0xff) != 0xfe) { ++i; } } /* check the result */ switch (c) { case 0x40: ret = 0; Reason = ""; break; case 0x41: Reason = "K-5 write error"; break; case 0x42: Reason = "K-5 write error (protected)"; break; case 0x43: Reason = "K-5 write error (no card)"; break; default: Reason = "Wierd response (is that really a K-5 you have?)"; break; } getout: return(ret); } k5sinsedit() { } k5mulsedit() { } /* * k5singbulk * * get all internal SINGLE voices from K-5. */ k5singbulk(data) char *data; { return k5gbulk(data, K5SINGLE); } /* * k5mulgbulk * * get all internal MULTI voices from K-5. */ k5mulgbulk(data) char *data; { return k5gbulk(data, K5MULTI); } /* * k5gbulk * * common routine - get all SINGLE or MULTI voices from K-5. */ k5gbulk(data, which) register char *data; int which; /* K5SINGLE or K5MULTI */ { int c, v, sumerr = 0; register int n, i, sum; long begin, toolong; message(""); flushmidi(); for(v = 0; v < Nvoices; v++) { retry: if (which == K5MULTI) { /* i don't know if this is a K-5 or Amiga problem */ /* but multi patch download seems to need this delay */ millisleep(500); } /* request the voice */ sendmidi(0xf0); sendmidi(0x40); sendmidi(Channel-1); sendmidi(0x00); sendmidi(0x00); sendmidi(0x02); sendmidi(which); sendmidi(v); sendmidi(EOX); /* set up for timeout */ begin = milliclock(); toolong = begin + 1000 * TIMEOUT; /* wait for the xf0 byte starting the dump */ while ( milliclock() < toolong ) { if ( STATMIDI && (c=(getmidi() & 0xff)) == 0xf0 ) break; } if ( c != 0xf0 ) { Reason = "Timeout waiting for dump from K-5"; return 1; } /*printf("%02x ", c);*/ /* skip the next 7 bytes (remainder of sysex header) */ for (i=0; i<7; ) { /* wait for midi byte or timeout */ while ( ! STATMIDI ) { if ( milliclock() > toolong ) goto timeout; } /* ignore active sensing */ if ((c = getmidi() & 0xff) != 0xfe) { ++i; /*printf("%02x ", c);*/ } } /* read voice data until EOX */ n = 0; while (1) { /* wait for midi byte or timeout */ while ( ! STATMIDI ) { if ( milliclock() > toolong ) goto timeout; } if ((c = getmidi() & 0xff) == 0xfe) { /* ignore active sensing */ continue; } else if (c == EOX) { /* finished */ break; } else { /* got a data byte */ VOICEBYTE(data,v,n) = c; ++n; } } /*printf("got block n=%d\n", n);*/ /* verify the checksum */ for (sum=0, i=0; i> 4) && (data[n-3] == (sum & 0x000f)) && (data[n-2] == (sum & 0xf000) >> 12) && (data[n-1] == (sum & 0x0f00) >> 8)) { sumerr = 0; windputc('+'); } else { /* retry a few times if checksum failed */ windputc('-'); if (sumerr++ >= NERRS) { Reason = "Too many checksum errors!"; return(1); } goto retry; } windrefresh(); } /* go back for another voice */ Reason = ""; return(0); timeout: Reason = "Timeout while reading!"; return(1); }