/*
 * GLIB - a Generic LIBrarian and editor for synths
 */

#include "glib.h"
#include <ctype.h>

char *Reason = "";

int Currrow = 0;        /* at top of screen, for messages */
int Libbank = 0;        /* from 0 to LIBBANKS-1, is the current library bank*/
int Nsynths = 0;

char *Currdata;
char *Yankdata;         /* current 'yank' buffer (middle of screen) */

struct peredinfo *PE;   /* array of per-editor miscellany */

char Buff[BUFSIZ];
int Redraw = 0;         /* if non-0, edit screen is completely redrawn. */
                        /* parameter functions can make use of this. */
int Changed = 0;

/* All the global values below are set as appropriate for the */
/* synthesizer currently being dealt with. */

int Nvoices = 0;
int Voicesize =0;
int Namesize = 0;
int Libindex;           /* from 0 to Nvoices-1 */
int Synindex;           /* from 0 to Nvoices-1 */
int Channel;
int Editrow;            /* from 0 to NUMONSCREEN-1 */
int Editcol;            /* 0==synth, 1==library */
char *Libdata;          /* current library data (includes all LIBBANKS) */
                        /* ie. the stuff on the right side of the screen */
char *Syndata;          /* current synth data (1 bank), ie. the left side */
struct paraminfo *P;    /* list of parameter info */
struct labelinfo *L;    /* arbitrary screen labels for edit screen */
char *Synthname;

int (*Sendedit)();      /* function to send parameters to synth's edit buffer*/
int (*Datain)();        /* convert data from file-storage format to the */
                        /* format stored in the P[] parameter array (p_val) */
int (*Dataout)();       /* reverse of Datain */
int (*Sendone)();       /* function to send one (permanent) voice to synth */
int (*Sendbulk)();      /* function to send bulk dump to synth */
int (*Getbulk)();       /* reverse of Sendbulk */
char *(*Nameof)();      /* pulls voice name out of file-storage data */
int (*Setnameof)();     /* reverse of Nameof */
char *(*Numof)();       /* convert voice number to on-screen text */
int (*Cvtnum)();        /* convert visible voice number to std. format */
int (*Cvtanum)();       /* convert alphanumeric voice number to std. format */
                        /* should never define both Cvtnum and Cvtanum */
main()
{
        int n;

        hello();
        windinit();
        initstuff();

        if ( Nsynths == 0 )
                windstr("Hey, the E array is empty?");
        else if ( Nsynths == 1 ) {
                /* If there's only 1 synth, don't bother asking */
                setedit(0);
                libinteract();
        }
        else {
                while ( (n=choosesynth()) >= 0 ) {
                        setedit(n);
                        libinteract();
                        unsetedit(n);
                }
        }
        bye();
}

/* choose a synth, returning its position in the E array */
choosesynth()
{
        int n, pick;

    retry:
        flushconsole();
        windclear();
        windgoto(2,18);
        windstr("GLIB - A Generic Librarian/Editor");

        for ( n=1; n<=Nsynths; n++ )
                libchoice(n);

        windgoto(10+n,16);
        windstr("Choose your synth (or 'q' to quit) --> ");
        windrefresh();
        pick = mouseorkey();
        if ( pick == 'q' || pick == EOF )
                return(-1);
        if ( pick != MOUSE )
                pick = pick - '0';
        else {
                int row, col;
                getmouse(&row,&col);
                /* wait until mouse goes down */
                while ( statmouse() > 0 )
                        ;
                pick = row - 6; 
        }
        if ( pick < 1 || pick > Nsynths )
                goto retry;
        return(pick-1);
}

libchoice(n)
{
        windgoto(6+n,27);
        sprintf(Buff,"%d  -  %s",n,E[n-1].ed_name);
        windstr(Buff);
}

initstuff()
{
        int n, banksize, maxvsize = 0;
        char *p;
        
        for ( n=0; E[n].ed_name != NULL; n++ ) {
                if ( maxvsize < E[n].ed_vsize )
                        maxvsize = E[n].ed_vsize;
        }
        Nsynths = n;
        Currdata = alloc( maxvsize );

        /* allocate an array of peredinfo structs */
        PE =(struct peredinfo *)alloc((int)(Nsynths*sizeof(struct peredinfo)));
        for ( n=0; n<Nsynths; n++ ) {
                banksize =  E[n].ed_nvoices * E[n].ed_vsize;
                p = PE[n].ed_libdata = alloc( LIBBANKS * banksize );
                clrdata(p,LIBBANKS*banksize);
                p = PE[n].ed_syndata = alloc( banksize );
                clrdata(p,banksize);
                p = PE[n].ed_yankdata = alloc( E[n].ed_vsize );
                clrdata(p,E[n].ed_vsize);
                PE[n].ed_libindex = 0;
                PE[n].ed_synindex = 0;
                PE[n].ed_channel = 1;
                PE[n].ed_erow = 0;
                PE[n].ed_ecol = 0;
        }
}

clrdata(data,size)
char *data;
{
        register char *p = data, *endp = data+size;

        while ( p<endp )
                *p++ = 0;
}

setedit(n)
{
        Synthname = E[n].ed_name;
        Datain = E[n].ed_din;
        Dataout = E[n].ed_dout;
        Nvoices = E[n].ed_nvoices;
        Sendedit = E[n].ed_sedit;
        Sendone = E[n].ed_sone;
        Sendbulk = E[n].ed_sbulk;
        Getbulk = E[n].ed_gbulk;
        Nameof = E[n].ed_nof;
        Numof = E[n].ed_numof;
        Cvtnum = E[n].ed_cvtnum;
        Cvtanum = E[n].ed_cvtanum;
        Setnameof = E[n].ed_snof;
        Voicesize = E[n].ed_vsize;
        Namesize = E[n].ed_nsize;
        Libdata = PE[n].ed_libdata;
        Syndata = PE[n].ed_syndata;
        Yankdata = PE[n].ed_yankdata;
        Libindex = PE[n].ed_libindex;
        Synindex = PE[n].ed_synindex;
        Channel = PE[n].ed_channel;
        Editrow = PE[n].ed_erow;
        Editcol = PE[n].ed_ecol;
        clrdata(Currdata,Voicesize);
        P = E[n].ed_params;
        L = E[n].ed_labels;
}

unsetedit(n)
{
        int k;

        PE[n].ed_libindex = Libindex;
        PE[n].ed_synindex = Synindex;
        PE[n].ed_channel = Channel;
        PE[n].ed_erow = Editrow;
        PE[n].ed_ecol = Editcol;
        for ( k=0; k<Voicesize; k++ )
                PE[n].ed_yankdata[k] = Yankdata[k];
}

/* template - show the boxes and such on the main library screen */
template()
{
        int n, k, r;

        r = FIRSTROW-1;
        sprintf(Buff,"%s Voices",Synthname);
        n = 13 - strlen(Buff)/2;        /* center it */
        windgoto(r,n<0?0:n);
        windstr(Buff);
        r++;
        windgoto(r,0);
        for ( n=0; n<26; n++ )
                windputc('=');
        windgoto(r,52);
        for ( n=0; n<26; n++ )
                windputc('=');
        for ( n=r+1; n<(r+13); n++ ) {
                windgoto(n,0);
                windputc('|');
                windgoto(n,6);
                windputc('|');
                windgoto(n,25);
                windputc('|');

                k=52;
                windgoto(n,k);
                windputc('|');
                windgoto(n,k+6);
                windputc('|');
                windgoto(n,k+25);
                windputc('|');
        }
        windgoto(r+13,0);
        for ( n=0; n<26; n++ )
                windputc('=');
        windgoto(r+13,52);
        for ( n=0; n<26; n++ )
                windputc('=');

        windgoto(YANKROW-2,YANKCOL);
        windstr("  Yank Buffer");
        windgoto(YANKROW-1,YANKCOL);
        windstr(" ------------- ");
        windgoto(YANKROW,YANKCOL);
        windstr("               ");
        windgoto(YANKROW+1,YANKCOL);
        windstr(" ------------- ");
        windrefresh();
}

/* clear the message area */
clearmess()
{
        int n;
        for(n=1;n<(FIRSTROW-1);n++)
                winderaserow(n);
        Currrow = 0;
}

/* set the current voice (ie. the synth's edit buffer) to the indicated */
/* voice.  c==0 is the synth (left) side, c==1 is the library (right) side. */
editto(r,c)
{
        int voicenum;

        /* Clear the existing '*' */
        editchar(' ',Editrow,Editcol);
        editchar('*',Editrow=r,Editcol=c);
        if ( Editcol==0 ) {
                /* we're on the synth side */
                voicenum = Editrow+Synindex;
                tocurrent(Syndata,voicenum);
        }
        else {
                /* we're on the lib side */
                voicenum = Editrow+Libindex;
                tocurrent(bankvoice(0),voicenum);
        }
}

editchar(ec,r,c)
{
        r = r + FIRSTROW + 1;
        if ( c == 0 )
                c = LEFTSIDE-1;
        else
                c = RIGHTSIDE-1;
        windgoto(r,c);
        windputc(ec);
        windrefresh();
}

/* control interaction on the main library bank screen */
libinteract()
{
        int c, n, swap, voicenum, maxindex;
        char *p, *data;

        flushmidi();
        drawall();
        for ( ;; ) {
                Currrow = 0;
                winderaserow(Currrow);
                windgoto(Currrow,0);
                windstr("Command --> ");
                windrefresh();
                
                c = mouseorkey();
                if ( c == MOUSE ) {
                        libmouse();
                        continue;
                }

                if ( isprint(c) )
                        windputc(c);
                clearmess();
                switch ( c ) {
                case ' ':
                        playnote(1);
                        break;
                case '\n':
#ifndef macintosh
                case '\r':
#endif
                        /* ignore */
                        break;
                case EOF:
                case 'q':
                        return;
                case CH_REDRAW:
                        drawall();
                        break;
                case 's':
                case 'p':
                        swap = (c=='s')?1:0;
                        if ( Editcol==0 )
                                tosyn(Synindex+Editrow,Yankdata,swap);
                        else
                                tolib(Libindex+Editrow,Yankdata,swap);
                        updatedisplay();
                        pryankname();
                        break;
                case 'y':
                        for(n=0;n<Voicesize;n++)
                                Yankdata[n] = Currdata[n];
                        pryankname();
                        break;
                case '?':
                        helpmessage();
                        break;
                case SCR_DOWN:
                        maxindex = Nvoices - NUMONSCREEN;
                        if ( Editcol==0 ) {
                                /* we're on the synth side */
                                if ( (Synindex+=NUMONSCREEN/2) > maxindex )
                                        Synindex = maxindex;
                        }
                        else {
                                /* we're on the lib side */
                                if ( (Libindex+=NUMONSCREEN/2) > maxindex )
                                        Libindex = maxindex;
                        }
                        updatedisplay();
                        break;
                case SCR_UP:
                        if ( Editcol==0 ) {
                                /* we're on the synth side */
                                if ( (Synindex-=NUMONSCREEN/2) < 0 )
                                        Synindex = 0;
                        }
                        else {
                                /* we're on the lib side */
                                if ( (Libindex-=NUMONSCREEN/2) < 0 )
                                        Libindex = 0;
                        }
                        updatedisplay();
                        break;
                case '\033':
                case '`':
                        allnotesoff();
                        break;
                case 't':
                        transcmd();
                        break;
                case 'd':       /* download from synth to display */
                        clrdata(Syndata,Nvoices*Voicesize);
                        if ( readsynth(Syndata) == 0 )
                                syntodisplay(Synindex=0);
                        break;
                case 'u':       /* upload TO synth */
                        if ( Editcol==0 ) {
                                voicenum = Editrow+Synindex;
                                data = &(VOICEBYTE(Syndata,voicenum,0));
                        }
                        else
                                data = bankvoice(Editrow+Libindex);
                        upload(data);
                        break;
                case 'r':
                        readall();
                        break;
                case 'w':
                        writeall();
                        break;
                case 'c':
                        setchan();
                        break;
                case 'b':
                        /* cycle through banks, from 0 to LIBBANKS-1 */
                        if ( ++Libbank >= LIBBANKS )
                                Libbank = 0;
                        libtodisplay(Libindex);
                        updatedisplay();
                        break;
                case 'e':
                        p = (*Nameof)(Currdata);
                        if ( Editcol==0 ) {
                                voicenum = Editrow+Synindex;
                                data = &(VOICEBYTE(Syndata,voicenum,0));
                                editdata(p,data);
                                windclear();
                                /* Update Currdata */
                                for ( n=0; n<Voicesize; n++ )
                                    Currdata[n] = VOICEBYTE(Syndata,voicenum,n);
                        }
                        else {
                                data = bankvoice(Editrow+Libindex);
                                editdata(p,data);
                        }
                        drawall();
                        break;
                case 'g': /* goto a specific voice */
                        do_goto();
                        break;
                case CH_LEFT:
                        if ( Editcol==1 )
                                editto(Editrow,0);
                        break;
                case CH_DOWN:
                        if ( Editrow < (NUMONSCREEN-1) )
                                editto(Editrow+1,Editcol);
                        else {
                                /* we're at the bottom, so try to scroll */
                                if ( Editcol==0 ) {
                                        /* we're on the synth side */
                                        if (Synindex<(Nvoices-NUMONSCREEN))
                                                Synindex++;
                                }
                                else {
                                        /* we're on the lib side */
                                        if (Libindex<(Nvoices-NUMONSCREEN))
                                                Libindex++;
                                }
                                updatedisplay();
                        }
                        break;
                case CH_UP:
                        if ( Editrow>0 )
                                editto(Editrow-1,Editcol);
                        else {
                                /* we're at the top, so try to scroll */
                                if ( Editcol==0 ) {
                                        /* we're on the synth side */
                                        if (Synindex>0)
                                                Synindex--;
                                }
                                else {
                                        /* we're on the lib side */
                                        if (Libindex>0)
                                                Libindex--;
                                }
                                updatedisplay();
                        }       
                        break;
                case CH_RIGHT:
                        if ( Editcol==0 )
                                editto(Editrow,1);
                        break;
                case 'f':
                        filelist();
                        break;
                default:
                        message("Unrecognized command!  Press '?' for help.");
                        break;
                }
        }
}

filelist()
{
        char *p, *q, buff[BUFSIZ];
        int n, ninline = 0, nprinted = 0;

        clearmess();
        Currrow = -1;   /* To start message on top line */
        openls();
        message("Files in current directory:");
        strcpy(buff,"  ");
        while ( (p=nextls()) != NULL ) {
                /* add the next file to the line being constructed */
                q = &buff[strlen(buff)];
                strcpy(q,p);
                q += (n=strlen(p));
                while ( n++ < 15 )
                        *q++ = ' ';
                *q = '\0';
                if ( ninline++ > 3 ) {
                        message(buff);
                        if ( nprinted++ > 4 ) {
                                message("Press any key to continue ...");
                                getconsole();
                                clearmess();
                                nprinted = 0;
                        }
                        strcpy(buff,"  ");
                        ninline = 0;
                }
        }
        if ( ninline > 0 )
                message(buff);
        closels();
}

libmouse()
{
        int row, col;

        getmouse(&row,&col);
        if ( row <= FIRSTROW || row > FIRSTROW+NUMONSCREEN+1 )
                goto getout;
        if ( col < Cols/2 )
                col = 0;
        else
                col = 1;
        row = row - FIRSTROW - 1;
        editto(row,col);
getout:
        /* wait until mouse button is released */
        while ( statmouse() > 0 )
                ;
}

do_goto()
{
        int n, r, maxindex, new_ecol;
        char sbuf[100], *sp;
        
        message("");
        message("Where to? ");
        windgets(sbuf);
        
        sp = sbuf;
        switch(*sp++) {
          case 's': /* synth side */
                new_ecol = 0;
                break;
          case 'l': /* library side */
                new_ecol = 1;
                break;
          default: /* no change in side */
                new_ecol = Editcol;
                sp--;   /* but don't trash the first character */
                break;
        }
        
        clearmess();
        r = sscanf(sp, "%d", &n); /* this may fail - we handle it later */
        if(Cvtnum != NULL) { /* convert to internal format if needed */
                n = (*Cvtnum)(n) + 1; /* we are 1-based for user input */
        }
        if (Cvtanum != NULL) { /* convert using alphanumeric voice number */
                n = (*Cvtanum)(sp) + 1;
        }
        if(r != 1) {
                message("type one of: sn, ln, or n");
                message("  s = synth side (literal character 's')");
                message("  l = library side (literal character 'l')");
                message("  n = voice number to select (an integer)");
                return;
        }
        if(n <= 0 || n > Nvoices) { /* 1-based */
                message("Bad voice number!");
                return;
        }
        
        /* it can be done.  nuke the old '*' and change columes (if needed) */
        editchar(' ', Editrow, Editcol);
        Editcol = new_ecol;
        
        /* try to center it */
        maxindex = Nvoices - NUMONSCREEN;
        if(Editcol == 0) {
                Synindex = (n - 1) - NUMONSCREEN/2; /* 0-based */
                if(Synindex < 0) { /* impossible to center */
                        Synindex = 0; /* so do your best */
                } else if(Synindex > maxindex) {
                        Synindex = maxindex;
                }
                Editrow = (n - 1) - Synindex; /* and put a '*' on it */
        } else {
                Libindex = (n - 1) - NUMONSCREEN/2; /* 0-based */
                if(Libindex < 0) {
                        Libindex = 0;
                } else if(Libindex > maxindex) {
                        Libindex = maxindex;
                }
                Editrow = (n - 1) - Libindex;
        }
        
        updatedisplay(); /* do the real work */
        return;
}

upload(data)
char *data;
{
        int c, n;
        char num[16];

        message("");
        message("Upload TO synth:");
        message("                 c - current voice");
        message("                 a - ALL voices");
        message("Choose --> ");
        c = getconsole();
        if ( c == 'c' ) {
                clearmess();
                if ( Sendone == NULL ) {
                        message("Single voices can't be sent to that synth!");
                        return;
                }
                message("What voice number to you want to send it TO? --> ");
                windgets(num);
                clearmess();
                n = atoi(num);
                if(Cvtnum != NULL) { /* convert to internal format if needed */
                        n = (*Cvtnum)(n) + 1; /* we are 1-based for user input */
                }
                if (Cvtanum != NULL) { /* howzabout alphanumeric format? */
                        n = (*Cvtanum)(num) + 1;
                }
                if ( n > 0 && n <= Nvoices ) {
                        if ( (*Sendone)(n-1,data) != 0 ) { /* 0-based on calls -SAF */
                                message("Unable to write data to synth!");
                                sprintf(Buff,"Reason: %s",Reason);
                                message(Buff);
                        }
                } else {
                        message("Bad voice number!");
                }
        }
        else if ( c == 'a' ) {
                clearmess();
                if ( Sendbulk != NULL )
                        (*Sendbulk)(Syndata);
                else {
                        for ( n=0; n<Nvoices; n++ ) {
                                if ( (*Sendone)(n, &(VOICEBYTE(Syndata,n,0)) ) != 0 ) {
                                        message("Unable to write data to synth!");
                                        sprintf(Buff,"Reason: %s",Reason);
                                        message(Buff);
                                        break;
                                }
                        }
                }
                /* clearmess(); */
        }
        else {
                clearmess();
                message("Bad choice!");
        }
}

helpmessage()
{
clearmess();
sprintf(Buff,"%s,%s,%s,%s - move around              e - edit current voice",
        STR_LEFT,STR_DOWN,STR_UP,STR_RIGHT);
message(Buff);
message("r - read voices from a file        y - yank into buffer");
message("w - write voices to a file         p - put from buffer");
message("b - cycle through library banks    s - swap current voice with yank buffer");
message("t - transfer all voices            f - list files in current directory");
message("d - download voices from synth     c - set MIDI channel");
message("u - upload voices to synth         g - 'goto' form of moving around");
message("q - quit                           <space> - play a note");
}

updatedisplay()
{
        if ( Editcol==0 ) {
                /* we're on the synth side */
                syntodisplay(Synindex);
                editto(Editrow,Editcol);
        }
        else {
                /* we're on the lib side */
                libtodisplay(Libindex);
                editto(Editrow,Editcol);
        }
}

pryankname()
{
        char ybuff[33];
        char *p;

        windgoto(YANKROW,YANKCOL-4);
        windstr("                    ");
        strcpy(ybuff,(*Nameof)(Yankdata));
        /* take off trailing blanks */
        p = ybuff + strlen(ybuff) - 1;
        while ( p>ybuff && *p == ' ' )
                *p-- = '\0';
        windgoto(YANKROW,YANKCOL+7-strlen(ybuff)/2);
        windstr(ybuff);
        windrefresh();
}

transcmd()
{
        int fromc;

        message("");
        message("Transfer ALL voices:");
        message("                       1:   <<-----   from library bank to synth bank");
        message("                       2:   ----->>   from synth bank to library bank");
        message("1 or 2 --> ");
        fromc = getconsole();
        windputc(fromc);
        if ( fromc!='1' && fromc!='2' ) {
                clearmess();
                return;
        }
        switch ( fromc ) {
        case '1':
                copyall(bankvoice(0),Syndata);
                syntodisplay(Synindex);
                clearmess();
                message("Use the 'u'pload command to actually send the synth bank voices to the synth.");
                break;
        case '2':
                copyall(Syndata,bankvoice(0));
                libtodisplay(Libindex);
                clearmess();
                break;
        }
}

copyall(fromdata,todata)
char *fromdata;
char *todata;
{
        int n, v;

        for ( v=0; v<Nvoices; v++ )
                for ( n=0; n<Voicesize; n++ )
                        VOICEBYTE(todata,v,n) = VOICEBYTE(fromdata,v,n);
}

message(s)
char *s;
{
        windgoto(++Currrow,0);
        windstr(s);
        windrefresh();
}

setchan()
{
        int c;

        message("New MIDI channel --> ");
        windgets(Buff);
        if ( (c=atoi(Buff)) <= 0 || c > 16 ) {
                clearmess();
                message("Invalid channel!");
        }
        else {
                clearmess();
                Channel = c;
                showchan();
        }
}

showchan()
{
        windgoto(20,31);
        windstr("MIDI Channel: ");
        sprintf(Buff,"%d ",Channel);
        windstr(Buff);
        windrefresh();
}

/* read data from a file, filling the current library bank. */
readall()
{
        char fname[100];
        FILE *f;
        int v, n, r;

        message("File name --> ");
        windgets(fname);
        OPENBINFILE(f,fname,"r");
        if (f == NULL ) {
                sprintf(Buff,"Can't open '%s'!",fname);
                message(Buff);
                return;
        }
        /* Check the first byte.  If it's 0xdd, then the format is mine. */
        /* If the first byte is 0-31, it's also mine. */
        n = (getc(f) & 0xff);
        if ( n == 0xdd || n<=31 ) {
                if ( n <= 31 )
                        ungetc(n,f);
                /* This is my format, just raw data. */
                for ( v=0; v<Nvoices; v++ ) {
                        char *p = bankvoice(v);
                        for ( n=0; n<Voicesize; n++ )
                                *p++ = getc(f);
                }
                r = 0;
        }
        /* code for other formats would go here */
        else {
                message("Unknown file format");
                r = 1;
        }
        fclose(f);
        if ( r==0 ) {
                libtodisplay(Libindex=0);
                clearmess();
        }
}

/* write current library bank to a file */
writeall()
{
        char fname[100];
        FILE *f;
        int v, n;

        message("File name --> ");
        windgets(fname);
        OPENBINFILE(f,fname,"w");
        if ( f == NULL ) {
                sprintf(Buff,"Can't open '%s'!",fname);
                message(Buff);
                return;
        }
        putc(0xdd,f);   /* magic byte to identify my format */
        for ( v=0; v<Nvoices; v++ ) {
                char *p = bankvoice(v);
                for ( n=0; n<Voicesize; n++ )
                        putc(*p++,f);
        }
        fclose(f);
        clearmess();
}

/* draw main library/synth voice bank screen */
drawall()
{
        windclear();
        template();
        libtodisplay(Libindex);
        syntodisplay(Synindex);
        editto(Editrow,Editcol);
        pryankname();
        showchan();
}

/*
 * tosyn
 *
 * Store the given 'data' in in voice 'voicenum' (both in Syndata
 * AND on the synth itself).  If swap is non-zero, the voice is swapped
 * with the current voice in Syndata.
 */

tosyn(voicenum,data,swap)
char *data;
{
        int n, t;

        for ( n=0; n<Voicesize; n++ ) {
                if ( swap ) {
                        t = VOICEBYTE(Syndata,voicenum,n);
                        VOICEBYTE(Syndata,voicenum,n) = data[n];
                        data[n] = t;
                }
                else
                        VOICEBYTE(Syndata,voicenum,n) = data[n];
        }
}

tolib(voicenum,data,swap)
char *data;
{
        int n, t;
        char *p = bankvoice(voicenum);

        for ( n=0; n<Voicesize; n++ ) {
                if ( swap ) {
                        t = *p;
                        *p = data[n];
                        data[n] = t;
                }
                else
                        *p = data[n];
                p++;
        }
}

tocurrent(data,voicenum)
char *data;
int voicenum;
{
        int n;

        for ( n=0; n<Voicesize; n++ )
                Currdata[n] = VOICEBYTE(data,voicenum,n);
        (*Sendedit)(Currdata);
}

/*
 * readsynth
 *
 * Read a bulk dump from the synth, with some tolerance for errors.
 */
readsynth(data)
char *data;
{
        if ( Getbulk == NULL ) {
                message("That synth is unable to dump anything!!");
                return(1);
        }
        message("Trying to download data from synth...");
        if ( (*Getbulk)(data) == 0 ) {
                clearmess();
                return(0);
        }
        message("Unable to read data from synth!");
        sprintf(Buff,"Reason: %s",Reason);
        message(Buff);
        message("Perhaps connections are amiss?");
        return(1);
}

char *
vnumtext(n)
{
        static char vnbuff[6];

        if ( Numof == NULL ) {
                sprintf(vnbuff,"%2d",n);
                return(vnbuff);
        }
        else
                return((*Numof)(n - 1)); /* keep this 0-based */
}

/*
 * syntodisplay(n)
 *
 * Tranfer Syndata names to dialog boxes (Dxvoices) starting at n.
 */
syntodisplay(n)
{
        int k, r;

        /* for the NUMONSCREEN dialog boxes */
        for ( k=0; k<NUMONSCREEN; k++ ) {
                r = FIRSTROW+1+k;
                windgoto(r,LEFTSIDE);
                sprintf(Buff,"%-3s",vnumtext(n+k+1));
                windstr(Buff);
                windgoto(r,LEFTSIDE+6);
                windstr("                 ");

                /* pull the name out of the Syndata */
                windgoto(r,LEFTSIDE+6);
                windstr((*Nameof)( & (VOICEBYTE(Syndata,n+k,0) )) );
        }
        windrefresh();
}

/*
 * libtodisplay
 *
 * Tranfer Libdata names to the screen, starting at voice 'firstv'.
 */

libtodisplay(firstv)
{
        int k, r;

        windgoto(FIRSTROW-1,RIGHTSIDE-1);
        sprintf(Buff,"Library Voices (Bank %d)",Libbank+1);
        windstr(Buff);

        /* for the NUMONSCREEN dialog boxes */
        for ( k=0; k<NUMONSCREEN; k++ ) {
                r = FIRSTROW+1+k;
                windgoto(r,RIGHTSIDE);
                sprintf(Buff,"%-3s",vnumtext(firstv+k+1));
                windstr(Buff);
                windgoto(r,RIGHTSIDE+6);
                windstr("                 ");

                /* pull the name out of the Libdata */
                windgoto(r,RIGHTSIDE+6);
                windstr((*Nameof)(bankvoice(firstv+k)));
        }
        windrefresh();
}

allnotesoff()
{
        int n;

        /* Go through all channels. */
        for ( n=0; n<15; n++ ) {
                sendmidi(n | 0xb0);
                sendmidi(0x7b);
                sendmidi(0x00);
        }
}

/*
 * Below are generic edit routines, which manage a screen display
 * showing parameter values, and let you roam around and make changes.
 * The display is managed by the contents of the P array, which
 * contains the name, screen location, min/max values, etc. of
 * each parameter.
 */

int Prow = 0;
int Pcol = 0;
int Parm = 0;

/* redraw the parameter screen */
showallparms(name)
char *name;
{
        int n;
        char *s;

        windclear();

        showname(name);
        windgoto(1,0);
        for(n=strlen(name)+6;n>0;n--)
                windputc('=');

        /* The L array contains arbitrary screen labels */
        for ( n=0; L[n].l_text != NULL; n++ ) {
                windgoto(L[n].l_row,L[n].l_col);
                windstr(L[n].l_text);
        }
        /* Display each parameter value, and a label if there is one. */
        for ( n=0; P[n].p_name != NULL; n++ ) {
                if ( P[n].p_flags != 0 )
                        continue;
                if ( (s=P[n].p_label) != NULL )
                        showstr(s,P[n].p_lrow,P[n].p_lcol,0);
                showparam(n,0);
        }
        windrefresh();
}

showname(name)
char *name;
{
        windgoto(0,0);
        windstr("Name:                 ");
        windgoto(0,6);
        windstr(name);
}

showparam(n,eras)
{
        char *p;

        /* The p_tovis element of the P array is a function which, given */
        /* the parameter value as an argument, returns a string which is */
        /* what should be displayed on the screen. */
        p = (*(P[n].p_tovis))(P[n].p_val);
        showstr(p,P[n].p_vrow,P[n].p_vcol,eras);
}

showstr(p,row,col,eras)
register char *p;
register int col;
{
        register int c;

        windgoto(row,col);
        while ( (c=(*p++)) != '\0' ) {
                switch(c){
                case '~':
                        switch( (c=(*p++)) ) {
                        case 'u': row--; goto wgoto;
                        case 'd': row++; goto wgoto;
                        case 'l': col--; goto wgoto;
                        case 'r': col++;
                            wgoto:
                                windgoto(row,col);
                                break;
                        default:
                                windputc(eras?' ':c);
                                col++;
                                break;
                        }
                        break;
                default:
                        windputc(eras?' ':c);
                        col++;
                        break;
                }
        }
}

/* Allow roaming around and changing of parameter values. */
editdata(name,data)
char *name;
char *data;     /* vmem format */
{
        int c, n;

        windclear();
        windrefresh();
        /* enable all the parameters */
        for ( n=0; P[n].p_name != NULL; n++ )
                enableparm(n);

        /* Take the voice data and put it into P */
        (*Datain)(data);

        Prow = Pcol = 0;
        Changed = 0;
        Redraw = 1;
        gotoparm(CH_RIGHT);     /* Get to the first parameter */
        for ( ;; ) {
                if ( Redraw ) {
                        showallparms(name);
                        Redraw = 0;
                }
                windgoto(Prow,Pcol);
                windrefresh();
                c = mouseorkey();
                if ( c == MOUSE ) {
                        editmouse();
                        continue;
                }
                switch(c){
                case CH_RIGHT:
                case CH_UP:
                case CH_DOWN:
                case CH_LEFT:
                        gotoparm(c);
                        break;
                case CH_REDRAW:
                        showallparms(name);
                        break;
                case 'N':
                        /* Allow changing of voice name */
                        windgoto(0,5);
                        windstr("                ");
                        windgoto(0,6);
                        windrefresh();
                        windgets(Buff);
                        if ( Buff[0]!='\0' && Buff[0]!='\n' )
                                (*Setnameof)(data,Buff);
                        showname(name=(*Nameof)(data));
                        Changed = 1;
                        break;

                case CH_INC:
                        adjuparm(1);
                        break;
                case CH_INC2:
                        adjuparm(4);
                        break;
                case CH_INC3:
                        adjuparm(P[Parm].p_max - P[Parm].p_min);
                        break;
                case CH_DEC:
                        adjuparm(-1);
                        break;
                case CH_DEC2:
                        adjuparm(-4);
                        break;
                case CH_DEC3:
                        adjuparm(P[Parm].p_min - P[Parm].p_max);
                        break;
#ifdef OLDSTUFF
                case 'a':
                        sendaced(data);
                        playnote(0);
                        break;
#endif
                case ' ':
                case '\n':
#ifndef macintosh
                case '\r':
#endif
                        if ( Changed ) {
                                (*Dataout)(data);
                                (*Sendedit)(data);
                        }
                        playnote(0);
                        break;
                case '\033':
                case '`':
                        allnotesoff();
                        break;
                case 'q':
                case EOF:
                        if ( Changed ) {
                                (*Dataout)(data);
                                (*Sendedit)(data);
                        }
                        return;
                default:
                        break;
                }
        }
}

adjuparm(incdec)
{
        int v, n;

        v = P[Parm].p_val + incdec;
        if ( v < (n=P[Parm].p_min) )
                v = n;
        if ( v > (n=P[Parm].p_max) )
                v = n;
        Changed = 1;
        showparam(Parm,1);      /* erase the old val */
        P[Parm].p_val = v;
        showparam(Parm,0);      /* show the new val */
}

editmouse()
{
        int row, col, thisparm;

        getmouse(&row,&col);
        thisparm = closeparm(row,col);
        if ( thisparm == Parm ) {
                if ( statmouse() > 1 )
                        adjuparm(-1);   /* right button */
                else if ( statmouse() == 1 ) /* added by mab - bug fix */
                        adjuparm(1);    /* left button */
        }
        else {
                Parm = thisparm;
                Prow = P[Parm].p_vrow;
                Pcol = P[Parm].p_vcol;
        }
}

/* closeparm - Find the closest parameter */
closeparm(row,col)
{
        register struct paraminfo *pp;
        register int n;
        int dist, mindist, minparm, dr, dc;

        minparm = 0;
        mindist = Rows + Cols;
        for ( n=0,pp=P; pp->p_name != NULL; n++,pp++ ) {
                if ( pp->p_flags != 0 )
                        continue;
                if ( (dr=row-(pp->p_vrow)) < 0 )
                        dr = -dr;
                if ( (dc=col-(pp->p_vcol)) < 0 )
                        dc = -dc;
                if ( (dist=dr*dr+dc*dc) < mindist ) {
                        minparm = n;
                        mindist = dist;
                }
        }
        return(minparm);
}

/* playnote - play the 'auto' note */
playnote(i)
{
        int pitch, vol, dur, chan;
        long endtime;

        pitch = getval("autopitch");
        if(i == 0) { /* called from inside edit-mode */
                chan = getval("autochan");
        } else { /* called from the top level */
                chan = Channel;
        }
        vol = getval("autovol");
        dur = getval("autodur");
        endtime = milliclock() + dur * 100;
        midinote(1,chan,pitch,vol);
        while ( milliclock() < endtime )
                ;
        midinote(0,chan,pitch,vol);
}

/* gotoparm - search for the next parameter in the specified direction */
gotoparm(dir)
{
        int n, k, inc, pm, orig, r = Prow, c = Pcol;

        if ( dir==CH_LEFT || dir==CH_RIGHT ) {
                if ( dir==CH_LEFT )
                        c--;
                else
                        c++;
                orig = c;
                inc = 0;
                pm = -1;
                /* look up and down, alternately */
                for ( n=2*Rows; n>0; n-- ) {
                        r += (pm * inc++);
                        pm = -pm;
                        if ( r < 0 || r >= Rows )
                                continue;
                        if ( dir == CH_LEFT ) {
                                for ( c=orig; c>=0; c-- ) {
                                        if ( parmat(r,c) )
                                                return;
                                }
                        }
                        else {
                                for ( c=orig; c<Cols; c++ ) {
                                        if ( parmat(r,c) )
                                                return;
                                }
                        }
                }
                return;
        }
        if ( dir==CH_DOWN || dir==CH_UP ) {
                if ( dir==CH_DOWN )
                        r++;
                else
                        r--;
                orig = c;
                while ( r >= 0 && r < Rows ) {
                        /* look toward both sides at the same time */
                        inc = 0;
                        pm = -1;
                        for ( k=2*Cols; k>0; k-- ) {
                                c += (pm * inc++);
                                pm = -pm;
                                if ( c < 0 || c >= Cols )
                                        continue;
                                if ( parmat(r,c) )
                                        return;
                        }
                        if ( dir==CH_DOWN )
                                r++;
                        else
                                r--;
                        c = orig;
                }
                return;
        }
}

/* paramat - return non-zero if a parameter value is at position r,c */
parmat(r,c)
register int r, c;
{
        register int n;
        register struct paraminfo *pp;

        for ( n=0,pp=P; pp->p_name != NULL; n++,pp++ ) {
                if ( pp->p_flags != 0 )
                        continue;
                if ( pp->p_vrow==r && pp->p_vcol==c ) {
                        Prow = r;
                        Pcol = c;
                        Parm = n;
                        return(1);
                }
        }
        return(0);
}

/* parmindex - return index (in P) of a given parameter name. */
parmindex(name)
char *name;
{
        int n;
        char *s;

        for ( n=0; (s=P[n].p_name) != NULL; n++ ) {
                if ( strcmp(s,name) == 0 )
                        return(n);
        }
        sprintf(Buff,"HEY, PARMINDEX(%s) NOT FOUND!",name);
        windstr(Buff);
        windrefresh();
        return(-1);
}

setval(name,v)
char *name;
{
        int n;

        if ( (n=parmindex(name)) < 0 )
                return;
        P[n].p_val = v;
}

getval(name)
char *name;
{
        int n;

        if ( (n=parmindex(name)) < 0 )
                return(0);
        return(P[n].p_val);
}

enableparm(n)
{
        if ( P[n].p_flags != 0 )
                P[n].p_flags = 0;
}

disableparm(n)
{
        if ( P[n].p_flags == 0 )
                P[n].p_flags = 1;
}

midinote(onoff,chan,pitch,vol)
{
        sendmidi( ((onoff==1)?(0x90):(0x80)) | ((chan-1)&0xf) );
        sendmidi( pitch & 0x7f );
        sendmidi( vol & 0x7f );
}

static char Nbuff[16];

char *visnum(v)
{
        sprintf(Nbuff,"%d",v);
        return(Nbuff);
}
char *visonoff(v)
{
        if ( v==0 )
                return("off");
        else
                return("on");
}

char *
bankvoice(voice)
{
        int offset = Libbank * Nvoices * Voicesize + voice * Voicesize;
        return(Libdata + offset);
}
