/* Midi Monitor - CAMD / Realtime V37 version */

#include <exec/types.h>
#include <devices/timer.h>	/* for timeval */

#include <libraries/dos.h>
#include <midi/camd.h>
#include <midi/camdbase.h>
#include <midi/realtimebase.h>	/* for TickFreq and TickErr */
#include <midi/mididefs.h>
#include <midi/midiprefs.h>	/* for MaxMidiUnits */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <clib/camd_protos.h>
#include <clib/exec_protos.h>

#include <pragmas/camd_pragmas.h>

#ifndef MAX
#define MAX(a,b)        ((a) > (b) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b)        ((a) < (b) ? (a) : (b))
#endif
#ifndef ABS
#define ABS(x)          ((x) < 0 ? -(x) : (x))
#endif

struct CamdBase *CamdBase;
struct RealTimeBase *RealTimeBase;
struct MidiNode *mi;
struct MidiLink *inlink,*in2link,*outlink;

#define DISPF_Hex	0x0001
#define DISPF_Interpret 0x0002
#define DISPF_LongSysEx 0x0004
#define DISPF_Time	0x0008

UWORD dispopts;

char *cmerr (long err);
void process (void);
void cleanup (void);
void dumperr (register UBYTE err);
void dumpmsg (MidiMsg *);
void prttime (ULONG time);
void prtchan (register MidiMsg *);
void prtctrl (register MidiMsg *);
void prtmode (register MidiMsg *);
void prtsys (MidiMsg *);
void prtrt (MidiMsg *);
void prtex (MidiMsg *);
void dumplong (long len);
void xgeneric (ULONG len);
void prtund (MidiMsg *);

void dump (register UBYTE *s, register int len);
void dumpascii (register UBYTE *s, register int len);
void gettv (ULONG time, register struct timeval *tv);
void prtqtrframe (register UBYTE data);

void _wb_parse(void) {}


/*
 * Lattice control-c stop...
 */
int CXBRK(void) { return(0); }		/* Disable Lattice CTRL/C handling */
int chkabort(void) { return(0); }	/* really */

struct MidiNode *CreateMidi(Tag tag, ...)
{	return CreateMidiA((struct TagItem *)&tag );
}

struct MidiLink *AddMidiLink(struct MidiNode *mi, LONG type, Tag tag, ...)
{	return AddMidiLinkA(mi, type, (struct TagItem *)&tag );
}


/*******
* Main * ------------------------------------------------------------------------
*******/
void main (int argc, char **argv)
{
	char *inlinkname = "in.0";
	char *in2linkname = "in.0";
	char *outlinkname = "out.0";

	if (argc > 1) inlinkname = argv[1];

	if (argc > 2) outlinkname = argv[2];

	printf("Linking with in.0\n");
	printf("Also linking with incluster=%s\noutcluster=%s\n\n",inlinkname,outlinkname);

	if (RealTimeBase = (struct RealTimeBase *)OpenLibrary("realtime.library",37))
	{
		if (CamdBase = (struct CamdBase *)OpenLibrary("camd.library",37))
		{
			if (mi = CreateMidi (	MIDI_Name,		"MIDI Monitor",
									MIDI_RecvSignal,SIGBREAKB_CTRL_E,
									MIDI_MsgQueue,	255,
									MIDI_ErrFilter,	CMEF_All,
									MIDI_SysExSize,	8096,
									TAG_END ))
			{
				if (inlink = AddMidiLink(mi,MLTYPE_Receiver,
										MLINK_Name,			"MIDI Monitor Link",
										MLINK_Location,		inlinkname,
										MLINK_EventMask,	CMF_All,
										MLINK_Comment,		"MIDI Monitor [Input]",
										MLINK_SysExFilter,	SXFM_Off,
										MLINK_ChannelMask,	0xffffL,
										TAG_END ))
				{
	
					in2link = AddMidiLink(mi,MLTYPE_Receiver,
										MLINK_Name,			"MIDI Monitor Link",
										MLINK_Location,		in2linkname,
											MLINK_EventMask,	CMF_All,
										MLINK_Comment,		"MIDI Monitor [Input]",
										MLINK_SysExFilter,	SXFM_Off,
										MLINK_ChannelMask,	0xffffL,
										TAG_END );
	
					if (! (outlink = AddMidiLink(mi,MLTYPE_Sender,
											MLINK_Name,			"MIDI Monitor Link",
											MLINK_Location,		outlinkname,
											MLINK_Comment,		"MIDI Monitor [Output]",
											TAG_END )))
					{
						printf("Can't add output link\n");
					}
	
					printf ("Ctrl-C to exit.\n");
					process();
				}
			}
		}
	}
	cleanup();
	CloseLibrary ((struct Library *)CamdBase);
	CloseLibrary ((struct Library *)RealTimeBase);
	exit(0L);
}


void cleanup(void)
{
	if (mi)
	{
		RemoveMidiLink(outlink);
		RemoveMidiLink(in2link);
		RemoveMidiLink(inlink);
		DeleteMidi (mi);
		mi = NULL;
	}
}


void process(void)
{
	ULONG sigmask = (SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_E);
	MidiMsg msg;
	UBYTE err;

	while (!(Wait(sigmask) & SIGBREAKF_CTRL_C))
	{
		if (err = GetMidiErr(mi)) dumperr (err);

		while (GetMidi (mi,&msg))
		{
			dumpmsg (&msg);
		}
	}
}

void dumperr(register UBYTE err)
{
   if (dispopts & DISPF_Time) /* prttime(*MidiTime()); */

   printf ("***");
   if (err & (UBYTE)CMEF_RecvOverflow) printf (" RecvOverflow");
   if (err & (UBYTE)CMEF_RecvErr) printf (" RecvErr");
   if (err & (UBYTE)CMEF_ParseMem) printf (" ParseMem");
   if (err & (UBYTE)CMEF_SysExFull) printf (" SysExFull");
   if (err & (UBYTE)CMEF_SysExTooBig) printf (" SysExTooBig");
   if (err & (UBYTE)CMEF_BufferFull) printf (" BufferFull");
   if (err & (UBYTE)CMEF_MsgErr) printf (" MsgErr");
   printf ("\n");
}

char *num (char *b, unsigned val)
{
   sprintf (b, dispopts & DISPF_Hex ? "%x" : "%u", val);
   return b;
}

char *snum (char *b, int val)
{
   sprintf (b, dispopts & DISPF_Hex ? "%x" : "%+d", val);
   return b;
}

char *notename (char *b, int note)
{
   static char *notes[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };

   if (dispopts & DISPF_Interpret)
      sprintf (b,"%s%d",notes[note%12],note/12-2);
   else
      sprintf (b,"%s",num(b,note));

   return b;
}


void dumpmsg (MidiMsg *msg)
{
   int type = MidiMsgType(msg);
   if (dispopts & DISPF_Time){;} /* prttime(msg->mm_Time); */

   if (1L << type & CMF_Channel)
      {
      prtchan (msg);
      }
   else
      switch (type)
         {
         case CMB_SysCom:
         prtsys(msg);
         break;

         case CMB_RealTime:
         prtrt(msg);
         break;

         case CMB_SysEx:
         prtex(msg);
         break;

         default:    /* probably won't get this */
         prtund(msg);
         break;
         }
}


/* !!! time could be relative to the time the program started */
/*
void prtqtrframe (UBYTE data)
{
   struct timeval tv;

   gettv (time, &tv);
   printf ("%d:%02d:%02d.%03d ", (int)(tv.tv_secs / 3600), (int)(tv.tv_secs / 60 % 60), (int)(tv.tv_secs % 60), (int)((tv.tv_micro + 500) / 1000));
}
*/
void gettv (ULONG time, register struct timeval *tv)
{
   unsigned long err = time/1000*ABS(RealTimeBase->TickErr) + (time%1000*ABS(RealTimeBase->TickErr)+500)/1000;

   tv->tv_secs = time / RealTimeBase->TickFreq;
   tv->tv_micro = time % RealTimeBase->TickFreq * 1000000 / RealTimeBase->TickFreq;

   /* apply err */
   if (RealTimeBase->TickErr >= 0)
      {
      tv->tv_secs += err / 1000000;
      tv->tv_micro += err % 1000000;
      }
   else
      {
      tv->tv_secs -= err / 1000000 + 1;
      tv->tv_micro -= err % 1000000 - 1000000;
      }

   /* normalize timeval */
   if (tv->tv_micro >= 1000000)
      {
      tv->tv_secs += tv->tv_micro / 1000000;
      tv->tv_micro %= 1000000;
      }
}



/* print channel message */

void prtchan (MidiMsg *msg)
{
   unsigned chan = (msg->mm_Status & MS_ChanBits) + 1;
   char b1[20],b2[20];
   int type = MidiMsgType(msg);

   /* MIDI thru added by dart */
   PutMidiMsg(outlink,msg);

   if (1L << type & CMF_Ctrl) prtctrl (msg);
   else
      switch (type)
         {
         case CMB_Note:
         printf ("Nt%s:%d %s %s\n", noteon(msg) ? "Dn" : "Up", chan, notename(b1,msg->mm_Data1), num(b2,msg->mm_Data2));
         break;

         case CMB_PolyPress:
         printf ("Pp:%d %s %s\n", chan, notename(b1,msg->mm_Data1), num(b2,msg->mm_Data2));
         break;

         case CMB_Prog:
         printf ("Pg:%d %s\n", chan, num(b1,msg->mm_Data1+1));
         break;

         case CMB_ChanPress:
         printf ("Mp:%d %s\n", chan, num(b1,msg->mm_Data1));
         break;

         case CMB_PitchBend:
         printf ("Pw:%d %s\n", chan, snum(b1,MIDIWord(msg->mm_Data2,msg->mm_Data1)-PitchBendCenter));
         break;

         case CMB_Mode:
         prtmode (msg);
         break;
         }
}


void prtmode (register MidiMsg *msg)
{
   unsigned chan = (msg->mm_Status & MS_ChanBits) + 1;
   register int md = msg->mm_Data1;
   int val = msg->mm_Data2;
   char b1[20],b2[20];

   printf ("Md:%d ",chan);
   if (dispopts & DISPF_Interpret)
      {
      switch (md)
         {
         case MM_ResetCtrl:
         printf ("Reset All Controllers\n");
         break;

         case MM_Local:
         printf ("Local Control %s\n",val ? "On" : "Off");
         break;

         case MM_AllOff:
         printf ("All Notes Off\n");
         break;

         case MM_OmniOff:
         printf ("Omni Off\n");
         break;

         case MM_OmniOn:
         printf ("Omni On\n");
         break;

         case MM_Mono:
         printf ("Mono %s\n",num(b1,val));
         break;

         case MM_Poly:
         printf ("Poly\n");
         break;
	 }
      }
   else
      printf ("#%s %s\n",num(b1,md),num(b2,val));
}


struct desc
{
   ULONG id;		/* need UWORD for matching ext id's */
   char *desc;
};


char *finddesc (ULONG id, struct desc *list, int count)
{
   for (; count--; list++)
      {
      if (list->id == id) return list->desc;
      }
   return NULL;
}



#define CMERRS (sizeof cmerrlist / sizeof cmerrlist[0])

static struct desc cmerrlist[] =
{
    { CME_NoMem,    "Out of memory" },
    { CME_NoTimer,  "No free CIA channels" },
    { CME_BadPrefs, "Bad midi.prefs file" },
};

char *cmerr (long err)      /* CreateMidi() error codes */
{
   static char b[64];
   char *errmsg;

   if (err >= CME_NoUnit(0) && err <= CME_NoUnit(3))
      {
      sprintf (b, "Unit %d open failure", (int)(err - CME_NoUnit(0)));
      return b;
      }

   if (errmsg = finddesc (err, cmerrlist, CMERRS)) return errmsg;

   sprintf (b, "IoErr=%ld", err);
   return b;
}


#define CTRL1S (sizeof ctrl1list / sizeof ctrl1list[0])
#define CTRL2S (sizeof ctrl2list / sizeof ctrl2list[0])

static struct desc ctrl2list[] =
{
    { MC_ModWheel,  "ModW" },
    { MC_Breath,    "Breath" },
    { MC_Foot,	    "Foot" },
    { MC_PortaTime, "Porta" },
    { MC_DataEntry, "Data" },
    { MC_Volume,    "Vol" },
    { MC_Balance,   "Bal" },
    { MC_Pan,	    "Pan" },
    { MC_Expression,"Exprs" },
    { MC_General1,  "Gen1" },
    { MC_General2,  "Gen2" },
    { MC_General3,  "Gen3" },
    { MC_General4,  "Gen4" },
};

static struct desc ctrl1list[] =
{
    { MC_Sustain,   "Sustain" },
    { MC_Porta,     "Porta" },
    { MC_Sustenuto, "Sustenuto" },
    { MC_SoftPedal, "Soft" },
    { MC_Hold2,     "Hold2" },
    { MC_General5,  "Gen5" },
    { MC_General6,  "Gen6" },
    { MC_General7,  "Gen7" },
    { MC_General8,  "Gen8" },
    { MC_ExtDepth,  "Ext Depth" },
    { MC_TremoloDepth,	"Tremolo Depth" },
    { MC_ChorusDepth,	"Chorus Depth" },
    { MC_CelesteDepth,	"Celeste Depth" },
    { MC_PhaserDepth,	"Phaser Depth" },

    { MC_DataIncr,  "Data +" },
    { MC_DataDecr,  "Data -" },
    { MC_NRPNL,     "Non-Reg Param(L)" },
    { MC_NRPNH,     "Non-Reg Param(H)" },
    { MC_RPNL,	    "Reg Param(L)" },
    { MC_RPNH,	    "Reg Param(H)" }
};

#define CTRLMIN CMB_CtrlMSB

char *ctrltypedesc[] = { "MSB", "LSB", "Switch", "Byte", "Param", "Undef" };


void prtctrl (register MidiMsg *msg)
{
    unsigned chan = (msg->mm_Status & MS_ChanBits) + 1;
    int type = MidiMsgType(msg);
    register int ct = msg->mm_Data1;
    int val = msg->mm_Data2;
    char *desc;
    char b1[20];

    printf ("Ct:%d ",chan);

    if (dispopts & DISPF_Interpret) {
	switch (type) {
	    case CMB_CtrlMSB:
	    case CMB_CtrlLSB:
		    if (!(desc = finddesc ((ULONG)(ct & 31), ctrl2list, CTRL2S))) goto unknown;
		    printf ("%s(%c)", desc, ct>31 ? 'L' : 'H');
		    break;

	    default:
		    if (!(desc = finddesc ((ULONG)ct, ctrl1list, CTRL1S))) goto unknown;
		    printf ("%s", desc);
		    break;

	    unknown:
		    printf ("%s #%s", ctrltypedesc[type-CTRLMIN], num(b1,ct));
		    break;
	}
    }
    else
	printf ("#%s", num(b1,ct));

    printf (" %s\n", num(b1,val));
}


/* print system common */

void prtsys (MidiMsg *msg)
{
    char b1[20];

    switch (msg->mm_Status)
      {
	case MS_SongPos:
		printf ("Pos %s\n", num(b1,MIDIWord(msg->mm_Data1,msg->mm_Data2)));
		break;

	case MS_SongSelect:
		printf ("Song %s\n", num(b1,msg->mm_Data1));
		break;

	case MS_TuneReq:
		printf ("Tune\n");
		break;

	case MS_QtrFrame:
		prtqtrframe (msg->mm_Data1);
		break;

      #if 0	/* !!! shouldn't be able to get here */
	default:
		printf ("Und#%02x\n",msg->mm_Status);
		break;
      #endif
    }
}


void prtqtrframe (register UBYTE data)
{
   static char *desc[] = { "FrameL", "FrameH", "SecL", "SecH", "MinL", "MinH", "HourL", "HourH" };
   char b1[20];

   printf ("QtrFrame ");

   if (dispopts & DISPF_Interpret)
      {
      printf ("%s %s\n", desc[data>>4], num(b1,data & MTCQ_DataMask));
      }
   else printf ("%s\n",num(b1,data));
}


/* print real time */

void prtrt (MidiMsg *msg)
{
   static char *desc[] = { "Clk", "", "Start", "Cont", "Stop", "", "Actv", "Reset" };

   printf ("%s\n", desc[msg->mm_Status-MS_RealTime]);
}


/* print system exclusive */

#define IDS  (sizeof idlist / sizeof idlist[0])

static struct desc idlist[] =
{
    { MID_Sequential,	"Sequential" },
    { MID_IDP,		"IDP" },
    { MID_OctavePlateau,"Octave-Plataeu" },
    { MID_Moog, 	"Moog" },
    { MID_Passport,	"Passport" },
    { MID_Lexicon,	"Lexicon" },
    { MID_Kurzweil,	"Kurzweil" },
    { MID_Fender,	"Fender" },
    { MID_Gulbransen,	"Gulbransen" },
    { MID_AKG,		"AKG" },
    { MID_Voyce,	"Voyce" },
    { MID_Waveframe,	"Waveframe" },
    { MID_ADA,		"ADA" },
    { MID_Garfield,	"Garfield" },
    { MID_Ensoniq,	"Ensoniq" },
    { MID_Oberheim,	"Oberheim" },
    { MID_Apple,	"Apple" },
    { MID_GreyMatter,	"Grey Matter Response" },
    { MID_PalmTree,	"Palm Tree" },
    { MID_JLCooper,	"JL Cooper" },
    { MID_Lowrey,	"Lowrey" },
    { MID_AdamsSmith,	"Adams-Smith" },
    { MID_Emu,		"E-Mu" },
    { MID_Harmony,	"Harmony" },
    { MID_ART,		"ART" },
    { MID_Baldwin,	"Baldwin" },
    { MID_Eventide,	"Eventide" },
    { MID_Inventronics, "Inventronics" },
    { MID_Clarity,	"Clarity" },

    { MID_SIEL, 	"SIEL" },
    { MID_Synthaxe,	"Synthaxe" },
    { MID_Hohner,	"Hohner" },
    { MID_Twister,	"Twister" },
    { MID_Solton,	"Solton" },
    { MID_Jellinghaus,	"Jellinghaus" },
    { MID_Southworth,	"Southworth" },
    { MID_PPG,		"PPG" },
    { MID_JEN,		"JEN" },
    { MID_SSL,		"SSL" },
    { MID_AudioVeritrieb, "Audio Veritrieb" },
    { MID_Elka, 	"Elka" },
    { MID_Dynacord,	"Dynacord" },
    { MID_Clavia,	"Clavia" },
    { MID_Soundcraft,	"Soundcraft" },

    { MID_Kawai,	"Kawai" },
    { MID_Roland,	"Roland" },
    { MID_Korg, 	"Korg" },
    { MID_Yamaha,	"Yamaha" },
    { MID_Casio,	"Casio" },
    { MID_Kamiya,	"Kamiya" },
    { MID_Akai, 	"Akai" },
    { MID_JapanVictor,	"Japan Victor" },
    { MID_Mesosha,	"Mesosha" },

    { MID_UNC,		"Non-Commercial" },
    { MID_UNRT, 	"Non-Real Time" },
    { MID_URT,		"Real Time" }
};


#define XIDS (sizeof xidlist / sizeof xidlist[0])

static struct desc xidlist[] =
{
    { MIDX_DigitalMusic, "Digital Music" },
    { MIDX_Iota,	"Iota" },
    { MIDX_Artisyn,	"Artisyn" },
    { MIDX_IVL, 	"IVL" },
    { MIDX_SouthernMusic, "Southern Music" },
    { MIDX_LakeButler,	"Lake Butler" },
    { MIDX_DOD, 	"DOD" },
    { MIDX_PerfectFret, "Perfect Fretworks" },
    { MIDX_KAT, 	"KAT" },
    { MIDX_Opcode,	"Opcode" },
    { MIDX_Rane,	"Rane" },
    { MIDX_SpatialSound, "Spatial Sound" },
    { MIDX_KMX, 	"KMX" },
    { MIDX_Brenell,	"Brenell" },
    { MIDX_Peavey,	"Peavey" },
    { MIDX_360, 	"360" },
    { MIDX_Axxes,	"Axxes" },
    { MIDX_CAE, 	"CAE" },
    { MIDX_Cannon,	"Cannon" },
    { MIDX_BlueSkyLogic,"Blue Sky Logic" },
    { MIDX_Voce,	"Voce" },
};

void prtex (MidiMsg *msg)
{
   UBYTE b[2];
   UBYTE id;
   ULONG xid;
   ULONG len = QuerySysEx(mi);
   char *idname=NULL;

   printf ("SysEx ");

   GetSysEx (mi, b, 1L);      /* throw away F0 */

   if (dispopts & DISPF_Interpret)
      {
      GetSysEx (mi, &id, 1L);

      printf ("id=");

      if (id == MID_XAmerica && QuerySysEx(mi) > 2)
         {
         GetSysEx (mi, b, 2L);
         xid = MakeMIDX (id, b[0], b[1]);

         if (idname = finddesc(xid,xidlist,XIDS))
            printf ("%s ", idname);
         else
            printf ("%02x-%04x ", (UBYTE)(xid>16), (UWORD)xid);
         }
      else
         {
         if (idname = finddesc((ULONG)id,idlist,IDS))
            printf ("%s ",idname);
	 else
	    printf ("%02x ", id);
	 }
      }

   xgeneric(len);


  #if 0
   if (dispopts & DISPF_Interpret)
      {

      GetSysEx (mi, &id, 1L);

      if (id == MID_XAmerica && QuerySysEx(mi) > 2)
         {
         GetSysEx (mi, b, 2L);
         xid = MakeMIDX (id, b[0], b[1]);

         if (idname = finddesc(xid,xidlist,XIDS))
            {
            printf ("%s ", idname);
            switch (xid)
               {
               /* no manuf. specific processing yet */
               default:
               xgeneric();
               break;
               }
            }
         else
            {
            printf ("id=%02x-%04x ", (UBYTE)(xid>16), (UWORD)xid);
            xgeneric();
            }
         }
      else
         {
         if (idname = finddesc((ULONG)id,idlist,IDS))
            {
            printf ("%s ",idname);
            switch (id)
               {
               case MID_Yamaha:
               xyamaha();
               break;

               case MID_Kawai:
               xkawai();
               break;

               default:
               xgeneric();
               break;
               }
            }
         else
            {
            printf ("id=%02x ",id);
            xgeneric();
            }
         }
      }
   else
      {
      xgeneric();
      }
  #endif
}


#define SHORTSX 8

void xgeneric (ULONG len)
{
   ULONG datalen = QuerySysEx(mi) - 1;     /* remove F7 */
   UBYTE buf[SHORTSX];

   if (datalen > SHORTSX)
      {
      if (dispopts & DISPF_LongSysEx)
         {
         printf ("- %ld bytes\n", len);
         dumplong (datalen);
         printf ("\n");
         }
      else
         {
         GetSysEx (mi, buf, (long)SHORTSX);  /* should always get SHORTSX bytes unless QuerySysEx() is broken */
         dump (buf,SHORTSX);
         printf ("... (%ld total bytes)\n", len);
         }
      }
   else
      {
      GetSysEx (mi, buf, datalen);            /* should always get datalen bytes unless QuerySysEx() is broken */
      dump (buf,(int)datalen);
      printf ("\n");
      }
}


#if 0

void xyamaha(void)
{
   register unsigned chan;
   UBYTE stat;
   unsigned wd;
   UBYTE b[16];
   char b1[20],b2[20],b3[20],b4[20];

   if (GetSysEx (mi, &stat, 1L))
      {
      chan=(stat & 0xf) + 1;

      switch (stat & 0x70)
         {
	 case YAMAHA_PARAMCHANGE:
         GetSysEx (mi, b, 3L);
         wd = MIDIWord(b[0],b[1]);
         printf ("Param:%d %s:%s:%s %s\n", chan, num(b1,YAMAHA_PGROUP(wd)), num(b2,YAMAHA_PSUBGROUP(wd)), num(b3,YAMAHA_PNUM(wd)), num(b4,b[2]));
         break;

         case YAMAHA_DUMPREQ:
         GetSysEx (mi, b, 1L);
         printf ("Request:%d fmt=%s\n", chan, num(b1,b[0]));
         break;

         case YAMAHA_BULKDUMP:
         xyamahadump (chan);
         break;

	 default:
         printf ("Und#%02x ",stat);
         xgeneric();
         break;
         }
      }
   else
      {
       printf ("\n");
       return;
       }
}


void xyamahadump (unsigned chan)
{
   int packetsize;
   int first=1;
   UBYTE b[2];
   char b1[20];
   char s[64];
   long len;

   sprintf (s, "Dump:%d fmt=%s", chan, num(b1,*data++));

   while (GetSysEx (mi, b, 2L) == 2)
      {
      packetsize = MIDIWord(b[0],b[1]);

      if (first) printf ("%s", s);
      else printf ("%*s", (int)strlen(s)+13, "...");

      /* printf (" size=%s sum=%02x", num(b1,packetsize), data[packetsize]); */
      printf (" size=%s", num(b1,packetsize));

      len = QuerySysEx(mi)-3;
      if (packetsize > len)
         {     /* !!! can only detect a loss in the last packet */
         printf (" (err: lost %s byte(s))", num(b1,(int)(packetsize-(len))));
         }
      printf ("\n");

      if (dispopts & DISPF_LongSysEx)
         {
         dumplong ((long)packetsize);
         printf ("\n");
	 }
      GetSysEx (mi,b,1L);     /* !!! sum */

      first = 0;
      }
}


void xkawai(void)
{
   xgeneric();
}

#endif


#define ROWLEN 16

int chkskip(void);

void dumplong (long len)
{
   int ilen;
   UBYTE buf[ROWLEN];

   chkskip();          /* clear CTRL_D signal */
   printf ("    ");

   while (ilen = GetSysEx (mi, buf, (long)MIN(len,ROWLEN)))
      {
      if (chkskip())
         {
         printf ("^D\n");
         break;
         }
      dump (buf,ilen);
      printf ("%*s", (ROWLEN-ilen) * 3 + 1, "");
      dumpascii (buf,ilen);
      printf ("\n    ");
      len -= ilen;
      }
}

#if 0
void getrow(UBYTE *buf)
{
   int len;

   if (len = GetSysEx(mi,buf,(long)ROWLEN))
      {
      if (buf[len-1] == MS_EOX) len--;
      }
   return len;
}
#endif

void dump (register UBYTE *s, register int len)
{
   while (len--) printf ("%02x ",*s++);
}

void dumpascii (register UBYTE *s, register int len)
{
   register int c;

   while (len--)
      {
      c = *s++;
      putchar ( isprint(c) ? c : '.' );
      }
}


int chkskip(void)
{
   return (SetSignal (0L,SIGBREAKF_CTRL_D) & SIGBREAKF_CTRL_D) != 0;
}


/* print undefined message (maybe raw?) */

void prtund (MidiMsg *msg)
{
   printf ("Und#%02x %02x %02x", msg->mm_Status, msg->mm_Data1, msg->mm_Data2);
}
/* eof */
