/* * GLIB - a Generic LIBrarian and editor for synths */ #include "glib.h" #include 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 "); 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 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; n0 ) 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 - 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 "); 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 "); 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; v0;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= 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); }