/* Having received little shareware money ($0), and desiring more features but
   lacking time to implement them, I hereby post this source code.  Please
   fix it up as you wish, documenting your changes and putting the updates
   on ftp site 128.54.16.1 in midi/patches/D10/.  Mail ttl@cs.wisc.edu with
   your notice of improvements.  Thanks. */

/* Compile with the CXL window package library, available from simtel
   or 128.252.135.4 in .../mouse?  or .../c?  (I forgot.) */

static char *ProgVer =
   "MIDIMENU, Copyright July 1991 by Tony Laundrie, Version 1.0";
static char *RegInfo =
   "MIDIMENU is Shareware.  If you like it, send 25 dollars to Tony Laundrie, 1005 Tompkins Drive, Madison, WI 53716";
   
/*1234567890123456789012345678901234567^^01234567890123456789012345678901234567+
0MULTI   TONE               VOLUME                BALANCE     REV SHF TUN BN AS|
1 a-23 AcousticPi  ------------I------------- -------I-------  *  +12 -50 24  1|
2 a-23 AcousticPi  -----------------I-------- -------I-------  *   -   -  24  1|
3 a-23 AcousticBa  --------------------I----- -------I-------  -   -   -  24  1|
4 a-23 Fantasy     -----------------I-------- -------I-------  *   -   -  24  1|
5 a-23 Accordian   -----I-------------------- -------I-------  -   -   -  24  1|
6 a-23 Fretless2   -----------------I-------- -----I---------  -   -   -  24  1|
7 r-22 Recorder    --I----------------------- -----I---------  *   -   -  24  1|
8 i-23 Attack Att  -----I-------------------- -----I---------  -   -   -  24  1|
9------------------------------------------------------------------------------+
0 r-34 AcousticPi  --------------I-----------     MIX: 50 50   -   -   -  12  1|
1 i-24 AcousticPi  a-43 Patch Name, Sirs  SPL: C#3  M: Split   -   -   -   1  1|
2----------------------------------------------+-------------------------------+
3   RHYTHM VOLUME  ---------I----------------  |  REVERB TYPE: Medium Room     |
4----------------------------------------------+  LEVEL ------I- TIME ----I--- |
5 Enter file name (Alt-D = Dir): abcdefgh.syx  +-------------------------------+
6                                                                              |
7                                         ^TIMBS WRITE  TIMBS SAVE  ALL SAVE   |
8                                         ^PATCH WRITE  PATCH SAVE  LOAD FILE  |
9                                                                   INITIALIZE |
0                                                                              |
1                     MIDIMENU FOR ROLAND D-5/10/20, by TTL               EXIT |
 01234567890123456789012345678901234567^^01234567890123456789012345678901234567+
*/


#include <conio.h>
#include <ctype.h>
#include <dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dir.h>

#include "cxldef.h"
#include "cxlkey.h"
#include "cxlmou.h"
#include "cxlstr.h"
#include "cxlvid.h"
#include "cxlwin.h"

#define PLAIN	LGREY|_BLACK
#define BOLD	WHITE|_BLACK
#define INVERSE	BLACK|_LGREY

#define NOWRAP	0
#define WRAP	1
#define TOGGLE	2

#define uc unsigned char

typedef struct itn {
   struct _item_t *citem;	/* -> row, col, str */
   uc max;			/* max for the value */
   uc mode;			/* wrap, nowrap, toggle */
   uc arg;			/* index argument for similar items */
   void (*v2s) ();		/* converts value to string (v, s) */
   void (*midg) ();		/* gets value from d20  (&v, arg) */
   void (*midp) ();		/* puts value into d20  (v, arg) */
   struct itn *next;
} ITN;

ITN *firstitn;

int *savescrn, crow, ccol;

int tagid = 0;
int hitinh = 0;
int D5 = 0;

/*****     D20 parameters     *****/

/* available tones */
char tonesstr[256][16] = {
   "a-11 AcouPiano1", "a-12 AcouPiano2", "a-13 AcouPiano3", "a-14 Honky-Tonk",
   "a-15 ElecPiano1", "a-16 ElecPiano2", "a-17 ElecPiano3", "a-18 ElecPiano4",
   "a-21 ElecOrgan1", "a-22 ElecOrgan2", "a-23 ElecOrgan3", "a-24 ElecOrgan4",
   "a-25 PipeOrgan1", "a-26 PipeOrgan2", "a-27 PipeOrgan3", "a-28 Accordian ",
   "a-31 Harpsi 1  ", "a-32 Harpsi 2  ", "a-33 Harpsi 3  ", "a-34 Clav 1    ",
   "a-35 Clav 2    ", "a-36 Clav 3    ", "a-37 Celesta 1 ", "a-38 Celesta 2 ",
   "a-41 Violin 1  ", "a-42 Violin 2  ", "a-43 Cello 1   ", "a-44 Cello 2   ",
   "a-45 Contrabass", "a-46 Pizzicato ", "a-47 Harp 1    ", "a-48 Harp 2    ",
   "a-51 Strings 1 ", "a-52 Strings 2 ", "a-53 Strings 3 ", "a-54 Strings 4 ",
   "a-55 Brass 1   ", "a-56 Brass 2   ", "a-57 Brass 3   ", "a-58 Brass 4   ",
   "a-61 Trumpet 1 ", "a-62 Trumpet 2 ", "a-63 Trombone 1", "a-64 Trombone 2",
   "a-65 Horn      ", "a-66 Fr Horn   ", "a-67 Engl Horn ", "a-68 Tuba      ",
   "a-71 Flute 1   ", "a-72 Flute 2   ", "a-73 Piccolo   ", "a-74 Recorder  ",
   "a-75 Pan Pipes ", "a-76 Bottleblow", "a-77 Breathpipe", "a-78 Whistle   ",
   "a-81 Sax 1     ", "a-82 Sax 2     ", "a-83 Sax 3     ", "a-84 Clarinet 1",
   "a-85 Clarinet 2", "a-86 Oboe      ", "a-87 Bassoon   ", "a-88 Harmonica ",
   "b-11 Fantasy   ", "b-12 Harmo Pan ", "b-13 Chorale   ", "b-14 Glasses   ",
   "b-15 Soundtrack", "b-16 Atmosphere", "b-17 Warm Bell ", "b-18 Space Horn",
   "b-21 Echo Bell ", "b-22 Ice Rains ", "b-23 Oboe 2002 ", "b-24 Echo Pan  ",
   "b-25 Bell Swing", "b-26 Reso Synth", "b-27 Steam Pad ", "b-28 VibeString",
   "b-31 Syn Lead 1", "b-32 Syn Lead 2", "b-33 Syn Lead 3", "b-34 Syn Lead 4",
   "b-35 Syn Bass 1", "b-36 Syn Bass 2", "b-37 Syn Bass 3", "b-38 Syn Bass 4",
   "b-41 AcouBass 1", "b-42 AcouBass 2", "b-43 ElecBass 1", "b-44 ElecBass 2",
   "b-45 SlapBass 1", "b-46 SlapBass 2", "b-47 Fretless 1", "b-48 Fretless 2",
   "b-51 Vibe      ", "b-52 Glock     ", "b-53 Marimba   ", "b-54 Xylophone ",
   "b-55 Guitar 1  ", "b-56 Guitar 2  ", "b-57 Elec Gtr 1", "b-58 Elec Gtr 2",
   "b-61 Koto      ", "b-62 Shamisen  ", "b-63 Jamisen   ", "b-64 Sho       ",
   "b-65 Shakuhachi", "b-66 WadaikoSet", "b-67 Sitar     ", "b-68 Steel Drum",
   "b-71 Tech Snare", "b-72 Elec Tom  ", "b-73 Revrse Cym", "b-74 Ethno Hit ",
   "b-75 Timpani   ", "b-76 Triangles ", "b-77 Wind Bell ", "b-78 Tube Bell ",
   "b-81 Orche Hit ", "b-82 Bird Tweet", "b-83 OneNoteJam", "b-84 Telephone ",
   "b-85 Typewriter", "b-86 Insect    ", "b-87 WaterBells", "b-88 JungleTune",
   "r-11 ClsdHiHat1", "r-12 ClsdHiHat2", "r-13 OpenHiHat1", "r-14 OpenHiHat2",
   "r-15 Crash Cym ", "r-16 Short Crsh", "r-17 Mute Crash", "r-18 Ride Cym  ",
   "r-21 Short Ride", "r-22 Mute Ride ", "r-23 Cup       ", "r-24 Mute Cup  ",
   "r-25 China Cym ", "r-26 Splash Cym", "r-27 BassDrum 1", "r-28 BassDrum2 ",
   "r-31 BassDrum3 ", "r-32 BassDrum4 ", "r-33 SnareDrum1", "r-34 SnareDrum2",
   "r-35 SnareDrum3", "r-36 SnareDrum4", "r-37 SnareDrum5", "r-38 SnareDrum6",
   "r-41 Rim Shot  ", "r-42 Brush 1   ", "r-43 Brush 2   ", "r-44 Hi TomTom1",
   "r-45 MidTomTom1", "r-46 LowTomTom1", "r-47 Hi TomTom2", "r-48 MidTomTom2",
   "r-51 LowTomTom2", "r-52 Hi TomTom3", "r-53 MidTomTom3", "r-54 LowTomTom3",
   "r-55 HiPitchTT1", "r-56 HiPitchTT2", "r-57 Hand Clap ", "r-58 Tambourine",
   "r-61 Cowbell   ", "r-62 Hi Bongo  ", "r-63 Low Bongo ", "r-64 Mt H Conga",
   "r-65 High Conga", "r-66 Low Conga ", "r-67 Hi Timbale", "r-68 Lo Timbale",
   "r-71 Hi Agogo  ", "r-72 Lo Agogo  ", "r-73 Cabasa    ", "r-74 Maracas   ",
   "r-75 ShortWhisl", "r-76 Long Whisl", "r-77 Quijada   ", "r-78 Claves    ",
   "r-81 Castanets ", "r-82 Triangle  ", "r-83 Wood Block", "r-84 Bell      ",
   "r-85 NativeDrm1", "r-86 NativeDrm2", "r-87 NativeDrm3", "r-88 OFF       "
   /* the remaining 64 are user-programmable */
};
char *tones[257];

/* available patches */
char patchesstr[128][22];
char *patches[129];

/* available split points */
static char *splitpstr[63] = {
    "C 2","C#2","D 2","D#2","E 2","F 2","F#2","G 2","G#2","A 2","A#2","B 2",
    "C 3","C#3","D 3","D#3","E 3","F 3","F#3","G 3","G#3","A 3","A#3","B 3",
    "C 4","C#4","D 4","D#4","E 4","F 4","F#4","G 4","G#4","A 4","A#4","B 4",
    "C 5","C#5","D 5","D#5","E 5","F 5","F#5","G 5","G#5","A 5","A#5","B 5",
    "C 6","C#6","D 6","D#6","E 6","F 6","F#6","G 6","G#6","A 6","A#6","B 6",
    "C 7","C#7", NULL
};

static char *revtypestr[10] = { "Small Room ", "Medium Room", "Medium Hall",
      "Large Hall ", "Plate      ", "Delay 1    ", "Delay 2    ", "Delay 3    ",
      "Off        ", NULL
};

/*****     FIX     *****/

w_prints (r, c, m, s)
int r, c, m;
char *s;
{
   if (_mouse) mshidecur ();
   wprints (r, c, m, s);
   if (_mouse) msshowcur ();
}

/*****     LOW-LEVEL MIDI I/O     *****/

int DATAPORT = 0x330;
int COMDPORT = 0x331;
int STATPORT = 0x331;
#define   DRR        0x40	/* Mask for Data Receive Ready Bit */
#define   DSR        0x80	/* Mask for Data Send Ready Bit */
#define   MPURESET   0xFF	/* MPU-401 Total Reset Command */
#define   UARTMODE   0x3F	/* MPU-401 Dumb UART Mode */

void nonfatalerr (s)
char *s;
{
   beep ();
   wopen (3, 10, 5, 67, 1, BOLD, BOLD);
   wcenters (0, BOLD, s);
   delay (2000);
   wclose ();
}

void fatalerr (s)
char *s;
{
   beep ();
   wopen (3, 10, 5, 67, 1, BOLD, BOLD);
   wcenters (0, BOLD, s);
   delay (5000);
   srestore (savescrn);
   gotoxy_ (crow, ccol);
   showcur ();
   exit (0);
}

void midout (n)
uc n;
{
   while (inp (STATPORT) & DRR) ;	/* Wait until it's ready */
   outp (DATAPORT, (unsigned int) n );
}

#define TIMEOUT 5

extern unsigned long time (unsigned long *t);

uc midin ()
{
   uc i;
   unsigned long d, e;

   e = time (&d);
   do {
      while ((inp (STATPORT) & DSR) && (time (&e) - d <= TIMEOUT)) ;
      if (e - d > TIMEOUT) {
         fatalerr ("Timeout -- no data received");
      }
      i = inp (DATAPORT);
   } while ((i == 0xf8 || i == 0xfe) && (time (&e) - d <= TIMEOUT)) ;
   if (e - d > TIMEOUT) {
      fatalerr ("Timeout -- only clock/sense received");
   }
   return (i);
}

void midreset()
{
   outp( COMDPORT, MPURESET );		/* Send MPU-401 RESET Command */
   while (inp (STATPORT) & DRR) ;	/* Wait until it's ready */
   outp( COMDPORT, UARTMODE );		/* Set MPU-401 "Dumb UART" Mode */
   while (inp (STATPORT) & DRR) ;	/* Wait until it's ready */
   midout (0);				/* Dummy write */
}

void get_midi (a2, a1, a0, n, dat)
uc a2, a1, a0, n, *dat;
{
   static uc rqdmesshead[] = {0xf0, 0x41, 0x10, 0x16, 0x11};
   uc a2i, a1i, a0i;
   unsigned int i;

   for (i = 0; i < 5; ++i)
      midout (rqdmesshead[i]);

   midout (a2);  midout (a1);  midout (a0);  midout (0);  midout (0);
   midout (n);
   i = (-a2 - a1 - a0 - n) & 0x7f;
   midout (i); midout (0xf7);

   while (midin () != 0xf0) ;	/* put a timeout here */
   midin ();  midin ();  midin ();
   if (midin () != 0x12)
      fatalerr ("Expected 0x12");
   a2i = midin ();
   a1i = midin ();
   a0i = midin ();
   if (a2i != a2 || a1i != a1 || a0i != a0)
      fatalerr ("Wrong address");
   for (i = 0; i < n; ++i) {
      dat[i] = midin ();
   }

   /* reads of any size from temp patch area return everything from a2.a1.a0
       to the end of patch area.  So just gobble up rest of sysex message.
       Midin() timeout will catch errors. */
   while (midin () != 0xf7) ;	/* put a timeout here */
}

static uc dt1messhead[] = {0xf0, 0x41, 0x10, 0x16, 0x12};
   
void put_midi (a2, a1, a0, n, dat)
uc a2, a1, a0, n, *dat;
{
   unsigned int i, j;

   for (i = 0; i < 5; ++i)
      midout (dt1messhead[i]);
   midout (a2);  midout (a1);  midout (a0);
   j = a2 + a1 + a0;
   for (i = 0; i < n; ++i) {
      midout (dat[i]);
      j = j + dat[i];
   }
   midout ((-j) & 0x7f);
   midout (0xf7);
}

void d20display (s)
char *s;
{
   char buf[33];

   sprintf (buf, "%-32s", s);
   put_midi (0x20, 0, 0, 32, buf);
}

static char *clrbuf = "                                                                             ";

void midg_names ()
{
   unsigned int i, j;
   uc a2, a1, a0;

   wcenters (4, BOLD|BLINK, "RESETTING MIDI");
   midreset ();
   wcenters (4, BOLD|BLINK, "INITIALIZING...    If you like MIDIMENU, please register.");

   d20display ("Reading synth     Parameters...");

   /* Read Available Patch Names, set up patches array */
   for (i = 0; i < 128; ++i) {
      patches[i] = &(patchesstr[i][0]);
      patchesstr[i][0] = 'A' + (i >> 6);
      patchesstr[i][1] = '-';
      patchesstr[i][2] = '1' + ((i & 0x38) >> 3);
      patchesstr[i][3] = '1' + (i & 0x7);
      patchesstr[i][4] = ' ';
      j = i * 0x26 + 0x15;
      a0 = (j & 0x7f);
      a1 = j >> 7;
      get_midi (0x07, a1, a0, 16, &(patchesstr[i][5]));
      patchesstr[i][21] = '\0';
   }

   /* set up tones array */
   for (i = 0; i < 256; ++i)
      tones[i] = &(tonesstr[i][0]);

   /* read programmable tone names */
   for (i = 0; i < 64; ++i) {
      char *q;
      q = &(tonesstr[192+i][0]);
      q[0] = 'i';
      q[1] = '-';
      q[2] = '1' + (i >> 3);
      q[3] = '1' + (i & 0x7);
      q[4] = ' ';
      get_midi (0x08, 0x02 * i, 0x00, 10, &(tonesstr[192+i][5]));
      q[15] = '\0';
   }
   tones[256] = patches[128] = NULL;

   wcenters (4, PLAIN, clrbuf);
}

/*****     KEYPRESSES     *****/

char gotyes ()
{
   uc c;

   clearkeys();
   while ((c = getxch()) != 'y' && c != 'n' && c != 'Y' && c != 'N') ;
   return (c == 'y' || c == 'Y');
}

void byebye ()
{
   uc c;

   hitinh = 1;
   if (_mouse) mshidecur ();
   wopen (3, 23, 5, 54, 1, BOLD, BOLD);
   beep ();
   wcenters (0, PLAIN, "EXIT: Are you sure? (y or n)");
   if (gotyes ()) {
      srestore (savescrn);
      gotoxy_ (crow, ccol);
      showcur ();
      d20display ("Have a nice day!");
      fprintf (stderr, "If you like MIDIMENU, please register your copy with the author.\n");
      exit (0);
   }
   wclose ();
   if (_mouse) msshowcur ();
   hitinh = 0;
}

ITN *cit2itn (citem)
struct _item_t *citem;
{
   ITN *t;
   t = firstitn;
   while (t != NULL && t->citem != citem)
      t = t->next;
   return (t);
}

void v2s_patch ();	/* it appears later */

void hitkey (k)
uc k;
{
   uc v, i;
   struct _item_t *citem;
   ITN *tempitn;
   char buf[20];

   if (hitinh)
      return;
   citem = wmenuicurr();
   tempitn = cit2itn (citem);
   if (tempitn == NULL)
      return;
   hitinh = 1;
   if (k == '.' && tempitn->v2s == v2s_patch) {
      if (_mouse) mshidecur ();
      wprints (11, 23, PLAIN, "                ");
      wgotoxy (11, 23);
      clearkeys ();
      showcur ();
      wgetns (buf, 16);
      hidecur ();
      for (i = strlen(buf); i < 16; ++i)
         buf[i] = ' ';
      buf[16] = '\0';
      put_midi (0x03, 0x04,  0x15, 16, buf);
      strcpy (tempitn->citem->str + 5, buf);
      wprints (11, 23, INVERSE, buf);
      if (_mouse) msshowcur ();
      hitinh = 0;
      return;
   }
   if (k != '*' && k != '/') {
      tempitn->midg (&v, tempitn->arg);
   }
   if (tempitn->mode == TOGGLE && k != ',') {
      v = !v;
   }
   else if (k == '+') {
      if (v == tempitn->max) {
         if (tempitn->mode == WRAP)
            v = 0;
      }
      else
         ++v;
   }
   else if (k == '-') {
      if (v == 0) {
         if (tempitn->mode == WRAP)
            v = tempitn->max;
      }
      else
         --v;
   }
   else if (k == '*') {
      v = tempitn->max;
   }
   else if (k == '/') {
      v = 0;
   }

   if (k != ',') {
      /* write it, then reread to see if you did anything */
      tempitn->midp (v, tempitn->arg);
      tempitn->midg (&v, tempitn->arg);
   }
   tempitn->v2s (v, citem->str);
   w_prints (citem->wrow, citem->wcol, INVERSE, citem->str);
   hitinh = 0;
}

void hitplus () {hitkey ('+');} void hitminus () {hitkey ('-');}
void hitdiv () {hitkey ('/');}  void hittimes () {hitkey ('*');}
void hitcom () {hitkey (',');}  void hitdot () {hitkey ('.');}

/*****     ITN FUNCTIONS FOR EACH PARAMETER     *****/

/* split mode */

void v2s_splmode (v, s)
uc v, *s;
{
   static char *splmodestr[3] = {"M: Whole", "M: Dual ", "M: Split"};
   strcpy (s, splmodestr[v]);
}

void midg_splmode (v, arg)
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x00, 1, v);
}

void midp_splmode (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x00, 1, &v);
}

/* split point */

void v2s_splitp (v, s)
uc v, *s;
{
   strcpy (s, "SPL: ");
   strcpy (s+5, splitpstr[v]);
}

void midg_splitp (v, arg)
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x01, 1, v);
}

void midp_splitp (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x01, 1, &v);
}

/* reverb type */

void v2s_revtype (v, s)
uc v, *s;
{
   strcpy (s, revtypestr[v]);
}

void midg_revtype (v, arg)
uc *v, arg;
{
   if (D5) {*v = 0; return;}
   get_midi (0x10, 0x00, 0x01, 1, v);
}

void midp_revtype (v, arg)
uc v, arg;
{
   put_midi (0x10, 0x00, 0x01, 1, &v);
}

/* all sliders */

void v2s_slider (v, s)
uc v, *s;
{
   char *p;
   for (p = s; *p != '\0'; ++p)
      *p = '-';
   s[v] = 'I';
}

void ret_slider ()
{
   struct _item_t *citem;
   int b, r, c;
   uc v;
   ITN *tempitn;

   if (_mouse) {
      msstatus (&b, &r, &c);
      citem = wmenuicurr ();
      b = c - citem->wcol - 1;
      if (b >= 0 && b < strlen (citem->str)) {
         v = b;
         tempitn = cit2itn (citem);
         tempitn->midp (v, tempitn->arg);
         tempitn->midg (&v, tempitn->arg);
         v2s_slider (v, citem->str);
         w_prints (citem->wrow, citem->wcol, INVERSE, citem->str);
      }
   }
}

/* reverb level */

void midg_revlev (v, arg)
uc *v, arg;
{
   if (D5) {*v = 0; return;}
   get_midi (0x10, 0x00, 0x03, 1, v);
}

void midp_revlev (v, arg)
uc v, arg;
{
   put_midi (0x10, 0x00, 0x03, 1, &v);
}

/* reverb time */

void midg_revtime (v, arg)
uc *v, arg;
{
   if (D5) {*v = 0; return;}
   get_midi (0x10, 0x00, 0x02, 1, v);
}

void midp_revtime (v, arg)
uc v, arg;
{
   put_midi (0x10, 0x00, 0x02, 1, &v);
}

/* reverb stats */

void v2s_revstat (v, s)
uc v, *s;
{
   if (v)
      *s = '*';
   else
      *s = '-';
}

void midg_revstatm (v, arg)	/* arg = 0-7 */
uc *v, arg;
{
   if (D5) {*v = 0; return;}
   get_midi (0x03, 0x00, 0x10 * arg + 0x06, 1, v);
}

void midp_revstatm (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x00, 0x10 * arg + 0x06, 1, &v);
}

void midg_revstatp (v, arg)	/* arg = 0 (upper) or 1 (lower) */
uc *v, arg;
{
   if (D5) {*v = 0; return;}
   get_midi (0x03, 0x04, 0x0f - arg, 1, v);
}

void midp_revstatp (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x0f - arg, 1, &v);
}

/* key shifts */

void v2s_keyshf (v, s)
uc v, *s;
{
   if (v == 24)
      strcpy (s, " 0 ");
   else if (v < 24)
      sprintf (s, "-%-2d", 24 - v);
   else
      sprintf (s, "+%-2d", v - 24);
}

void midg_keyshfm (v, arg)	/* arg = 0-7 */
uc *v, arg;
{
   get_midi (0x03, 0x00, 0x10 * arg + 0x02, 1, v);
}

void midp_keyshfm (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x00, 0x10 * arg + 0x02, 1, &v);
}

void midg_keyshfp (v, arg)	/* arg = 0 (upper) or 1 (lower) */
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x07 - arg, 1, v);
}

void midp_keyshfp (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x07 - arg, 1, &v);
}

/* fine tuning */

void v2s_tune (v, s)
uc v, *s;
{
   if (v == 50)
      strcpy (s, " 0 ");
   else if (v < 50)
      sprintf (s, "-%-2d", 50 - v);
   else
      sprintf (s, "+%-2d", v - 50);
}

void midg_tunem (v, arg)	/* arg = 0-7 */
uc *v, arg;
{
   get_midi (0x03, 0x00, 0x10 * arg + 0x03, 1, v);
}

void midp_tunem (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x00, 0x10 * arg + 0x03, 1, &v);
}

void midg_tunep (v, arg)	/* arg = 0 (upper) or 1 (lower) */
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x09 - arg, 1, v);
}

void midp_tunep (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x09 - arg, 1, &v);
}

/* bender range */

void v2s_bender (v, s)
uc v, *s;
{
   sprintf (s, "%2d", v);
}

void midg_benderm (v, arg)	/* arg = 0-7 */
uc *v, arg;
{
   get_midi (0x03, 0x00, 0x10 * arg + 0x04, 1, v);
}

void midp_benderm (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x00, 0x10 * arg + 0x04, 1, &v);
}

void midg_benderp (v, arg)	/* arg = 0 (upper) or 1 (lower) */
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x0b - arg, 1, v);
}

void midp_benderp (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x0b - arg, 1, &v);
}

/* assign mode */

void v2s_assign (v, s)
uc v, *s;
{
    sprintf (s, "%2d", v+1);
}

void midg_assignm (v, arg)	/* arg = 0-7 */
uc *v, arg;
{
   get_midi (0x03, 0x00, 0x10 * arg + 0x05, 1, v);
}

void midp_assignm (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x00, 0x10 * arg + 0x05, 1, &v);
}

void midg_assignp (v, arg)	/* arg = 0 (upper) or 1 (lower) */
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x0d - arg, 1, v);
}

void midp_assignp (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x0d - arg, 1, &v);
}

/* balances */

void midg_bal (v, arg)		/* arg = 0-7 */
uc *v, arg;
{
   get_midi (0x10, 0x00, 0x2a + arg, 1, v);
}

void midp_bal (v, arg)
uc v, arg;
{
   put_midi (0x10, 0x00, 0x2a + arg, 1, &v);
}

/* volumes */

void midg_volm (v, arg)		/* arg = 0-8 */
uc *v, arg;
{
   get_midi (0x10, 0x00, 0x21 + arg, 1, v);
   *v = *v / 4;
}

void midp_volm (v, arg)
uc v, arg;
{
   v = v*4;
   put_midi (0x10, 0x00, 0x21 + arg, 1, &v);
}

void midg_volp (v, arg)
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x14, 1, v);
   *v = *v / 4;
}

void midp_volp (v, arg)
uc v, arg;
{
   v = v*4;
   put_midi (0x03, 0x04, 0x14, 1, &v);
}

void v2s_mix (v, s)
uc v, *s;
{
   char buf[12];
   sprintf (buf, "MIX: %d %d", v, 100-v);
   sprintf (s, "%-10s", buf);
}

void midg_mix (v, arg)
uc *v, arg;
{
   get_midi (0x03, 0x04, 0x13, 1, v);
}

void midp_mix (v, arg)
uc v, arg;
{
   put_midi (0x03, 0x04, 0x13, 1, &v);
}

void v2s_tone (v, s)
uc v, *s;
{
   strcpy (s, tones[v]);
}

void midg_tonem (v, arg)	/* arg = 0-7 */
uc *v, arg;
{
   uc bank, num;
   get_midi (0x03, 0x00, 0x10 * arg + 0x00, 1, &bank);
   get_midi (0x03, 0x00, 0x10 * arg + 0x01, 1, &num);
   if (bank > 1)
      bank = bank ^ 1;
   *v = (bank << 6) | num;
}

void midg_tonep (v, arg)	/* arg = 0 (upper) or 1 (lower) */
uc *v, arg;
{
   uc bank, num;
   get_midi (0x03, 0x04, 0x04-2*arg, 1, &bank);
   get_midi (0x03, 0x04, 0x05-2*arg, 1, &num);
   if (bank > 1)
      bank = bank ^ 1;
   *v = (bank << 6) | num;
}

void midp_tonem (v, arg)
uc v, arg;
{
   uc bank, num;
   bank = v >> 6;
   if (bank > 1)
      bank = bank ^ 1;
   num = v & 0x3f;
   put_midi (0x03, 0x00, 0x10 * arg + 0x00, 1, &bank);
   put_midi (0x03, 0x00, 0x10 * arg + 0x01, 1, &num);
}

void midp_tonep (v, arg)
uc v, arg;
{
   uc bank, num;
   bank = v >> 6;
   if (bank > 1)
      bank = bank ^ 1;
   num = v & 0x3f;
   put_midi (0x03, 0x04, 0x04-2*arg, 1, &bank);
   put_midi (0x03, 0x04, 0x05-2*arg, 1, &num);
}

void v2s_patch (v, s)
uc v, *s;
{
   strcpy (s, patches[v]);
}

void midg_patch (v, arg)
uc *v, arg;
{
   char buf[20];

   get_midi (0x03, 0x04, 0x15, 16, buf);
   for (*v = 0; *v < 128 && strncmp (buf, patchesstr[*v]+5, 16); ++(*v)) ;
   if (*v >= 128)
      *v = 0;
}

void midp_patch (v, arg)
uc v, arg;
{
   ITN *t;
   char buf[0x26];
   unsigned int i;
   uc a1, a0;

   i = v * 0x26;
   a1 = i >> 7;
   a0 = i & 0x7f;
   get_midi (0x07,   a1, a0, 0x26, buf);
   put_midi (0x03, 0x04,  0, 0x26, buf);

   t = firstitn;
   while (t != NULL && t->midp != midp_patch)
      t = t->next;
   t = t->next;
   while (t != NULL) {
      t->midg (&v, t->arg);
      t->v2s (v, t->citem->str);
      w_prints (t->citem->wrow, t->citem->wcol, PLAIN, t->citem->str);
      t = t->next;
   }
}

void ret_pickstr (strs)
char *strs[];
{
   struct _item_t *citem;
   uc v, nv;
   ITN *tempitn;

   citem = wmenuicurr ();
   tempitn = cit2itn (citem);
   tempitn->midg (&v, tempitn->arg);
   hitinh = 1;
   wsetesc (1);
   nv = wpickstr (0, 20, 23, 75, 0, PLAIN, PLAIN, INVERSE, strs, v, NULL);
   if (nv != v && nv != (uc) -1) {
      tempitn->midp (nv, tempitn->arg);
      tempitn->midg (&nv, tempitn->arg);
      tempitn->v2s (nv, citem->str);
      w_prints (citem->wrow, citem->wcol, INVERSE, citem->str);
   }
   hitinh = 0;
   wsetesc (0);
}

void ret_patch ()
{
   ret_pickstr (patches);
}

void ret_tone ()
{
   ret_pickstr (tones);
}

void ret_splitp ()
{
   ret_pickstr (splitpstr);
}

ret_revtype ()
{
   ret_pickstr (revtypestr);
}

void initialize ()
{
   uc v;
   ITN *t;

   delay (100);
   t = firstitn;
   while (t != NULL) {
      t->midg (&v, t->arg);
      t->v2s (v, t->citem->str);
      w_prints (t->citem->wrow, t->citem->wcol, PLAIN, t->citem->str);
      t = t->next;
   }
}

/*****     FILE LOAD AND SAVE     *****/

char fname[256];

int entername ()
{
   hitinh = 1;
   if (_mouse) mshidecur ();
   wprints (15, 0, BOLD,  " Enter file name (* = Dir, extension=.syx):");
   wprints (16, 0, PLAIN, clrbuf);
   wgotoxy (16, 1);
   clearkeys ();
   showcur ();
   wgetns (fname, 64);
   if (*fname == '*') {
      strcpy (fname,
         wpickfile (2, 2, 8, 75, 0, PLAIN, PLAIN, INVERSE, 1, "*.syx", NULL));
   }
   else {
      strcpy (fname + strlen(fname), ".syx");
   }
   hidecur ();
   wprints (16, 0, PLAIN, clrbuf);
   wprints (16, 9, BOLD, fname);
   if (_mouse) msshowcur ();
   hitinh = 0;
   return (*fname != '.');
}

void clearname ()
{
   wprints (15, 0, PLAIN,  "                                           ");
   wprints (16, 0, PLAIN, clrbuf);
}

FILE *f;
uc buf[280];

int doall = 0;		/* true if you want to crash your D-20 with old ROMs */

void loadfile ()
{
   uc c, a2, a1;
   int q, j, i;
   
   if (entername ()) {
      f = fopen (fname, "rb");
      if (f == NULL) {
         nonfatalerr ("Could not read file");
      }
      else {
         wprints (16, 1, BOLD, "LOADING");
         i = 0;
         q = 1;
         for (;;) {
            c = getc (f);
            if (feof (f))
               break;
            buf[i++] = c;
            if (i > 266) {
               nonfatalerr ("Not in good Roland sysex format");
               fclose (f);
               clearname ();
               return;
            }
            if (c == 0xf7) {
               /* With old ROMs, if you write to the Rhythm Track when you
                  are not in BULK-ONE-WAY transfer mode, you will crash the
                  D-20, and the only way to reset it is to short out the
                  internal battery.  Writes to rhythm track, rhythm patterns,
                  and areas 0b 10 00 and 0b 12 00 are thus rejected. */
               a2 = buf[5]; a1 = buf[6];
               if (i>8 && buf[0] == 0xf0 && buf[1] == 0x41 && (doall ||
                     (a2 <= 0x02 && a1 <= 0x01) ||
                     (a2 == 0x03 && a1 == 0x00) ||
                     (a2 == 0x03 && a1 == 0x01) ||
                     (a2 == 0x03 && a1 == 0x04) ||
                     (a2 == 0x04 && a1 <= 0x0e) ||
                     (a2 == 0x05 && a1 <= 0x07) ||
                     (a2 == 0x07 && a1 <= 0x25) ||
                     (a2 == 0x08 && a1 <= 0x7f) ||
                     (a2 == 0x09 && a1 <= 0x02) ||
                     (a2 == 0x10 && a1 == 0x00) ||
                     (a2 == 0x20 && a1 == 0x00) )) {
                        for (j = 0; j < i; ++j)
                           midout (buf[j]);
               }
               else if (q) {
                  nonfatalerr ("Ignoring unknown or rhythm info");
                  q = 0;
               }
               i = 0;
            }
         }
         fclose (f);
         initialize ();
      }
   }
   clearname ();
}

void get_spit (a2, a1, a0, n)
uc a2, a1, a0, n;
{
   int i, j;
   get_midi (a2, a1, a0, n, buf);
   for (i = 0; i < 5; ++i)
      putc (dt1messhead[i], f);
   putc (a2, f); putc (a1, f); putc (a0, f);
   j = a2 + a1 + a0;
   for (i = 0; i < n; ++i) {
      putc (buf[i], f);
      j = j + buf[i];
   }
   putc ((-j) & 0x7f, f);
   putc (0xf7, f);
}

void savetimbs()
{
   int i, j;
   if (entername ()) {
      f = fopen (fname, "wb");
      if (f == NULL) {
         nonfatalerr ("Could not write file");
      }
      else {
         int i;
         wprints (16, 1, BOLD, "WRITING");
         /* timbre temps */
         for (i = 0; i < 8; ++i)
            get_spit (0x03, 0x00, 0x10 * i, 0x10);
         /* tone temps */
         for (i = 0; i < 8; ++i) {
            j = i * 246;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x0e);
            j = j + 0x0e;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
         }
         /* system area */
         get_spit (0x10, 0x00, 0x00, 0x32);
         fclose (f);
      }
   }
   clearname ();
}

void savepatch ()
{
   int i, j;
   if (entername ()) {
      f = fopen (fname, "wb");
      if (f == NULL) {
         nonfatalerr ("Could not write file");
      }
      else {
         wprints (16, 1, BOLD, "WRITING");
         /* patch temp */
         get_spit (0x03, 0x04, 0x00, 0x26);
         /* upper/lower tone temps */
         for (i = 0; i < 2; ++i) {
            j = i * 246;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x0e);
            j = j + 0x0e;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
         }
         /* system area */
         get_spit (0x10, 0x00, 0x00, 0x32);
         fclose (f);
      }
   }
   clearname ();
}

void saveevery ()
{
   int i, j, k;
   uc a2, a1, a0;
   
   if (entername ()) {
      f = fopen (fname, "wb");
      if (f == NULL) {
         nonfatalerr ("Could not write file");
      }
      else {
         wprints (16, 1, BOLD, "WRITING");
         
         /* timbre temps */
         for (i = 0; i < 8; ++i)
            get_spit (0x03, 0x00, 0x10 * i, 0x10);
            
         /* rhythm temp */
         get_spit (0x03, 0x01, 0x00, 0x10);
         
         /* rhythm setup temp */
         get_spit (0x03, 0x01, 0x10, 0x04);
         
         /* patch temp */
         get_spit (0x03, 0x04, 0x00, 0x26);
         
         /* tone temps */
         for (i = 0; i < 8; ++i) {
            j = i * 246;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x0e);
            j = j + 0x0e;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x04, (j >> 7), (j & 0x7f), 0x3a);
         }
         
         /* timbre memory */
         for (i = 0; i < 128; ++i) {
            j = i * 0x08;
            get_spit (0x05, (j >> 7), (j & 0x7f), 0x08);
         }
         
         /* patch memory */
         for (i = 0; i < 128; ++i) {
            j = i * 0x26;
            get_spit (0x07, (j >> 7), (j & 0x7f), 0x26);
         }
         
         /* tone memory */
         for (i = 0; i < 64; ++i) {
            j = i * 0x100;
            get_spit (0x08, (j >> 7), (j & 0x7f), 0x0e);
            j = j + 0x0e;
            get_spit (0x08, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x08, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x08, (j >> 7), (j & 0x7f), 0x3a);
            j = j + 0x3a;
            get_spit (0x08, (j >> 7), (j & 0x7f), 0x3a);
         }
         
         /* rhythm setups */
         for (i = 0; i < 85; ++i) {
            j = i * 0x04;
            get_spit (0x09, (j >> 7), (j & 0x7f), 0x04);
         }
         
         if (doall) {
            /* rhythm patterns */
            for (i = 0; i < 32; ++i) {
               j = i * 0x24c;
               get_spit (0x0a + (j >> 14), (j & 0x3f80) >> 7, (j & 0x7f), 100);
               j = j + 100;
               get_spit (0x0a + (j >> 14), (j & 0x3f80) >> 7, (j & 0x7f), 100);
               j = j + 100;
               get_spit (0x0a + (j >> 14), (j & 0x3f80) >> 7, (j & 0x7f), 100);
               j = j + 100;
               get_spit (0x0a + (j >> 14), (j & 0x3f80) >> 7, (j & 0x7f), 100);
               j = j + 100;
               get_spit (0x0a + (j >> 14), (j & 0x3f80) >> 7, (j & 0x7f), 100);
               j = j + 100;
               get_spit (0x0a + (j >> 14), (j & 0x3f80) >> 7, (j & 0x7f), 88);
            }
            
            /* rhythm track */
            j = 0;
            get_spit (0x0c, (j >> 7), (j & 0x7f), 100);
            j = j + 100;
            get_spit (0x0c, (j >> 7), (j & 0x7f), 100);
            j = j + 100;
            get_spit (0x0c, (j >> 7), (j & 0x7f), 100);
            j = j + 100;
            get_spit (0x0c, (j >> 7), (j & 0x7f), 100);
            j = j + 100;
            get_spit (0x0c, (j >> 7), (j & 0x7f), 102);
         }
         
         /* system area */
         get_spit (0x10, 0x00, 0x00, 0x32);
        
         fclose (f);
      }
   }
   clearname ();
}

void get_bank (b)
int *b;
{
   int bad;
   char buf[20];
   uc c;

   do {
      bad = 0;
      wprints (5, 10, PLAIN, "    ");
      wgotoxy (5, 10);
      showcur ();
      clearkeys();
      wgetns (buf, 4);
      hidecur ();
      if (*buf == '\0')
         *b = -1;
      else {
         if (*buf == 'a' || *buf == 'b' || *buf == 'A' || *buf == 'B') {
            if (*(buf+1) != '-') {
               bad = 1;
            }
            else {
               if (*(buf+2) < '1' || *(buf+2) > '8' ||
                   *(buf+3) < '1' || *(buf+3) > '8') {
                  bad = 1;
               }
               else {
	          if (*buf == 'a')
	             *buf = 'A';
	          else if (*buf == 'b')
	             *buf = 'B';
                  *b = ((*buf-'A') << 6)|((*(buf+2)-'1') << 3) | (*(buf+3)-'1');
               }
            }
         }
         else {
            sscanf (buf, "%d", b);
            if (*b < 0 || *b > 127)
               bad = 1;
         }
         if (bad) {
            beep ();
            wprints (6, 3, BOLD, "Try Again");
         }
      }
   } while (bad);
}

void writetimbs ()
{
   int i, banks[8], one;
   uc buf[20];

   hitinh = 1;
   if (_mouse) mshidecur ();
   one = 0;
   for (i = 0; i < 8; ++i) {
      wopen (3+i, 0, 11+i, 17, 1, BOLD, PLAIN);
      wprints (0, 1, PLAIN, "TIMBRE WRITE:");
      wprints (1, 2, PLAIN, "Enter the");
      wprints (2, 2, PLAIN, "destination");
      wprints (3, 2, PLAIN, "Bank-Number");
      wprints (4, 2, PLAIN, "for timbre on");
      sprintf (buf, "Part %d:", i+1);
      wprints (5, 2, PLAIN, buf);
      get_bank (&(banks[i]));
      if (banks[i] != -1)
         one = 1;
      wclose ();
   }
   if (one) {
      wopen (10, 6, 22, 29, 1, BOLD, PLAIN);
      wprints (0, 4, PLAIN, "TIMBRE WRITE:");
      wprints (1, 2, PLAIN, "Part  Bank = Prog");
      for (i = 0; i < 8; ++i) {
         if (banks[i] == -1)
            sprintf (buf, "%d    -", i);
         else
            sprintf (buf, "%d   %c-%c%c = %d", i, (banks[i] >> 6) + 'A',
               ((banks[i] & 0x38) >> 3) + '1', (banks[i] & 0x7) + '1',
		 banks[i]);
         wprints (2+i, 4, PLAIN, buf);
      }
      wprints (10, 4, BOLD, "Sure? (y / n)");
      wgotoxy (10, 14);
      beep ();
      if (gotyes ()) {
         wprints (10, 1, PLAIN, "  Writing... ");
         buf[1] = (uc) 0;		/* only write to Internal, not Card */
         for (i = 0; i < 8; ++i) {
            if (banks[i] != -1) {
               *buf = (uc) banks[i];
               put_midi (0x40, 0x01, 0x02 * i, 2, buf);
            }
         }
         get_midi (0x40, 0x10, 0, 1, buf);
         if (*buf != (uc) 0) {
            beep ();
            wprints (1, 1, BOLD, "Error: D-20 is");
            wprints (2, 1, BOLD, "in Performance");
            wprints (3, 1, BOLD, "Mode.         ");
            delay (3000);
         }
      }
      wclose ();
   }
   hitinh = 0;
   if (_mouse) msshowcur ();
}

void writepatch ()
{
   int i, b;
   uc buf[20];

   hitinh = 1;
   if (_mouse) mshidecur ();
   wopen (13, 0, 21, 22, 1, BOLD, PLAIN);
   wprints (0, 4, PLAIN, "PATCH WRITE:");
   wprints (1, 2, PLAIN, "Enter the");
   wprints (2, 2, PLAIN, "destination");
   wprints (3, 2, PLAIN, "Bank-Number");
   wprints (4, 2, PLAIN, "for this");
   wprints (5, 2, PLAIN, "patch:");
   get_bank (&b);
   if (b != -1) {
      sprintf (buf, "%c-%c%c = %d", (b >> 6) + 'A', ((b & 0x38) >> 3) + '1',
            (b & 0x7) + '1', b);
      wprints (5, 10, PLAIN, buf);
      wprints (6, 2, BOLD, "  Sure? (y / n)  ");
      wgotoxy (6, 14);
      beep ();
      if (gotyes ()) {
         wprints (6, 1, PLAIN, "  Writing... ");
         buf[1] = (uc) 0;		/* only write to Internal, not Card */
         buf[0] = (uc) b;
         put_midi (0x40, 0x03, 0, 2, buf);
         get_midi (0x40, 0x10, 0, 1, buf);
         if (*buf != (uc) 0) {
            beep ();
            wprints (1, 1, BOLD, "Error: D-20 is");
            wprints (2, 1, BOLD, "in Timbre     ");
            wprints (3, 1, BOLD, "Mode.         ");
            delay (3000);
         }
	 get_midi (0x03, 0x04, 0x15, 16, buf);
	 strcpy (patchesstr[b]+5, buf);
      }
   }
   wclose ();
   hitinh = 0;
   if (_mouse) msshowcur ();
}

/*****     DEFINING AN ELEMENT     *****/

void bofem (r, c, fl, max, mode, arg, v2s, midg, midp, retf)
int r, c, fl;
uc mode, arg;
void v2s (), midg (), midp (), retf ();
{
     char *b;
     int i;
     struct _item_t *citem;
     ITN *tempitn;

     b = (char *) calloc (fl+1, 1);
     for (i = 0; i < fl; ++i)
        b[i] = '-';
     ++tagid;
     wmenuitem (r, c, b, 0, tagid, 0, retf, 0, 0);
     citem = wmenuifind (tagid);
     tempitn = (ITN *) calloc (1, sizeof (ITN));
     tempitn->citem = citem;
     tempitn->max = max;
     tempitn->mode = mode;
     tempitn->arg = arg;
     tempitn->v2s = v2s;
     tempitn->midg = midg;
     tempitn->midp = midp;
     tempitn->next = firstitn;
     firstitn = tempitn;
}

void init_screen ()
{
   whline ( 9,  0, 78, 0, PLAIN);
   whline (12,  0, 78, 0, PLAIN);
   whline (14,  0, 46, 0, PLAIN);
   whline (15, 46, 32, 0, PLAIN);
   wvline (12, 46,  4, 0, PLAIN);
   wprints (0, 0, PLAIN, "MULTI    TONE              VOLUME                BALANCE     REV SHF TUN BN AS");
   wprints (13,  3, PLAIN, "RHYTHM VOLUME");
   wprints (13, 49, PLAIN, "REVERB TYPE:");
   wprints (14, 49, PLAIN, "LEVEL          TIME");
   wprints (21,  1, PLAIN, "+ - / * , .");
   wcenters (21, BOLD, "MIDIMENU for ROLAND D-5/10/20, by TTL");
}

void usage ()
{
   fprintf (stderr, "usage: midimenu [-p hex_port] [-d5] [-all]\n");
   exit (1);
}

/*****     MAIN     *****/

void main (argc, argv)
int argc;
char *argv[];
{
   char c;
   
   ++argv;
   while (*argv != NULL) {
      c = **argv;
      if (c == '-')
         c = *(*argv + 1);
      if (c == 'p') {
         ++argv;
         if (argv[0] == NULL)
            usage ();
         c = **argv;
         if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
         			       (c >= 'A' && c <= 'F')) {
            sscanf (argv[0], "%x", &DATAPORT);
            STATPORT = COMDPORT = (DATAPORT | 1);
         }
         else
            usage ();
      }
      else if (c == 'd') {
         D5 = 1;
      }
      else if (c == 'a') {
         doall = 1;
      }
      else
         usage ();
      ++argv;
   }
   
   videoinit ();
   readcur (&crow, &ccol);
   savescrn = ssave ();
   if (msinit ()) {
       mssupport (MS_FULL);
       msgotoxy (21, 1);
   }
   numoff ();
   hidecur ();
   wopen (0, 0, 23, 79, 0, PLAIN, PLAIN);
   init_screen ();
   setonkey (0x2d00, byebye, 0);	/* exit on Alt-X */

   midg_names ();

   setonkey (0x4e2b, hitplus, 0);
   setonkey (0x4a2d, hitminus, 0);
   setonkey (0x352f, hitdiv, 0);
   setonkey (0x372a, hittimes, 0);
   setonkey (0x332c, hitcom, 0);
   setonkey (0x342e, hitdot, 0);
   firstitn = NULL;
   wmenubegc ();
   {
   /* do all performance items first, so I can traverse itn linked list */

bofem (10, 1, 15, 255, WRAP, 0, v2s_tone, midg_tonep, midp_tonep, ret_tone);
bofem (11, 1, 15, 255, WRAP, 1, v2s_tone, midg_tonep, midp_tonep, ret_tone);
bofem (10, 18, 26, 25, NOWRAP, -1, v2s_slider, midg_volp, midp_volp, ret_slider);
bofem (10, 49, 10, 100, NOWRAP, -1, v2s_mix, midg_mix, midp_mix, hitplus);
bofem (14, 55, 8, 7, NOWRAP, -1, v2s_slider, midg_revlev, midp_revlev, ret_slider);
bofem (14, 69, 8, 7, NOWRAP, -1, v2s_slider, midg_revtime, midp_revtime, ret_slider);
bofem (10, 62, 1, 1, TOGGLE, 0, v2s_revstat, midg_revstatp, midp_revstatp,hitplus);
bofem (11, 62, 1, 1, TOGGLE, 1, v2s_revstat, midg_revstatp, midp_revstatp,hitplus);
bofem (10, 65, 3, 48, WRAP, 0, v2s_keyshf, midg_keyshfp, midp_keyshfp, hitplus);
bofem (11, 65, 3, 48, WRAP, 1, v2s_keyshf, midg_keyshfp, midp_keyshfp, hitplus);
bofem (10, 69, 3, 100, WRAP, 0, v2s_tune, midg_tunep, midp_tunep, hitplus);
bofem (11, 69, 3, 100, WRAP, 1, v2s_tune, midg_tunep, midp_tunep, hitplus);
bofem (10, 73, 2, 24, WRAP, 0, v2s_bender, midg_benderp, midp_benderp, hitplus);
bofem (11, 73, 2, 24, WRAP, 1, v2s_bender, midg_benderp, midp_benderp, hitplus);
bofem (10, 76, 2, 3, WRAP, 0, v2s_assign, midg_assignp, midp_assignp, hitplus);
bofem (11, 76, 2, 3, WRAP, 1, v2s_assign, midg_assignp, midp_assignp, hitplus);

bofem (11, 41,  8, 61, WRAP, -1, v2s_splitp, midg_splitp, midp_splitp, ret_splitp);
bofem (11, 51, 8,  2, WRAP, -1, v2s_splmode, midg_splmode, midp_splmode, hitplus);
bofem (13, 62, 11,  8, WRAP, -1, v2s_revtype, midg_revtype, midp_revtype, ret_revtype);

bofem (11, 18, 21, 127, WRAP, -1, v2s_patch, midg_patch, midp_patch, ret_patch);

bofem (1, 1, 15, 255, WRAP, 0, v2s_tone, midg_tonem, midp_tonem, ret_tone);
bofem (2, 1, 15, 255, WRAP, 1, v2s_tone, midg_tonem, midp_tonem, ret_tone);
bofem (3, 1, 15, 255, WRAP, 2, v2s_tone, midg_tonem, midp_tonem, ret_tone);
bofem (4, 1, 15, 255, WRAP, 3, v2s_tone, midg_tonem, midp_tonem, ret_tone);
bofem (5, 1, 15, 255, WRAP, 4, v2s_tone, midg_tonem, midp_tonem, ret_tone);
bofem (6, 1, 15, 255, WRAP, 5, v2s_tone, midg_tonem, midp_tonem, ret_tone);
bofem (7, 1, 15, 255, WRAP, 6, v2s_tone, midg_tonem, midp_tonem, ret_tone);
bofem (8, 1, 15, 255, WRAP, 7, v2s_tone, midg_tonem, midp_tonem, ret_tone);

bofem (1, 18, 26, 25, NOWRAP, 0, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (2, 18, 26, 25, NOWRAP, 1, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (3, 18, 26, 25, NOWRAP, 2, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (4, 18, 26, 25, NOWRAP, 3, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (5, 18, 26, 25, NOWRAP, 4, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (6, 18, 26, 25, NOWRAP, 5, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (7, 18, 26, 25, NOWRAP, 6, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (8, 18, 26, 25, NOWRAP, 7, v2s_slider, midg_volm, midp_volm, ret_slider);
bofem (13, 18, 26, 25, NOWRAP, 8, v2s_slider, midg_volm, midp_volm, ret_slider);

bofem (1, 45, 15, 14, NOWRAP, 0, v2s_slider, midg_bal, midp_bal, ret_slider);
bofem (2, 45, 15, 14, NOWRAP, 1, v2s_slider, midg_bal, midp_bal, ret_slider);
bofem (3, 45, 15, 14, NOWRAP, 2, v2s_slider, midg_bal, midp_bal, ret_slider);
bofem (4, 45, 15, 14, NOWRAP, 3, v2s_slider, midg_bal, midp_bal, ret_slider);
bofem (5, 45, 15, 14, NOWRAP, 4, v2s_slider, midg_bal, midp_bal, ret_slider);
bofem (6, 45, 15, 14, NOWRAP, 5, v2s_slider, midg_bal, midp_bal, ret_slider);
bofem (7, 45, 15, 14, NOWRAP, 6, v2s_slider, midg_bal, midp_bal, ret_slider);
bofem (8, 45, 15, 14, NOWRAP, 7, v2s_slider, midg_bal, midp_bal, ret_slider);

bofem (1, 62, 1, 1, TOGGLE, 0, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);
bofem (2, 62, 1, 1, TOGGLE, 1, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);
bofem (3, 62, 1, 1, TOGGLE, 2, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);
bofem (4, 62, 1, 1, TOGGLE, 3, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);
bofem (5, 62, 1, 1, TOGGLE, 4, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);
bofem (6, 62, 1, 1, TOGGLE, 5, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);
bofem (7, 62, 1, 1, TOGGLE, 6, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);
bofem (8, 62, 1, 1, TOGGLE, 7, v2s_revstat, midg_revstatm, midp_revstatm, hitplus);

bofem (1, 65, 3, 48, WRAP, 0, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);
bofem (2, 65, 3, 48, WRAP, 1, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);
bofem (3, 65, 3, 48, WRAP, 2, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);
bofem (4, 65, 3, 48, WRAP, 3, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);
bofem (5, 65, 3, 48, WRAP, 4, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);
bofem (6, 65, 3, 48, WRAP, 5, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);
bofem (7, 65, 3, 48, WRAP, 6, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);
bofem (8, 65, 3, 48, WRAP, 7, v2s_keyshf, midg_keyshfm, midp_keyshfm, hitplus);

bofem (1, 69, 3, 100, WRAP, 0, v2s_tune, midg_tunem, midp_tunem, hitplus);
bofem (2, 69, 3, 100, WRAP, 1, v2s_tune, midg_tunem, midp_tunem, hitplus);
bofem (3, 69, 3, 100, WRAP, 2, v2s_tune, midg_tunem, midp_tunem, hitplus);
bofem (4, 69, 3, 100, WRAP, 3, v2s_tune, midg_tunem, midp_tunem, hitplus);
bofem (5, 69, 3, 100, WRAP, 4, v2s_tune, midg_tunem, midp_tunem, hitplus);
bofem (6, 69, 3, 100, WRAP, 5, v2s_tune, midg_tunem, midp_tunem, hitplus);
bofem (7, 69, 3, 100, WRAP, 6, v2s_tune, midg_tunem, midp_tunem, hitplus);
bofem (8, 69, 3, 100, WRAP, 7, v2s_tune, midg_tunem, midp_tunem, hitplus);

bofem (1, 73, 2, 24, WRAP, 0, v2s_bender, midg_benderm, midp_benderm, hitplus);
bofem (2, 73, 2, 24, WRAP, 1, v2s_bender, midg_benderm, midp_benderm, hitplus);
bofem (3, 73, 2, 24, WRAP, 2, v2s_bender, midg_benderm, midp_benderm, hitplus);
bofem (4, 73, 2, 24, WRAP, 3, v2s_bender, midg_benderm, midp_benderm, hitplus);
bofem (5, 73, 2, 24, WRAP, 4, v2s_bender, midg_benderm, midp_benderm, hitplus);
bofem (6, 73, 2, 24, WRAP, 5, v2s_bender, midg_benderm, midp_benderm, hitplus);
bofem (7, 73, 2, 24, WRAP, 6, v2s_bender, midg_benderm, midp_benderm, hitplus);
bofem (8, 73, 2, 24, WRAP, 7, v2s_bender, midg_benderm, midp_benderm, hitplus);

bofem (1, 76, 2, 3, WRAP, 0, v2s_assign, midg_assignm, midp_assignm, hitplus);
bofem (2, 76, 2, 3, WRAP, 1, v2s_assign, midg_assignm, midp_assignm, hitplus);
bofem (3, 76, 2, 3, WRAP, 2, v2s_assign, midg_assignm, midp_assignm, hitplus);
bofem (4, 76, 2, 3, WRAP, 3, v2s_assign, midg_assignm, midp_assignm, hitplus);
bofem (5, 76, 2, 3, WRAP, 4, v2s_assign, midg_assignm, midp_assignm, hitplus);
bofem (6, 76, 2, 3, WRAP, 5, v2s_assign, midg_assignm, midp_assignm, hitplus);
bofem (7, 76, 2, 3, WRAP, 6, v2s_assign, midg_assignm, midp_assignm, hitplus);
bofem (8, 76, 2, 3, WRAP, 7, v2s_assign, midg_assignm, midp_assignm, hitplus);

     wmenuitem (17, 41, "^TIMBS WRITE", '', ++tagid, 0, writetimbs,  0, 0);
     wmenuitem (18, 41, "^PATCH WRITE", '', ++tagid, 0, writepatch,  0, 0);
     wmenuitem (17, 55, "TIMBS SAVE", 't', ++tagid, 0, savetimbs,  0, 0);
     wmenuitem (18, 55, "PATCH SAVE", 'p', ++tagid, 0, savepatch,  0, 0);
     wmenuitem (17, 67, "ALL SAVE  ", 'a', ++tagid, 0, saveevery,  0, 0);
     wmenuitem (18, 67, "LOAD FILE ", 'l', ++tagid, 0, loadfile,   0, 0);
     wmenuitem (19, 67, "INITIALIZE", 'i', ++tagid, 0, initialize, 0, 0);
     wmenuitem (21, 73, "EXIT", 'e', ++tagid, 0, byebye, 0, 0);
   }
   wmenuend (1, M_OMNI, 0, 0, PLAIN, PLAIN, PLAIN, INVERSE);
   wsetesc (0);

   initialize ();
   d20display ("Ready.          Have fun!");
   
   for (;;)
      if (wmenuget () == -1)
         fatalerr ("wmenuget error");
}
