/*
 * dim (after mftext by Tim Thompson)
 * 
 * Convert a MIDI file to verbose text.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#define NO_LC_DEFINES
#include "midifile.h"
/* #include "adagio.h" */
/* #include "allphase.h" */



struct short_voice_type {
	char *vname;
#define SOLO_MASK 1
	char flags;
};


/*
 * names and info for drumsets
 */
struct short_voice_type gs_drumset[49] = {
/* 0*/	{ "Standard",	0 },
/* 1*/	{ 0,	0 },
/* 2*/	{ 0,	0 },
/* 3*/	{ 0,	0 },
/* 4*/	{ 0,	0 },
/* 2*/	{ 0,	0 },
/* 6*/	{ 0,	0 },
/* 7*/	{ 0,	0 },
/* 8*/	{ "Room",	0 },
/* 9*/	{ 0,	0 },
/* 10*/	{ 0,	0 },
/* 11*/	{ 0,	0 },
/* 12*/	{ 0,	0 },
/* 13*/	{ 0,	0 },
/* 14*/	{ 0,	0 },
/* 15*/	{ 0,	0 },
/* 16*/	{ "Power",	0 },
/* 17*/	{ 0,	0 },
/* 18*/	{ 0,	0 },
/* 19*/	{ 0,	0 },
/* 20*/	{ 0,	0 },
/* 21*/	{ 0,	0 },
/* 22*/	{ 0,	0 },
/* 23*/	{ 0,	0 },
/* 24*/	{ "Electronic",	0 },
/* 25*/	{ "TR-808",	0 },
/* 26*/	{ 0,	0 },
/* 27*/	{ 0,	0 },
/* 28*/	{ 0,	0 },
/* 29*/	{ 0,	0 },
/* 30*/	{ 0,	0 },
/* 31*/	{ 0,	0 },
/* 32*/	{ "Jazz",	0 },
/* 33*/	{ 0,	0 },
/* 34*/	{ 0,	0 },
/* 35*/	{ 0,	0 },
/* 36*/	{ 0,	0 },
/* 37*/	{ 0,	0 },
/* 38*/	{ 0,	0 },
/* 39*/	{ 0,	0 },
/* 40*/	{ "Brush",	0 },
/* 41*/	{ 0,	0 },
/* 42*/	{ 0,	0 },
/* 43*/	{ 0,	0 },
/* 44*/	{ 0,	0 },
/* 45*/	{ 0,	0 },
/* 46*/	{ 0,	0 },
/* 47*/	{ 0,	0 },
/* 48*/	{ "Orchestra",	0 }
};

/*
 * names and info for general-midi instruments
 */
struct short_voice_type gm_voice[256] = {
/* 0*/	{ "Acoustic Grand Piano",0 },
/* 1*/	{ "Bright Acoustic Grand",0 },
/* 2*/	{ "Electric Grand Piano",0 },
/* 3*/	{ "Honky-tonk Piano",	0 },
/* 4*/	{ "Rhodes Piano",	0 },
/* 2*/	{ "Chorused Piano",	0 },
/* 6*/	{ "Harpsichord",	0 },
/* 7*/	{ "Clavinet",		0 },
/* 8*/	{ "Celesta",		0 },
/* 9*/	{ "Glockenspiel",	0 },
/* 10*/	{ "Musicbox",		0 },
/* 11*/	{ "Vibraphone",		0 },
/* 12*/	{ "Marimba",		0 },
/* 13*/	{ "Xylophone",		0 },
/* 14*/	{ "Tubular Bells",	0 },
/* 15*/	{ "Dulcimer",		0 },
/* 16*/	{ "Hammond Organ",	0 },
/* 17*/	{ "Percussive Organ",	0 },
/* 18*/	{ "Rock Organ",		0 },
/* 19*/	{ "Church Organ",	0 },
/* 20*/	{ "Reed Organ",		0 },
/* 21*/	{ "Accordion",		0 },
/* 22*/	{ "Harmonica",		1 },
/* 23*/	{ "Tango Accordion",	0 },
/* 24*/	{ "Nylon Guitar",	0 },
/* 25*/	{ "Steel Guitar",	0 },
/* 26*/	{ "Jazz Guitar",	1 },
/* 27*/	{ "Clean Guitar",	0 },
/* 28*/	{ "Muted Guitar",	1 },
/* 29*/	{ "Overdriven Guitar",	0 },
/* 30*/	{ "Distortion Guitar",	0 },
/* 31*/	{ "Guitar Harmonics",	0 },
/* 32*/	{ "Acoustic Bass",	0 },
/* 33*/	{ "Finger Bass",	0 },
/* 34*/	{ "Pick Bass",		0 },
/* 35*/	{ "Fretless Bass",	0 },
/* 36*/	{ "Slap Bass 1",	0 },
/* 37*/	{ "Slap Bass 2",	0 },
/* 38*/	{ "Synth Bass 1",	0 },
/* 39*/	{ "Synth Bass 2",	0 },
/* 40*/	{ "Violin",		0 },
/* 41*/	{ "Viola",		0 },
/* 42*/	{ "Cello",		0 },
/* 43*/	{ "Contrabass",		0 },
/* 44*/	{ "Tremolo Strings",	0 },
/* 45*/	{ "Pizzicato String",	0 },
/* 46*/	{ "Orchestral Harp",	0 },
/* 47*/	{ "Timpani",		1 },
/* 48*/	{ "String Ensemble 1",	0 },
/* 49*/	{ "String Ensemble 2",	0 },
/* 50*/	{ "Synth Strings 1",	0 },
/* 51*/	{ "Synth Strings 2",	0 },
/* 52*/	{ "Choir Aahs",		0 },
/* 53*/	{ "Voice Oohs",		0 },
/* 54*/	{ "Synth Voice",	0 },
/* 55*/	{ "Orchestra Hit",	0 },
/* 56*/	{ "Trumpet",		1 },
/* 57*/	{ "Trombone",		1 },
/* 58*/	{ "Tuba",		1 },
/* 59*/	{ "Muted Trumpet",	1 },
/* 60*/	{ "French Horn",	0 },
/* 61*/	{ "Brass Section",	0 },
/* 62*/	{ "Synth Brass 1",	0 },
/* 63*/	{ "Synth Brass 2",	0 },

/* 64*/	{ "Soprano Sax",	1 },
/* 65*/	{ "Alto Sax",		1 },
/* 66*/	{ "Tenor Sax",		1 },
/* 67*/	{ "Baritone Sax",	1 },
/* 68*/	{ "Oboe",		1 },
/* 69*/	{ "English Horn",	1 },
/* 70*/	{ "Bassoon",		1 },
/* 71*/	{ "Clarinet",		1 },
/* 72*/	{ "Piccolo",		1 },
/* 73*/	{ "Flute",		1 },
/* 74*/	{ "Recorder",		1 },
/* 75*/	{ "Pan Flute",		1 },
/* 76*/	{ "Bottle Blow",	1 },
/* 77*/	{ "Shakuhachi",		1 },
/* 78*/	{ "Whistle",		1 },
/* 79*/	{ "Ocarina",		1 },
/* 80*/	{ "Lead1 squareea",	0 },
/* 81*/	{ "Lead2 sawtooth",	0 },
/* 82*/	{ "Lead3 calliope",	0 },
/* 83*/	{ "Lead4 chiff",	0 },
/* 84*/	{ "Lead5 charang",	0 },
/* 85*/	{ "Lead6 voice",	0 },
/* 86*/	{ "Lead7 fifths",	0 },
/* 87*/	{ "Lead8 brass+ld",	0 },
/* 88*/	{ "Pad1 newage",	0 },
/* 89*/	{ "Pad2 warm",		0 },
/* 90*/	{ "Pad3 polysynth",	0 },
/* 91*/	{ "Pad4 choir",		0 },
/* 92*/	{ "Pad5 bowed",		0 },
/* 93*/	{ "Pad6 metallic",	0 },
/* 94*/	{ "Pad7 halo",		0 },
/* 95*/	{ "Pad8 sweep",		0 },
/* 96*/	{ "FX1 rain",		0 },
/* 97*/	{ "FX2 soundtrack",	0 },
/* 98*/	{ "FX3 crystal",	1 },
/* 99*/	{ "FX4 atmosphere",	1 },
/*100*/	{ "FX5 brightness",	1 },
/*101*/	{ "FX6 goblins",	1 },
/*102*/	{ "FX7 echoes",		0 },
/*103*/	{ "FX8 sci-fi",		1 },
/*104*/	{ "Sitar",		0 },
/*105*/	{ "Banjo",		0 },
/*106*/	{ "Shamisen",		0 },
/*107*/	{ "Koto",		0 },
/*108*/	{ "Kalimba",		0 },
/*109*/	{ "Bagpipe",		0 },
/*110*/	{ "Fiddle",		0 },
/*111*/	{ "Shanai",		0 },
/*112*/	{ "Tinkle Bell",	1 },
/*113*/	{ "Agogo Bells",	1 },
/*114*/	{ "Steel Drums",	1 },
/*115*/	{ "Woodblock",		1 },
/*116*/	{ "Taiko Drum",		1 },
/*117*/	{ "Melodic Tom",	1 },
/*118*/	{ "Synth Drum",		1 },
/*119*/	{ "Reverse Cymbal",	1 },
/*120*/	{ "Guitar Fret Noise",	1 },
/*121*/	{ "Breath Noise",	1 },
/*122*/	{ "Seashore",		1 },
/*123*/	{ "Bird Tweet",		1 },
/*124*/	{ "Telephone Ring",	1 },
/*125*/	{ "Helicopter Blade",	1 },
/*126*/	{ "Applause/Noise",	1 },
/*127*/	{ "Gunshot",		1 },
/*128+000*/ {NULL, 0},
/*128+001*/ {NULL, 0},
/*128+002*/ {NULL, 0},
/*128+003*/ {NULL, 0},
/*128+004*/ {NULL, 0},
/*128+005*/ {NULL, 0},
/*128+006*/ {NULL, 0},
/*128+007*/ {NULL, 0},
/*128+008*/ {NULL, 0},
/*128+009*/ {NULL, 0},
/*128+010*/ {NULL, 0},
/*128+011*/ {NULL, 0},
/*128+012*/ {NULL, 0},
/*128+013*/ {NULL, 0},
/*128+014*/ {NULL, 0},
/*128+015*/ {NULL, 0},
/*128+016*/ {NULL, 0},
/*128+017*/ {NULL, 0},
/*128+018*/ {NULL, 0},
/*128+019*/ {NULL, 0},
/*128+020*/ {NULL, 0},
/*128+021*/ {NULL, 0},
/*128+022*/ {NULL, 0},
/*128+023*/ {NULL, 0},
/*128+024*/ {NULL, 0},
/*128+025*/ {NULL, 0},
/*128+026*/ {NULL, 0},
/*128+027*/ {"High_Q", 0},
/*128+028*/ {"Slap", 0},
/*128+029*/ {"Scratch_Push", 0},
/*128+030*/ {"Scratch_Pull", 0},
/*128+031*/ {"Sticks", 0},
/*128+032*/ {"Square_Click", 0},
/*128+033*/ {"Metronome_Click", 0},
/*128+034*/ {"Metronome_Bell", 0},

/*128+035*/ {"Acoustic Bass Drum", 0},
/*128+036*/ {"Bass Drum 1", 0},
/*128+037*/ {"Side Stick", 0},
/*128+038*/ {"Acoustic Snare", 0},
/*128+039*/ {"Hand Clap", 0},
/*128+040*/ {"Electric Snare", 0},
/*128+041*/ {"Low Floor Tom", 0},
/*128+042*/ {"Closed High Hat", 0},

/*128+043*/ {"Hi Floor Tom", 0},
/*128+044*/ {"Pedal High Hat", 0},
/*128+045*/ {"Low Tom", 0},
/*128+046*/ {"Open High Hat", 0},
/*128+047*/ {"Low-Mid Tom", 0},
/*128+048*/ {"High-Mid Tom", 0},
/*128+049*/ {"Crash Cymbal 1", 0},
/*128+050*/ {"High Tom", 0},

/*128+051*/ {"Ride Cymbal 1", 0},
/*128+052*/ {"Chinese Cymbal", 0},
/*128+053*/ {"Ride Bell", 0},
/*128+054*/ {"Tambourine", 0},
/*128+055*/ {"Splash Cymbal", 0},
/*128+056*/ {"Cow Bell", 0},
/*128+057*/ {"Crash Cymbal 2", 0},
/*128+058*/ {"Vibraslap", 0},

/*128+059*/ {"Ride Cymbal 2", 0},
/*128+060*/ {"High Bongo", 0},
/*128+061*/ {"Low Bongo", 0},
/*128+062*/ {"Mute High Conga", 0},
/*128+063*/ {"Open High Conga", 0},
/*128+064*/ {"Low Conga", 0},
/*128+065*/ {"High Timbale", 0},
/*128+066*/ {"Low Timbale", 0},

/*128+067*/ {"High Agogo", 0},
/*128+068*/ {"Low Agogo", 0},
/*128+069*/ {"Cabasa", 0},
/*128+070*/ {"Maraccas", 0},
/*128+071*/ {"Short Whistle", 0},
/*128+072*/ {"Long Whistle", 0},
/*128+073*/ {"Short Guiro", 0},
/*128+074*/ {"Long Guiro", 0},

/*128+075*/ {"Claves", 0},
/*128+076*/ {"High Wood Block", 0},
/*128+077*/ {"Low Wood Block", 0},
/*128+078*/ {"Mute Cuica", 0},
/*128+079*/ {"Open Cuica", 0},
/*128+080*/ {"Mute Triangle", 0},
/*128+081*/ {"Open Triangle", 0},

/*128+082*/ {"Small Shaker", 0},
/*128+083*/ {"Sleigh_Bells", 0},
/*128+084*/ {"Bell_Tree", 0},
/*128+085*/ {"Castanet", 0},
/*128+086*/ {"Short_Taiko_Hit", 0},
/*128+087*/ {"Long_Taiko_Hit", 0},
	{NULL, 0}
};


void prtime();
extern int Mf_nomerge;
static FILE *F;
int SECONDS;      /* global that tells whether to display seconds or ticks */
int division;        /* from the file header */
long tempo = 500000; /* the default tempo is 120 beats/minute */
static int quiet = 0;
static long last_time = 0;
static long last_realtime = 0;
static int delta_flag = 0;

filegetc()
{
	return(getc(F));
}


main(argc,argv)
char **argv;
{
	extern int optind;
	extern char * optarg;
	int opt;
	FILE *efopen();

	SECONDS = 0;


	while((opt = getopt(argc,argv,"sqd")) != -1)
	    switch(opt) {
		case 's' : SECONDS = 1; break;
		case 'q': quiet = 1; break;
		case 'd': delta_flag = 1; break;
	}

	if ( argc == optind )
	    F = stdin;
	else
	    F = efopen(argv[optind],"r");

	initfuncs();
	Mf_getc = filegetc;
	midifile();
	Mf_nomerge = 1;
	fclose(F);
	exit(0);
}

#include <errno.h>
#include <string.h>

FILE *
efopen(name,mode)
char *name;
char *mode;
{
	FILE *f;
	extern int errno;
	/*extern char *sys_errlist[];*/
	/*extern int sys_nerr;*/
	const char *errmess;
	char *midname;

	if ( (f=fopen(name, mode)) == NULL ) {
		midname = (char *)malloc(strlen(name)+5);
		strcpy(midname, name);
		strcat(midname, ".mid");
		if ( (f=fopen(midname, mode)) != NULL ) return(f);
		(void) fprintf(stderr,"*** ERROR *** Cannot open '%s'!\n",name);
		errmess = strerror(errno);
		(void) fprintf(stderr,"************* Reason: %s\n",errmess);
		exit(1);
	}
	return(f);
}

void
error(s)
char *s;
{
	fprintf(stderr,"Error: %s\n",s);
	exit(1);
}

void
txt_header(format,ntrks,ldivision)
{
        division = ldivision; 
	if (delta_flag)
		printf("Header DELTA format=%d ntrks=%d division=%d\n",format,ntrks,division);
	else
		printf("Header format=%d ntrks=%d division=%d\n",format,ntrks,division);
}

void
txt_trackstart()
{
	printf("Track start\n");
	last_time = 0;
}

void
txt_trackend()
{
	printf("Track end\n");
}

char *notename[12] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };

void
txt_noteon(chan,pitch,vol)
{
	char *drumname;

	if (quiet) return;
	prtime();
	if (chan == 9 || chan == 15) {
	    drumname = gm_voice[128+pitch].vname;
	    if (drumname == NULL) drumname = "unknown drum";
	    printf("Drum on, chan=%d %s (%s%d)[%d] vol=%d\n", chan+1,
		drumname, notename[pitch%12], pitch/12-2, pitch, vol);
	}
	else printf("Note on, chan=%d pitch=%s%d[%d] vol=%d\n", chan+1,
	    notename[pitch % 12], pitch/12 - 2, pitch, vol);
}

void
txt_noteoff(chan,pitch,vol)
{
	char *drumname;

	if (quiet) return;
	prtime();
	if (chan == 9 || chan == 15) {
	    drumname = gm_voice[128+pitch].vname;
	    if (drumname == NULL) drumname = "unknown drum";
	    printf("Drum off, chan=%d %s (%s%d)[%d] vol=%d\n", chan+1,
		drumname, notename[pitch%12], pitch/12-2, pitch, vol);
	}
	else printf("Note off, chan=%d pitch=%s%d[%d] vol=%d\n", chan+1,
		notename[pitch % 12], pitch/12 - 2, pitch, vol);
}

void
txt_pressure(chan,pitch,press)
{
	prtime();
	printf("Pressure, chan=%d pitch=%d press=%d\n",chan+1,pitch,press);
}
/**
     Table 3: Status Bytes 176-191; Control and Mode Changes (per channel)
------------------------------------------------------------------------------
 2nd Byte Value |  Function                  |  3rd Byte
Dec |                                        | Value  |  Use
- - | - - - - - - - - - - - - - - - - - - - -|- - - - | - - - - 
  0 | Continuous controller #0               | 0-127  |  MSB
  1 | Modulation wheel                       | 0-127  |  MSB
  2 | Breath control                         | 0-127  |  MSB
  3 | Continuous controller #3               | 0-127  |  MSB
  4 | Foot controller                        | 0-127  |  MSB
  5 | Portamento time                        | 0-127  |  MSB
  6 | Data Entry                             | 0-127  |  MSB
  7 | Main Volume                            | 0-127  |  MSB
  8 | Continuous controller #8               | 0-127  |  MSB
	...
 31 | Continuous controller #31              | 0-127  |  MSB
 32 | Continuous controller #0               | 0-127  |  LSB
 33 | Modulation wheel                       | 0-127  |  LSB
 34 | Breath control                         | 0-127  |  LSB
 35 | Continuous controller #3               | 0-127  |  LSB
 36 | Foot controller                        | 0-127  |  LSB
 37 | Portamento time                        | 0-127  |  LSB
 38 | Data entry                             | 0-127  |  LSB
 39 | Main volume                            | 0-127  |  LSB
 40 | Continuous controller #8               | 0-127  |  LSB
	...
 63 | Continuous controller #31              | 0-127  |  LSB
 64 | Damper pedal on/off (Sustain)          | 0=off  | 127=on
 65 | Portamento on/off                      | 0=off  | 127=on
 66 | Sustenuto on/off                       | 0=off  | 127=on
 67 | Soft pedal on/off                      | 0=off  | 127=on
 68 | Undefined on/off                       | 0=off  | 127=on
	...
 95 | Undefined on/off                       | 0=off  | 127=on
                                              -----------------
 96 | Data entry +1                          |       127
 97 | Data entry -1                          |       127
 98 | Undefined                              |        ?
	...
121 | Undefined                              |        ?
122 | Local control on/off                   | 0=off    127=on   
123 | All notes off (!!)                     |        0
124 | Omni mode off (includes all notes off) |        0
125 | Omni mode on (includes all notes off)  |        0
126 | Poly mode on/off(includes all notes off)|       **
127 | Poly mode on(incl mono=off&all notes off)|      0

 **Note: This equals the number of channels, or zero if the number of channels
         equals the number of voices in the receiver.

**/

char *cname1[8] = {
	"Continuous controller #0",
	"Modulation wheel",
	"Breath control",
	"Continuous controller #3",
	"Foot controller",
	"Portamento time",
	"Data Entry",
	"Main Volume"
};

char *cname2[7] = {
	"Reset controllers",
	"Local control",
	"All notes off",
	"Omni mode off",
	"Omni mode on",
	"Poly mode",
	"Poly mode on"
};

#define GENERAL_4 68
#define HOLD_2 69
#define GENERAL_5 80
#define GENERAL_6 81
#define GENERAL_7 82
#define GENERAL_8 83
#define TREMULO_DEPTH 92
#define CHORUS_DEPTH 93
#define DETUNE 94
#define PHASER_DEPTH 95

void
txt_parameter(chan,control,value)
{	static int nrpn_msb, nrpn_lsb;

	prtime();
	printf("Parameter, chan=%d ", chan+1);

	if (control < 8) printf("%s[%d] msb=%d\n", cname1[control], control, value);
	else if (control == 10) printf("Pan[%d] %d[=%d]\n", control, value-64, value);
	else if (control == 11) printf("Expression[%d] =%d\n", control, value);
	else if (control < 32) printf("Continuous controller [%d] msb=%d\n", control, value);
	else if (control < 40) printf("%s[%d] lsb=%d\n", cname1[control-32], control, value);
	else if (control < 64) printf("Continuous controller %d[%d] lsb=%d\n",
					control-32, control, value);
	else if (control == 64) printf("Damper pedal[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 65) printf("Portamento[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 66) printf("Sostenuto[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 67) printf("Soft pedal[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 69) printf("Hold2[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 71) printf("Harmonic Content[%d] %s[=%d]\n", control,
		(value==64)? "off":"on", value);
	else if (control == 72) printf("Release Time[%d] %s[=%d]\n", control,
		(value==64)? "off":"on", value);
	else if (control == 73) printf("Attack Time[%d] %s[=%d]\n", control,
		(value==64)? "off":"on", value);
	else if (control == 74) printf("Brightnesss[%d] %s[=%d]\n", control,
		(value==64)? "off":"on", value);
	else if (control == 91) printf("Reverb[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 92) printf("Tremulo depth[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 93) printf("Chorus depth[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 94) printf("Detune[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control == 95) printf("Phaser depth[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control < 96) printf("Undefined[%d] %s[=%d]\n", control,
		(value==0)? "off":"on", value);
	else if (control < 98) printf("Data entry[%d] %c1%s[=%d]\n", control,
		(control==96)? '+':'-', (value==127)? "":" (undef. value)", value);
/*
XG/GS NRPNs
	MSB LSB Description
	01h 08h Vibrato rate
	01h 09h Vibrato depth
	01h 0Ah Vibrato delay
	01h 20h Filter cutoff frequency
	01h 21h Filter resonance
	01h 63h Envelope attack rate
	01h 64h Envelope decay rate
	01h 66h Envelope release rate
*/
	else if (control == 99) {
		nrpn_msb = value;
		printf("Unregistered Param 1[%d] val=%d\n", control, value);
	}
	else if (control == 98) {
		nrpn_lsb = value;
		printf("Unregistered Param 2[%d] val=%d ", control, value);
		if (nrpn_msb == 1) switch (nrpn_lsb) {
		    case 0x08: printf("Vibrato rate\n"); break;
		    case 0x09: printf("Vibrato depth\n"); break;
		    case 0x0a: printf("Vibrato delay\n"); break;
		    case 0x20: printf("Filter cutoff frequency\n"); break;
		    case 0x21: printf("Filter resonance\n"); break;
		    case 0x63: printf("Envelope attack rate\n"); break;
		    case 0x64: printf("Envelope decay rate\n"); break;
		    case 0x66: printf("Envelope release rate\n"); break;
		    default: printf("(unknown)\n"); break;
		}
		else {
		    switch (nrpn_msb) {
		    case 0x14: printf("Filter cutoff frequency"); break;
		    case 0x15: printf("Filter resonance"); break;
		    case 0x16: printf("Envelope attack rate"); break;
		    case 0x17: printf("Envelope decay rate"); break;
		    case 0x18: printf("Pitch coarse"); break;
		    case 0x19: printf("Pitch fine"); break;
		    case 0x1a: printf("Level"); break;
		    case 0x1c: printf("Panpot"); break;
		    case 0x1d: printf("Reverb send level"); break;
		    case 0x1e: printf("Chorus send level"); break;
		    case 0x1f: printf("Variation send level"); break;
		    default:   printf("(unknown)"); break;
		    }
		    printf(" of drum %d\n", nrpn_lsb);
		}
	}
/*
XG 	14h rr Filter cutoff frequency of specified drum sound
XG 	15h rr Filter resonance of specified drum sound
XG 	16h rr Envelope attack rate of specified drum sound
XG 	17h rr Envelope decay rate of specified drum sound
	18h rr Pitch coarse of specified drum sound
	19h rr Pitch fine of specified drum sound
	1Ah rr Level of specified drum sound
	1Ch rr Panpot of specified drum sound
	1Dh rr Reverb send level of specified drum sound
	1Eh rr Chorus send level of specified drum sound
XG 	1Fh rr Variation send level of specified drum sound
*/
	else if (control == 100) printf("Pitch bend sensitivity[%d] =%d\n", control, value);
	else if (control == 101) printf("Fine tuning[%d] =%d\n", control, value);
	else if (control == 102) printf("Coarse tuning[%d] =%d\n", control, value);
	else if (control < 121) printf("Undefined [%d] val=%d\n", control, value);
	else if (control < 128) printf("%s[%d] val=%d\n", cname2[control-121], control, value);
	else printf("impossible controller[%d] val=%d\n", control, value);
}

void
txt_pitchbend(chan,msb,lsb)
{
	prtime();
	printf("Pitchbend, chan=%d msb=%d lsb=%d\n",chan+1,msb,lsb);
}

void
txt_program(chan,program)
{
	prtime();
	if ( (chan == 9||chan == 15) && program <= 48 && gs_drumset[program].vname )
	  printf("Drumset, chan=%d program=%s[%d]\n",chan+1,gs_drumset[program].vname,program);
	else printf("Program, chan=%d program=%s[%d]\n",chan+1,gm_voice[program].vname,program);
}

void
txt_chanpressure(chan,press)
{
	prtime();
	printf("Channel pressure, chan=%d pressure=%d\n",chan+1,press);
}
/**
	Sequential Circuits   1	     Bon Tempi	   0x20	    Kawai     0x40
	Big Briar	      2	     S.I.E.L.	   0x21	    Roland    0x41
	Octave / Plateau      3				    Korg      0x42
	Moog		      4	     SyntheAxe	   0x23	    Yamaha    0x43
	Passport Designs      5
	Lexicon		      6
	PAIA		      0x11
	Simmons		      0x12
	Gentle Electric	      0x13
	Fairlight	      0x14
**/
char *man_id[68] = {
	"",
	"Sequential Circuits",
	"Big Briar",
	"Octave/Plateau",
	"Moog",
	"Passport Designs",
	"Lexicon",
	"","","","","","","","","",

	"",
	"PAIA",
	"Simmons",
	"Gentle Electric",
	"Fairlight",
	"","","","","","","","","","","",

	"Bon Tempi",
	"S.I.E.L.",
	"",
	"SyntheAxe",
	"","","","","","","","","","","","",

	"Linux","","","","","","","","","","","","","","","",

	"Kawai",
	"Roland",
	"Korg",
	"Yamaha"
};


char *yam_prm_name[41] = {
/*00 1 00 - 20 */ "ELEMENT RESERVE", /* 0 - 32 part10=0, other =2 */
/*01 1 00 - 7F */ "BANK SELECT MSB", /* 0 - 127 part10=7F, other=0 */
/*02 1 00 - 7F  */ "BANK SELECT LSB", /* 0 - 127 00 */
/*03 1 00 - 7F */ "PROGRAM NUMBER", /* 1 - 128 00 */
/*04 1 00 - 0F, 7F */ "Rcv CHANNEL", /* 1 - 16, OFF Part No. */
/*05 1 00 - 01 */ "MONO/POLY MODE", /* 0:MONO 01 1:POLY */
/*06 1 00 - 02 */ "SAME NOTE NUMBER", /* 0:SINGLE 01 KEY ON ASSIGN 1:MULTI 2:INST (for DRUM) */
/*07 1 00 - 05 */ "PART MODE", /* 0:NORMAL 00 (Part other than 10) 1:DRUM 02 (Part10) 2 - 5:DRUMS1 - 4 04, 05 = [Ext.] */
/*08 1 28 - 58 */ "NOTE SHIFT", /* -24 - +24[semitones] 40 */
/*09 2 00 - FF */ "DETUNE", /* -12.8 - +12.7[Hz] 08 00 */
/*0A 	 */ "DETUNE hb", /* 1st bit3-0Ýbit7-4 2nd bit3-0Ýbit3-0 */
/*0B 1 00 - 7F */ "VOLUME", /* 0 - 127 64 */
/*0C 1 00 - 7F */ "VELOCITY SENSE DEPTH", /* 0 - 127 40 */
/*0D 1 00 - 7F */ "VELOCITY SENSE OFFSET", /* 0 - 127 40 */
/*0E 1 00 - 7F */ "PAN", /* 0:random 40 L63...C...R63(1...64...127) */
/*0F 1 00 - 7F */ "NOTE LIMIT LOW", /* C-2 - G8 00 */
/*10 1 00 - 7F */ "NOTE LIMIT HIGH", /* C-2 - G8 7F */
/*11 1 00 - 7F */ "DRY LEVEL", /* 0 - 127 7F */
/*12 1 00 - 7F */ "CHORUS SEND", /* 0 - 127 00 */
/*13 1 00 - 7F */ "REVERB SEND", /* 0 - 127 28 */
/*14 1 00 - 7F */ "VARIATION SEND", /* 0 - 127 00 */
/*15 1 00 - 7F */ "VIBRATO RATE", /* -64 - +63 40 */
/*16 1 00 - 7F */ "VIBRATO DEPTH", /* -64 - +63 40 */
/*17 1 00 - 7F */ "VIBRATO DELAY", /* -64 - +63 40 */
/*18 1 00 - 7F */ "FILTER CUTOFF FREQUENCY", /* -64 - +63 40 */
/*19 1 00 - 7F */ "FILTER RESONANCE", /* -64 - +63 40 */
/*1A 1 00 - 7F */ "EG ATTACK TIME", /* -64 - +63 40 */
/*1B 1 00 - 7F */ "EG DECAY TIME", /* -64 - +63 40 */
/*1C 1 00 - 7F */ "EG RELEASE TIME", /* -64 - +63 40 */
/*1D 1 28 - 58 */ "MW PITCH CONTROL", /* -24 - +24[semitones] 40 */
/*1E 1 00 - 7F */ "MW FILTER CONTROL", /* -9600 - +9450[cent] 40 */
/*1F 1 00 - 7F */ "MW AMPLITUDE CONTROL", /* -100 - +100[%] 40 */
/*20 1 00 - 7F */ "MW LFO PMOD DEPTH", /* 0 - 127 0A */
/*21 1 00 - 7F */ "MW LFO FMOD DEPTH", /* 0 - 127 00 */
/*22 1 00 - 7F */ "MW LFO AMOD DEPTH [Ext.]", /* 0 - 127 00 */
/*23 1 28 - 58 */ "BEND PITCH CONTROL", /* -24 - +24[semitones] 42 */
/*24 1 00 - 7F */ "BEND FILTER CONTROL", /* -9600 - +9450[cent] 40 */
/*25 1 00 - 7F */ "BEND AMPLITUDE CONTROL", /* -100 - +100[%] 40 */
/*26 1 00 - 7F */ "BEND LFO PMOD DEPTH", /* 0 - 127 00 */
/*27 1 00 - 7F */ "BEND LFO FMOD DEPTH", /* 0 - 127 00 */
/*28 1 00 - 7F */ "BEND LFO AMOD DEPTH [Ext.]" /* 0 - 127 00 */
};

char *yam_reverb_type[20][4] = {
/*000 0 	*/ "NO EFFECT", "", "", "",
/*001 1 	*/ "HALL1", 		"HALL2", "", "",
/*002 2 	*/ "ROOM1", 		"ROOM2", "", 		"ROOM3",
/*003 3 	*/ "STAGE1", 		"STAGE2", "", "",
/*004 4 	*/ "PLATE", "", "", "",
/*005 5 	*/ "NO EFFECT", "", "", "",
/*006 5 	*/ "NO EFFECT", "", "", "",
/*007 5 	*/ "NO EFFECT", "", "", "",
/*008 5 	*/ "NO EFFECT", "", "", "",
/*009 5 	*/ "NO EFFECT", "", "", "",
/*010 5 	*/ "NO EFFECT", "", "", "",
/*011 5 	*/ "NO EFFECT", "", "", "",
/*012 5 	*/ "NO EFFECT", "", "", "",
/*013 5 	*/ "NO EFFECT", "", "", "",
/*014 5 	*/ "NO EFFECT", "", "", "",
/*015 F 	*/ "NO EFFECT", "", "", "",
/*016 10 	*/ "WHITE ROOM", "", "", "",
/*017 11 	*/ "TUNNEL", "", "", "",
/*018 12 	*/ "CANYON", "", "", "",
/*019 13 	*/ "BASEMENT", "", "", ""
};

char *yam_chorus_type[8][4] = {
/*065 41 	*/ "CHORUS1", 	"CHORUS2", 	"CHORUS3", 	"CHORUS4",
/*066 42 	*/ "CELESTE1", 	"CELESTE2", 	"CELESTE3", 	"CELESTE4",
/*067 43 	*/ "FLANGER1", 	"FLANGER2", 	"FLANGER3", "",
/*068 44 	*/ "SYMPHONIC", "", "", "",
/*069 45 	*/ "NO EFFECT", "", "", "",
/*070 46 	*/ "NO EFFECT", "", "", "",
/*071 47 	*/ "NO EFFECT", "", "", "",
/*072 48 	*/ "PHASER", "", "", ""
};

char *yam_variation_type[87][4] = {
/*013 C 	*/ "NO EFFECT or THRU", "", "", "",
/*001 1 	*/ "HALL1", 		"HALL2", "", "",
/*002 2 	*/ "ROOM1", 		"ROOM2", 		"ROOM3", "",
/*003 3 	*/ "STAGE1", 		"STAGE2", "", "",
/*004 4 	*/ "PLATE", "", "", "",
/*005 5 	*/ "DELAY L,C,R", "", "", "",
/*006 6 	*/ "DELAY L,R", "", "", "",
/*007 7 	*/ "ECHO", "", "", "",
/*008 8 	*/ "CROSS DELAY", "", "", "",
/*009 9 	*/ "ER1", 		"ER2", "", "",
/*010 A 	*/ "GATE REVERB", "", "", "",
/*011 B 	*/ "REVERSE GATE", "", "", "",
/*012 C 	*/ "NO EFFECT or THRU", "", "", "",
/*013 C 	*/ "NO EFFECT or THRU", "", "", "",
/*014 C 	*/ "NO EFFECT or THRU", "", "", "",
/*015 C 	*/ "NO EFFECT or THRU", "", "", "",
/*016 C 	*/ "NO EFFECT or THRU", "", "", "",
/*017 C 	*/ "NO EFFECT or THRU", "", "", "",
/*018 C 	*/ "NO EFFECT or THRU", "", "", "",
/*019 13 	*/ "NO EFFECT or THRU", "", "", "",
/*020 14 	*/ "KARAOKE1", 	"KARAOKE2", 	"KARAOKE3", "",
/*064 40 	*/ "THRU", "", "", "",
/*065 41 	*/ "CHORUS1", 	"CHORUS2", 	"CHORUS3", 	"CHORUS4",
/*066 42 	*/ "CELESTE1", 	"CELESTE2", 	"CELESTE3", 	"CELESTE4",
/*067 43 	*/ "FLANGER1", 	"FLANGER2", 	"FLANGER3", "",
/*068 44 	*/ "SYMPHONIC", "", "", "",
/*069 45 	*/ "ROTARY SPEAKER", "", "", "",
/*070 46 	*/ "TREMOLO", "", "", "",
/*071 47 	*/ "AUTO PAN", "", "", "",
/*072 48 	*/ "PHASER1", "", "",					"PHASER2",
/*073 49 	*/ "DISTORTION", "", "", "",
/*074 4A 	*/ "OVER DRIVE", "", "", "",
/*075 4B 	*/ "AMP SIMULATOR", "", "", "",
/*076 4C 	*/ "3-BAND EQ", "", "", "",
/*077 4D 	*/ "2-BAND EQ", "", "", "",
/*078 4E 	*/ "AUTO WAH (LFO)", "", "", "", "",
/*079 4F 	*/ "THRU", "", "", "",
/*080 50 	*/ "PITCH CHANGE", "", "", "",
/*081 51 	*/ "AURAL EXCITER", "", "", "",
/*082 52 	*/ "TOUCH WAH", 	"TOUCH WAH+DIST", "", "",
/*083 53 	*/ "COMPRESSOR", "", "", "",
/*084 54 	*/ "NOISE GATE", "", "", "",
/*085 55 	*/ "VOICE CANCEL", "", "", "", "",
/*086 56 - 127 	*/ "THRU", "", "", ""
};

void
txt_sysex(leng,mess)
char *mess;
{
	register int id, n, c;
	register unsigned char *p = mess;

	prtime();
	printf("Sysex, leng=%d",leng);

	if (leng > 1 && *p == 0xf0) {
		id = *(p+1);
		if (id == 0x7e && leng == 6 &&
			*(p+2) == 0x7f && *(p+3) == 0x09 && *(p+4) == 0x01)
			printf(" GM System On");
		else if (id == 0x7f && leng == 8 &&
			*(p+2) == 0x7f && *(p+3) == 0x04 && *(p+4) == 0x01)
			printf(" GM Master Volume %d", *(p+5)+(*(p+6)<<7));
		else if (id < 68 && man_id[id][0]) printf(" Id is %s", man_id[id]);
		else printf(" unknown id");
	}
/*
Reverb Parameter 	F0h 43h 1nh 4Ch 02h 01h rr* DD F7h
Chorus Parameter 	F0h 43h 1nh 4Ch 02h 01h cc** DD F7h
Variation Parameter 	F0h 43h 1nh 4Ch 02h 01h vv*** DD dd F7h
	Note: n = device number; DD = data (MSB); dd = data (LSB).
	rr = Reverb parameter number
	(02h - 0Bh = parameters 1 - 10; 10h - 15h = parameters 11 - 16).
	cc = Chorus parameter number
	(22h - 2Bh = parameters 1 - 10; 30h - 35h = parameters 11 - 16).
	vv = Variation parameter number
	(42h, 44h, 46h, 48h, 4Ah, 4Ch, 4Eh, 50h, 52h, 54h = parameters 1 - 10;
	70h - 75h = parameters 11 - 16).

AC1 Controller Number       F0h 43h 1nh 4Ch 08h 00h 59h dd F7h 10h*
AC1 Variation Control Depth F0h 43h 1nh 4Ch 02h 01h 5Fh dd F7h 40h**
	Note: n = device number; dd = data. * = cc #16. ** = 0 (decimal)

Variation Connection = System 		F0h 43h 1nh 4Ch 02h 01h 5Ah 01h F7h
Variation Connection = Insertion* 	F0h 43h 1nh 4Ch 02h 01h 5Ah 00h F7h
*/
	if (id == 0x43 && leng >= 9 && *(p+3) == 0x4c) {
	    int adhi = *(p+4), adlo = *(p+5), cd = *(p+6), dta = *(p+7), dtb = *(p+8);
	    printf(", port %d", *(p+2) & 0x0f);
	    if (!adhi && !adlo && cd == 0x7e && !dta) printf(", XG System On");
	    else if (adhi == 2 && adlo == 1) {
		if (cd == 0x5a) printf(", Variation Connection %s", (!dta)? "Insertion":"System");
		else {
		    int tmp, dlsb=dtb;
		    if (dlsb == 8) dlsb = 3;
		    if (dlsb < 0 || dlsb > 3) {
			/*fprintf(stderr,"bad lsb %d\n", dtb);*/
			dlsb = 0;
		    }
		    if (cd == 0x00) printf(", Reverb type %s", yam_reverb_type[(dta<20)? dta:0][dlsb]);
		    else if (cd == 0x20) {
			if (dta > 72 || dta < 65) tmp = 5;
			else tmp = dta - 65;
			printf(", Chorus type %s", yam_chorus_type[tmp][dlsb]);
		    }
		    else if (cd == 0x40) printf(", Variation type %s", yam_variation_type[(dta>86)? 86:dta][dlsb]);
		    else if (cd < 0x20) printf(", Reverb parm %d", cd-1);
		    else if (cd < 0x40) printf(", Chorus parm %d", cd-0x21);
		    else printf(", Variation parm %d", cd-0x41);
		    printf(": data %d", dta); /* + another if var. */
		}
	    }
	    else if (adhi == 8 && cd <= 40) {
		printf(" channel %d change %s to %d", adlo+1, yam_prm_name[cd], dta);
	    }
	    else if (adhi == 16 && cd <= 40) {
		printf(" ext channel %d change %s to %d", adlo+1, yam_prm_name[cd], dta);
	    }
	    else if (adhi <= 0x3f && adhi >= 0x30) {
		printf(" drum %d setup", adhi & 0x0f);
	    }
	    else if (adhi == 8 && adlo == 0 && cd == 0x59) {
		if (cd == 0x59) printf(", AC1 Controller Number %d", dta);
	    }
	    else if (adhi == 2 && adlo == 1 && cd == 0x5f) {
		if (cd == 0x59) printf(", AC1 Variation Control Depth %d", dta);
	    }
	}
/*
	F0h 43h 1nh 4Ch 08h 0mh 04h ddh F7h
where n = device number, m = MIDI
channel of the part you wish to change, and
dd = new MIDI channel you want the
designated part to receive on (a data value
*/
	printf("\n     Dump = <");
	for ( n=0; n<leng; n++ ) {
		c = 0xff & *p++;
		printf( "\\0x%02x" , c);
	}
	printf(">\n");
}

void
txt_metamisc(type,leng,mess)
char *mess;
{	char *p = mess;
	int n, c;

	prtime();
	printf("Meta event, unrecognized, type=0x%02x leng=%d\n",type,leng);
	printf("     Text = \"");
	for ( n=0; n<leng; n++ ) {
		c = 0xff & *p++;
		printf( (isprint(c)||isspace(c)) ? "%c" : "\\0x%02x" , c);
	}
	printf("\"\n");
}

void
txt_metaspecial(leng,mess)
char *mess;
{	char *p = mess;
	int n, c;

	prtime();
	printf("Meta event, sequencer-specific, leng=%d\n",leng);
	printf("\n     Dump = <");
	for ( n=0; n<leng; n++ ) {
		c = 0xff & *p++;
		printf( "\\0x%02x" , c);
	}
	printf(">\n");
}

void
txt_metatext(type,leng,mess)
char *mess;
{
	static char *ttype[] = {
		NULL,
		"Text Event",		/* type=0x01 */
		"Copyright Notice",	/* type=0x02 */
		"Sequence/Track Name",
		"Instrument Name",	/* ...       */
		"Lyric",
		"Marker",
		"Cue Point",		/* type=0x07 */
		"Unrecognized"
	};
	int unrecognized = (sizeof(ttype)/sizeof(char *)) - 1;
	register int n, c;
	register char *p = mess;

	if ( type < 1 || type > unrecognized )
		type = unrecognized;
	prtime();
	printf("Meta Text, type=0x%02x (%s)  leng=%d\n",type,ttype[type],leng);
	printf("     Text = \"");
	for ( n=0; n<leng; n++ ) {
		c = 0xff & *p++;
		printf( (isprint(c)||isspace(c)) ? "%c" : "\\0x%02x" , c);
	}
	printf("\"\n");
}

void
txt_metaseq(num)
{
	prtime();
	printf("Meta event, sequence number = %d\n",num);
}

void
txt_metaeot()
{
	prtime();
	printf("Meta event, end of track\n");
}

void
txt_keysig(sf,mi)
{
	prtime();
	printf("Key signature, sharp/flats=%d  minor=%d\n",sf,mi);
}

void
txt_tempo(ltempo)
long ltempo;
{
	tempo = ltempo;
	prtime();
	printf("Tempo, microseconds-per-MIDI-quarter-note=%d\n",tempo);
}

void
txt_timesig(nn,dd,cc,bb)
{
	int denom = 1;
	int savedd = dd;
	while ( dd-- > 0 )
		denom *= 2;
	prtime();
	printf("Time signature=%d/%d[%d]  MIDI-clocks/click=%d  32nd-notes/24-MIDI-clocks=%d\n",
		nn,denom,savedd,cc,bb);
}

void
txt_smpte(hr,mn,se,fr,ff)
{
	prtime();
	printf("SMPTE, hour=%d minute=%d second=%d frame=%d fract-frame=%d\n",
		hr,mn,se,fr,ff);
}

void
txt_arbitrary(leng,mess)
char *mess;
{	char *p = mess;
	int n, c;

	prtime();
	printf("Arbitrary bytes, leng=%d\n",leng);
	printf("     Text = \"");
	for ( n=0; n<leng; n++ ) {
		c = 0xff & *p++;
		printf( (isprint(c)||isspace(c)) ? "%c" : "\\0x%02x" , c);
	}
	printf("\"\n");
}

void
prtime()
{	long the_time;
	long the_realtime;

	if (delta_flag) {
		the_time = Mf_currtime - last_time;
		the_realtime = Mf_realtime - last_realtime;
	}
	else {
		the_time = Mf_currtime;
		the_realtime = Mf_realtime;
	}
	last_time = Mf_currtime;
	last_realtime = Mf_realtime;

    if(SECONDS)
	printf("%3d.%02d.%02d ", the_realtime / (100*16),
		(the_realtime / 16) % 100,
		the_realtime % 16);
    else
	printf("%6ld ",the_time);
}

initfuncs()
{
	Mf_error = error;
	Mf_header =  txt_header;
	Mf_trackstart =  txt_trackstart;
	Mf_trackend =  txt_trackend;
	Mf_noteon =  txt_noteon;
	Mf_noteoff =  txt_noteoff;
	Mf_pressure =  txt_pressure;
	Mf_parameter =  txt_parameter;
	Mf_pitchbend =  txt_pitchbend;
	Mf_program =  txt_program;
	Mf_chanpressure =  txt_chanpressure;
	Mf_sysex =  txt_sysex;
	Mf_metamisc =  txt_metamisc;
	Mf_seqnum =  txt_metaseq;
	Mf_eot =  txt_metaeot;
	Mf_timesig =  txt_timesig;
	Mf_smpte =  txt_smpte;
	Mf_tempo =  txt_tempo;
	Mf_keysig =  txt_keysig;
	Mf_seqspecific =  txt_metaspecial;
	Mf_text =  txt_metatext;
	Mf_arbitrary =  txt_arbitrary;
}
