/***************************/
/* readmidi.c              */
/***************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "midi.h"
#include "maintypes.h"
#include "defines.h"
#include "readmidi.h"

#ifdef DEBUG
static void ausgabe()
{
	printf("MThd = %s, chunk_length = %d\n", head.MThd , head.chunk_length);
	printf("format = %d, tracks = %d, per_quarter = %x\n",
		head.format, head.tracks, head.per_quarter);

	printf("Mtrk = %s, Track_chunk_length = %d\n", 
		THead.MTrk, THead.chunk_length);
}
#endif 

static unsigned long get_valid_time(ti)
	unsigned long ti;
{
	if (ti < bartime) {
		if (ti < lastbartime) {
			converter = 0;
			sequencecounter = 0;
		}
		else {
			converter = lastbartime;
			sequencecounter = (double) lastbartime;
		}
	}
	else {
		converter = bartime;
		sequencecounter = (double) bartime;
	}
	while (ti > converter) {
		lastsc = sequencecounter;
		sequencecounter += one64tel;
		converter = (unsigned long) sequencecounter;
	}
	if (ti < converter) ti = (unsigned long) lastsc;
	return(ti);
}

static char midi_getc(fpointer)
	FILE *fpointer;
{
	if (THead.chunk_length-- < 0) {
		fprintf(stderr, "midi_getc: Length Underflow\n");
		exit(10);
	}
	return(getc(fpointer));
}

static void midi_ungetc(c, fpointer)
	FILE *fpointer;
	char c;
{
	THead.chunk_length++;
	ungetc(c, fpointer);
}

static unsigned int ReadVarLen()
{
	unsigned int value;
	unsigned char c;

	if ((value = midi_getc(fp)) & 0x80) {
		value  &= 0x7f;
		do {
			value = (value << 7) + ((c = midi_getc(fp)) & 0x7f);
		}
		while (c & 0x80);
	}

	return(value);
}


static void ReadMidiTrackName(delta)
	int delta;
{
	int len, i;

	len = ReadVarLen();
	for (i = 0; i < len; i++) {
		if (i < 80 - 1) TrackName[Track_Nr][i] = midi_getc(fp);
		else midi_getc(fp);
	}
	if (i<80) TrackName[Track_Nr][i] = '\0';
	else TrackName[Track_Nr][79] = '\0';

#ifdef DEBUG
	printf("MIDI_TRACK_NAME (%s) at %d\n", TrackName[Track_Nr], delta);
#endif

}

static void ReadMidiSequSpec(delta)
	int delta;
{
	int len, i;
	char Str[80];
#ifdef DEBUG
	int j;
#endif

	len = ReadVarLen();
	for (i = 0; i < len; i++) {
		if (i < 80 - 1) Str[i] = midi_getc(fp);
	}
	Str[i] = '\0';
#ifdef DEBUG
	printf("MIDI_SEQUENCER_SPECIFIC at %d:\n", delta);
	for (j = 0; j < i; j++)
		printf("\t0x%x\n", Str[j]);
#endif
}

#ifdef DEBUG
static char *p2str(oct, pit)
	int oct, pit;
{
	static char Str[10];
	static char *p_table[12] = {"C", "Cis", "D", "Dis",
				   "E",  "F",  "Fis", "G",
				   "Gis", "A", "B",  "H"};

	sprintf(Str, "%s%d", p_table[pit], oct);
	return(Str);
}
#endif

static void ReadMidiToneVal(oct, pit, laut)
	int *oct, *pit, *laut;
{
	int w;
	unsigned char inthelp;

	w = midi_getc(fp);
	*laut = midi_getc(fp);

	/* allow only octaves 0 - 5 */
	if ((w>24) && (w<95)) {
		*oct = (w / 12) - 2;
		*pit = (w % 12);
	}
	else {
		*oct = 6;
		*pit = 0;
	}

	inthelp = 12 * (*oct) + (*pit);
	if (max[Track_Nr] < inthelp) max[Track_Nr] = inthelp;
	if (min[Track_Nr] > inthelp) min[Track_Nr] = inthelp;
}

static void erzeuge_note(octave, pitch, laut, begin, dauer, channel)
	int octave, pitch, laut, dauer, channel;
	unsigned long int begin;
{
	if (firstnotetimeflag == -1) {
		firstnotetime = begin;
		firstnotetimeflag = 0;
	}
	/* if there were no TimeSig */
	if ((valid == 0) && (begin > firstnotetime)) {
		valid++;
		if ((!printonly) || (Track_Nr == (printonly -1))) {
			if ((!notes_flag) && (nom0) && (Track_Nr))
				ins_dyn_event(Track_Nr, 't', 0, nom0, denom0, 0, 0, 0, 0, 0);
			else
				ins_dyn_event(Track_Nr, 't', 0, 4, 4, 0, 0, 0, 0, 0);
		}
	}

	/* if there were no KeySig */
	if ((valid2 == 0) && (begin > firstnotetime)) {
		valid2++;
		if ((!printonly) || (Track_Nr == (printonly -1))) {
			if ((!notes_flag) && (key0) && (Track_Nr))
				ins_dyn_event(Track_Nr, 'k', 0, key0, 0, 0, 0, 0, 0, 0);
			else
				ins_dyn_event(Track_Nr, 'k', 0, 0, 0, 0, 0, 0, 0, 0);
		}
	}

#ifdef DEBUG
	fprintf(stdout, "NOTE into dyn_event_list inserted\n");
	fprintf(stdout, "oct: %d\t pit: %d\t laut: %x\t beg: %lx\t durat: %x\n",
		octave, pitch, laut, begin, dauer);
#endif
	/* notes_flag shows that notes of first track were read	*/
	/*							*/
	/* because f.e. rosegarden writes empty first track 	*/
	/* with takt and key info for all other tracks		*/

	if (Track_Nr == 0) notes_flag = 1;

	if (((!printonly) || (Track_Nr == (printonly -1))) && (octave != 6))
		ins_dyn_event(Track_Nr, 'n', 0, 0, 0, octave, pitch, laut, get_valid_time(begin), dauer);
}
	
static void ReadMidiNoteOff(channel, delta)
	int channel, delta;
{
	int dauer, octave, pitch, laut, la;
	unsigned long int begin;

	ReadMidiToneVal(&octave, &pitch, &laut);

#ifdef DEBUG
	printf("MIDI_NOTE_OFF at %d(%ld): Channel: %d, pitch = %s, laut = 0x%x\n",
		delta, zeit, channel, p2str(octave, pitch), laut);
#endif
	dauer = note_delete(octave, pitch, &la, zeit, &begin, channel);
#ifdef DEBUG
	printf("Generate_Note with begin = %ld, absolute time = %ld, duration = %d\n",
			begin, zeit, dauer);
#endif
	erzeuge_note(octave, pitch, la, begin, dauer, channel);
}

static void ReadMidiNoteOn(channel, delta)
	int channel, delta;
{
	int dauer, octave, pitch, laut, la;
	unsigned long int begin;

	ReadMidiToneVal(&octave, &pitch, &laut);

	if (laut) {
		note_insert(octave, pitch, laut, zeit, channel);
	}
	else {
		dauer = note_delete(octave, pitch, &la, zeit, &begin, channel);
#ifdef DEBUG
		printf("Generate_Note with begin = %ld, absolute time = %ld, duration = %d\n",
				begin, zeit, dauer);
#endif
		erzeuge_note(octave, pitch, la, begin, dauer, channel);
	}
#ifdef DEBUG
	printf("MIDI_NOTE_ON at %d(%ld): Channel: %d, pitch = %s, laut = 0x%x\n",
		delta, zeit, channel, p2str(octave, pitch), laut);
#endif
}

static void ReadMidiPolyAftertouch(channel, delta)
	int channel, delta;
{
	char dum1, dum2;

	dum1 = midi_getc(fp); dum2 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_POLY_AFTERTOUCH: 0x%x, 0x%x, 0x%x\n", channel, dum1, dum2);
#endif
}

static void ReadMidiCtrlChange(channel, delta)
	int channel, delta;
{
	char dum1, dum2;

	dum1 = midi_getc(fp); dum2 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_CTRL_CHANGE: 0x%x, 0x%x, 0x%x\n", channel, dum1, dum2);
#endif
}

static void ReadMidiProgChange(channel, delta)
	int channel, delta;
{
	char dum1;

	dum1 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_PROG_CHANGE: 0x%x, 0x%x\n", channel, dum1);
#endif
}

static void ReadMidiChnlAftertouch(channel, delta)
	int channel, delta;
{
	char dum1;

	dum1 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_CHNL_AFTERTOUCH: 0x%x, 0x%x\n", channel, dum1);
#endif
}

static void ReadMidiPitchBend(channel, delta)
	int channel, delta;
{
	char dum1, dum2;

	dum1 = midi_getc(fp); dum2 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_PITCH_BEND: 0x%x, 0x%x, 0x%x\n", channel, dum1, dum2);
#endif
}

static void ReadMidiSongPositionPtr(delta)
	int delta;
{
	char dum1, dum2;

	dum1 = midi_getc(fp); dum2 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_SONG_POSITION_PTR: 0x%x, 0x%x\n", dum1, dum2);
#endif
}

static void ReadMidiSongSelect(delta)
	int delta;
{
	char dum1;

	dum1 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_SONG_SELECT: 0x%x\n", dum1);
#endif
}

static void ReadMidiSystemExclusive()
{
	int dum;
	unsigned int value;

	dum = ReadVarLen();
	while ((value = midi_getc(fp)) != MIDI_EOX);
#ifdef DEBUG
	printf("MIDI_SYSTEM_EXCLUSIVE\n");
#endif
}

static void ReadMidiSetTempo(delta)
	int delta;
{
	unsigned int len, tempo;

	len = midi_getc(fp);
	if (len == 0x03) {
		tempo = 0;
		while(len--) {
			tempo *= 0x100;
			tempo += (unsigned int) (0xff & midi_getc(fp));
		}
#ifdef DEBUG
		D_Tempo = (double) tempo / 1000000.0;
		printf("MIDI_SET_TEMPO at %d: Tempo = %d(%f)\n", delta, tempo, D_Tempo);
#endif
	}
	else { /* system reset detect */
		midi_ungetc(len, fp);
		midi_ungetc(0x51, fp);
	}
}

static void ReadMidiTimeSig(delta)
	int delta;
{
	int dummy, nn, dd, cc, bb, dominator;

	dummy = midi_getc(fp);
	if (dummy == 0x04) {
		valid = 1;	/* means: TimeSig detected */
		nn = midi_getc(fp);
		dd = midi_getc(fp);
		cc = midi_getc(fp);
		bb = midi_getc(fp);
		dominator = (1 << dd);
		help = taktzeit;
		taktzeit = head.per_quarter * nn * 4 / dominator;
		diskrete_tz = diskrete_tz + taktzeit - help;
		if ((!Track_Nr) && (!nom0)) {
			nom0 = nn;
			denom0 = dominator;
		}
		if ((!printonly) || (Track_Nr == (printonly -1)))
			ins_dyn_event(Track_Nr, 't', 0, nn, dominator, 0, 0, 0, get_valid_time(zeit), 0);
		
#ifdef DEBUG
		printf("Takt: %x\n",taktzeit);
		printf("MIDI_TIME_SIGNATURE at %d: Takt:%d/%d\nMIDI-clocks per Metronom: %d,",
			delta, nn, dominator, cc);
		printf("%d 32nd per MIDI-Quarter\n", bb);
#endif
	}
	else { /* system reset detect */
		midi_ungetc(dummy, fp);
		midi_ungetc(0x58, fp);
	}
}

static void ReadMidiTextEvent(delta)
	int delta;
{
	unsigned int len;
	int value;

	len = ReadVarLen();
	while (len--) value = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_TEXT_EVENT\n");
#endif
}

static void ReadMidiCopyrightNotice(delta)
	int delta;
{
	unsigned int len;
	int value;

	len = ReadVarLen();
	while (len--) value = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_COPYRIGHT_NOTICE\n");
#endif
}

static void ReadMidiInstrumentName(delta)
	int delta;
{
	unsigned int len;
	int value=0;

	len = ReadVarLen();
	while (len--) Staffs[Track_Nr][value++] = midi_getc(fp);
	Staffs[Track_Nr][value] = 0;
#ifdef DEBUG
	printf("MIDI_INSTRUMENT_NAME: %s\n",Staffs[Track_Nr]);
#endif
}

static void ReadMidiLyrik(delta)
	int delta;
{
	unsigned int len;
	char *ptr, *ptrhelp;

	len = ReadVarLen();
	if ((ptr = (char *) malloc (len+1)) == NULL) {
		fprintf(stderr, "mallocC error\n");
		exit(10);
	}
	ptrhelp = ptr;
	while (len--) {
		*ptrhelp = midi_getc(fp);
		ptrhelp++;
	}
	*ptrhelp = 0;
#ifdef DEBUG
	printf("MIDI_LYRIC\n");
#endif
	if ((!printonly) || (Track_Nr == (printonly -1)))
		ins_dyn_event(Track_Nr, 'l', ptr, 0, 0, 0, 0, 0, get_valid_time(zeit), 0);
}

static void ReadMidiTextMarker(delta)
	int delta;
{
	unsigned int len;
	int value;

	len = ReadVarLen();
	while (len--) value = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_TEXT_MARKER\n");
#endif
}

static void ReadMidiCuePoint(delta)
	int delta;
{
	unsigned int len;
	int value;

	len = ReadVarLen();
	while (len--) value = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_CUE_POINT\n");
#endif
}

static void ReadMidiChannelPrefix(delta)
	int delta;
{
	int dum1, dum2;
	dum1 = midi_getc(fp);
	if (dum1 == 0x01) {
		dum2 = midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_CHANNEL_PREFIX\n");
#endif
	}
	else { /* system reset detect */
		midi_ungetc(dum1, fp);
		midi_ungetc(0x20, fp);
	}
}

static void ReadMidiSmpteOffset(delta)
	int delta;
{
	int dum;

	if (delta != 0) { /* system reset detect */
		midi_ungetc(0x54, fp);
	}
	else {
		dum = midi_getc(fp);
		if (dum == 0x05) {
			midi_getc(fp); midi_getc(fp); midi_getc(fp);
			midi_getc(fp); midi_getc(fp);
#ifdef DEBUG
	printf("MIDI_SMPTE_OFFSET\n");
#endif
		}
		else { /* system reset detect */
			midi_ungetc(dum, fp);
			midi_ungetc(0x54, fp);
		}
	}
}

static void ReadMidiSequenceNumber(MetaEvent, delta)
	int delta;
	unsigned char MetaEvent;
{
	int value;

	value = midi_getc(fp);
	if (value != 0x02) { /* system reset detect */
		midi_ungetc(value, fp);
		midi_ungetc(0x00, fp);
	}
	else {
		value = midi_getc(fp); value = midi_getc(fp);
		value = midi_getc(fp); value = midi_getc(fp);
#ifdef DEBUG
		printf("MIDI_SEQUENCE_NUMBER\n");
#endif
	}
}

static void ReadMidiKeySig(delta)
	int delta;
{
	int dum, sign;

	dum = midi_getc(fp);
	if (dum != 0x02) { /* system reset detect */
		midi_ungetc(dum, fp);
		midi_ungetc(0x59, fp);
	}
	else {
		valid2 = 1; /* KeySig detected */
		sign = midi_getc(fp);
		/* manipulate sign */
		if ((sign & 0x80) && (sign & 0x08)) {
			sign = sign ^ 0xf;
			sign++;
		}
		if ((!Track_Nr) && (!key0)) key0 = sign;
		if ((!printonly) || (Track_Nr == (printonly -1)))
			ins_dyn_event(Track_Nr, 'k', 0, sign, 0, 0, 0, 0, get_valid_time(zeit), 0);
		T_genus = (0xff & sign);
		Signature = (0xff & midi_getc(fp));
#ifdef DEBUG
		printf("MIDI_KEY_SIGNATURE at %d: T_genus = ", delta);
		switch (T_genus)  {
			case MINOR: printf("MINOR"); break;
			case MAJOR: printf("MAJOR"); break;
			default: printf("unknown"); break;
		}
		printf(" Signature = %d\n", Signature);
#endif
	}
}

static void ReadMidiEndOfTrack(delta)
	int delta;
{
	int dum, dauer, octave, pitch, laut, channel;
	unsigned long int begin;

	dum = midi_getc(fp);
	if (dum == 0) {
		dum = midi_getc(fp);	/* read - deltatime */
		while (dum == 0) {	/* falls nach EOTrack noch Folgeelement */
			midi_ungetc(dum, fp);
			ReadEvent();
			dum = midi_getc(fp);
		}
		midi_ungetc(dum, fp);
		while ((dauer = search_note(&octave, &pitch, &laut, zeit, &begin, &channel)) != -1) {
#ifdef DEBUG
			printf("erzeuge_note mit begin = %ld, zeit = %ld, dauer = %d\n",
				begin, zeit, dauer);
#endif
			erzeuge_note(octave, pitch, laut, begin, dauer, channel);
		}

		if (THead.chunk_length) {
			fprintf(stderr, "Warning: ReadMidiEndOfTrack: Sync_Error THead.chunk_length = %d\n",
			THead.chunk_length);
			while (THead.chunk_length >= 0) midi_getc(fp);
		}
#ifdef DEBUG
		printf("MIDI_END_OF_TRACK at %d\n", delta);
#endif
	}
	else { /* system reset detect */
		midi_ungetc(dum, fp);
		midi_ungetc(0x2f, fp);
	}
}

static int ReadMidiMetaEvent(delta)
	int delta;
{
	unsigned char MetaEvent;
	int not_eot = 1;

	switch(MetaEvent = midi_getc(fp)) {
		case MIDI_SEQUENCE_NUMBER: ReadMidiSequenceNumber(MetaEvent,delta); break;
		case MIDI_TEXT_EVENT: ReadMidiTextEvent(delta); break;
		case MIDI_TRACK_NAME: ReadMidiTrackName(delta); break;
		case MIDI_SET_TEMPO: ReadMidiSetTempo(delta); break;
		case MIDI_TIME_SIGNATURE: ReadMidiTimeSig(delta); break;
		case MIDI_END_OF_TRACK: ReadMidiEndOfTrack(delta); not_eot = 0; break;
		case MIDI_KEY_SIGNATURE: ReadMidiKeySig(delta); break;
		case MIDI_SEQUENCER_SPECIFIC: ReadMidiSequSpec(delta); break;
		case MIDI_CHANNEL_PREFIX: ReadMidiChannelPrefix(delta); break;
		case MIDI_SMPTE_OFFSET: ReadMidiSmpteOffset(delta); break;
		case MIDI_COPYRIGHT_NOTICE: ReadMidiCopyrightNotice(delta); break;
		case MIDI_INSTRUMENT_NAME: ReadMidiInstrumentName(delta); break;
		case MIDI_LYRIC: ReadMidiLyrik(delta); break;
		case MIDI_TEXT_MARKER: ReadMidiTextMarker(delta); break;
		case MIDI_CUE_POINT: ReadMidiCuePoint(delta); break;
		default: /* system reset detect */
			midi_ungetc(MetaEvent, fp); break;
	}
	return(not_eot);
}

static int ReadEvent()
{
	unsigned char Event_Type;
	int delta, not_eot = 1;

	delta = ReadVarLen();
	zeit += delta;

	while (zeit >= diskrete_tz) {
		if ((!printonly) || (Track_Nr == (printonly -1))) {
			lastbartime = bartime;
			bartime = diskrete_tz;
			ins_dyn_event(Track_Nr, 'b', 0, 0, 0, 0, 0, 0, diskrete_tz, taktzeit);
		}
		diskrete_tz += taktzeit;
	}

	Event_Type = midi_getc(fp);
r_event:
	if ((0xf0 & Event_Type) == MIDI_NOTE_OFF) {
		ReadMidiNoteOff((Event_Type & 0xf), delta);
	}
	else if ((0xf0  & Event_Type) ==  MIDI_NOTE_ON) {
		ReadMidiNoteOn((Event_Type & 0xf), delta);
	}
	else if ((0xf0  & Event_Type) ==  MIDI_POLY_AFTERTOUCH) {
		ReadMidiPolyAftertouch((Event_Type & 0xf), delta);
	}
	else if ((0xf0  & Event_Type) ==  MIDI_CTRL_CHANGE) {
		ReadMidiCtrlChange((Event_Type  & 0xf), delta);
	}
	else if ((0xf0  & Event_Type) ==  MIDI_PROG_CHANGE) {
		ReadMidiProgChange((Event_Type  & 0xf), delta);
	}
	else if ((0xf0  & Event_Type) ==  MIDI_CHNL_AFTERTOUCH) {
		ReadMidiChnlAftertouch((Event_Type & 0xf), delta);
	}
	else if ((0xf0  & Event_Type) ==  MIDI_PITCH_BEND) {
		ReadMidiPitchBend((Event_Type & 0xf), delta);
	}
	else if ((0xf0  & Event_Type) ==  MIDI_SYSTEM_MSG) {
		if (Event_Type == MIDI_FILE_META_EVENT) {
			not_eot = ReadMidiMetaEvent(delta);
		}
		else {
			/* treatment of all other 0xfx-events */
			switch (0xf & Event_Type) {
				case MIDI_SONG_POSITION_PTR: /* 0xf2 */
					ReadMidiSongPositionPtr(delta);
					break;
				case MIDI_SONG_SELECT: /* 0xf3 */
					ReadMidiSongSelect(delta);
					break;
				case MIDI_SYSTEM_EXCLUSIVE: /* 0xf0 */
					ReadMidiSystemExclusive();
					break;
				case MIDI_TUNE_REQUEST: /* 0xf6 */
				case MIDI_TIMING_CLOCK: /* 0xf8 */
				case MIDI_START: /* 0xfa */
				case MIDI_CONTINUE: /* 0xfb */
				case MIDI_STOP: /* 0xfc */
				case MIDI_ACTIVE_SENSING: /* 0xfe */
					midi_getc(fp);
					break;
				default: 
					fprintf(stderr, "Event: 0x%x unknown\n", Event_Type);
					fprintf(stderr, "Fileoffset: %lu\n", ftell(fp));
					exit(10);
			}
		}
	}
	else if (Running_Event) {
#ifdef DEBUG
		printf("Running Event\n");
#endif
		midi_ungetc(Event_Type, fp);

		Event_Type = Running_Event;
		goto r_event;
	}
	else {
		fprintf(stderr, "Event: 0x%x unknown\n", Event_Type);
		fprintf(stderr, "Fileoffset: %lu\n", ftell(fp));
		exit(10);
	}
	Running_Event = Event_Type;
	return(not_eot);
}
		
	
static void read_track()
{
	zeit = 0;
	THead.MTrk[0] = getc(fp); THead.MTrk[1] = getc(fp);
	THead.MTrk[2] = getc(fp); THead.MTrk[3] = getc(fp);
	THead.MTrk[4] = '\0';
	if (strcmp(THead.MTrk, MIDI_TRACK_HEADER)) {
		fprintf(stderr, "read_track: Sync_error(%s)\n", THead.MTrk);
		exit(10);
	}
	THead.chunk_length  = 0x1000000 * getc(fp);
	THead.chunk_length += 0x10000 * getc(fp);
	THead.chunk_length += 0x100 * getc(fp);
	THead.chunk_length += getc(fp);
#ifdef DEBUG
	ausgabe();
#endif
	Running_Event = 0;
	while (ReadEvent()); 
}

static void usage(conststrg)
	char *conststrg;
{
	fprintf(stderr,"usage:\t%s [-<nr>] <midifile> [<texfile>]\n", conststrg);
	fprintf(stderr,"\t%s -c <midifile>\n", conststrg);
	exit(10);
}

void main(argc, argv)
	int argc;
	char *argv[];
{
	int lenhelp;
	char opthelp;

	if (argc < 2) usage(argv[0]);

	if ((argc == 3) && (!strcmp(argv[1],"-c"))) {
		if ((fp = fopen(argv[2], "r")) == NULL) {
			fprintf(stderr, "cannot open %s\n", argv[1]);
			exit(10);
		}
		count = 1;
	}
	else {
		count = 0;
		if (*argv[1] == '-') {
			if ((argc == 2) || (*argv[2] == '-') || (argc > 4)) usage(argv[0]);
			opthelp = 1;
			printonly = (char) atoi((argv[1])+1);
			if (!printonly) printonly = 20;
		}
		else {
			if (argc > 3) usage(argv[0]);
			printonly = 0;	/* print every track */
			opthelp = 0;
		}

		if ((fp = fopen(argv[1+opthelp], "r")) == NULL) {
			fprintf(stderr, "cannot open %s\n", argv[1+opthelp]);
			exit(10);
		}

		/* fill the array for the name of tex file */
		if (argc == (3+opthelp)) {
			if (!strcmp(argv[1+opthelp], argv[2+opthelp])) {
				fprintf(stderr, "cannot process one file to itself\n");
				exit(10);
			}
			strcpy(texfilename, argv[2+opthelp]);
		}
		else {	/* mainmark1: try to select a nice name for texfile */
			strcpy(texfilename, argv[1+opthelp]);
			lenhelp = strlen(texfilename) - 4;

			/* delete midi file extension */
			if ((!strcmp(&(texfilename[lenhelp]),".mid")) ||
			    (!strcmp(&(texfilename[lenhelp]),".MID")))
				texfilename[lenhelp] = 0;
			else {
				if ((!strcmp(&(texfilename[lenhelp+1]),".mf")) ||
				    (!strcmp(&(texfilename[lenhelp+1]),".MF")))
					texfilename[lenhelp+1] = 0;
			}
			if ((texfilename[0] > 64) && (texfilename[0] < 91))
				strcat(texfilename, ".TEX");
			else
				strcat(texfilename, ".tex");
		}

	}

	/* mainmark2 */
	head.MThd[0] = getc(fp); head.MThd[1] = getc(fp);
	head.MThd[2] = getc(fp); head.MThd[3] = getc(fp); head.MThd[4] = '\0';
	if (strcmp(head.MThd, MIDI_FILE_HEADER)) {
		fprintf(stderr, "specified file probably not a midi-file or corrupted\n");
		exit(10);
	}

	head.chunk_length = 0x1000000 * getc(fp);
	head.chunk_length += 0x10000 * getc(fp);
	head.chunk_length += 0x100 * getc(fp);
	head.chunk_length += getc(fp);

	if (head.chunk_length != 6) {
		fprintf(stderr, "wrong header chunk length\n");
		exit(10);
	}

	head.format = 0x100 * getc(fp); head.format += getc(fp);
	head.tracks = 0x100 * getc(fp); head.tracks += getc(fp);

	/* mainmark3 */
	if (count) {
		printf("%d tracks detected\n", head.tracks);
		exit(0);
	}

	if (printonly && (printonly > head.tracks)) {
		printf("can't build tex-file of specified track \n");
		exit(0);
	}

	if ((!printonly) && ((head.tracks) > 9))
		fprintf(stderr, "Warning: printing out so many tracks together may look very ugly\n");

	head.per_quarter = 0x100 * (0xff & getc(fp));
	head.per_quarter += (0xff & getc(fp));
	if (head.format != 1) {
		fprintf(stderr, "Format %d not supported\n", head.format);
		exit(10);
	}

	if (head.per_quarter & 0x8000) {
		fprintf(stderr, "SMPTE-Time Code not supported\n");
		exit(10);
	}

	/* mainmark4 */
	one64tel = (double) head.per_quarter / 16;

	/* mainmark5 */
	nom0 = 0;
	key0 = 0;
	notes_flag = 0;
	for (lenhelp = 0; lenhelp < MAXTRACKS; lenhelp++) {
		Staffs[lenhelp][0]=0;
		max[lenhelp]=0;
		min[lenhelp]=255;
	}
	taktzeit = 4 * head.per_quarter;
	dyn_init();

	/* mainmark6 */
	if (!printonly)
		for (Track_Nr = 0; Track_Nr < head.tracks; Track_Nr++) {
			valid = 0;      /* no TimeSig */
			valid2 = 0;	/* no KeySig  */

			/* following variables allow event begin only at fixed time-points */
			lastbartime = 0;
			bartime = 0;

			firstnotetimeflag = -1;
			diskrete_tz = taktzeit;
			read_track();
			if (min[Track_Nr] > 0x1f) region[Track_Nr] = 1;
			else	if (max[Track_Nr] < 0x2a) region[Track_Nr] = 2;
				else region[Track_Nr] = 0;
#ifdef DEBUG
			printf("region: %x min: %x max: %x\n", region[Track_Nr], min[Track_Nr], max[Track_Nr]);
			printf("call now split_notes()\n");
#endif
			split_notes(Track_Nr, head.per_quarter);
#ifdef DEBUG
			printf("call now detect_accords()\n");
#endif
			detect_accords(region[Track_Nr], Track_Nr);
#ifdef DEBUG
			printf("call now insert_notetimes_and_slurs()\n");
#endif
			insert_notetimes_and_slurs(region[Track_Nr], Track_Nr, head.per_quarter);
#ifdef DEBUG
			printf("call now detect_note_positions_with_signs()\n");
#endif
			detect_note_positions_with_signs(Track_Nr);
#ifdef DEBUG
			printf("call now set_slur_regions()\n");
#endif
			set_slur_regions(Track_Nr);
#ifdef DEBUG
			printf("call now reset_treat_info()\n");
#endif
			reset_treat_info(Track_Nr);
#ifdef DEBUG
			printf("call now beam_detect()\n");
#endif
			beam_detect(region[Track_Nr], Track_Nr);
#ifdef DEBUG
			printf("call now set_beam_regions()\n");
#endif
			set_beam_regions(Track_Nr);
#ifdef DEBUG
			printf("call now reset_treat_info()\n");
#endif
			reset_treat_info(Track_Nr);
#ifdef DEBUG
			printf("call now slur_geometry()\n");
#endif
			slur_geometry(Track_Nr);
#ifdef DEBUG
			printf("call now detect_rests()\n");
#endif
			detect_rests(Track_Nr, region[Track_Nr], head.per_quarter);
#ifdef DEBUG
			printf("call now reset_treat_info()\n");
#endif
			reset_treat_info(Track_Nr);
#ifdef DEBUG
			printf("call now beam_geometry()\n");
#endif
			beam_geometry(Track_Nr);
#ifdef DEBUG
			printf("call now reset_treat_info()\n");
#endif
			reset_treat_info(Track_Nr);
#ifdef DEBUG
			printf("call now calculate_rest_positions()\n");
#endif
			calculate_rest_positions(Track_Nr);
#ifdef DEBUG
			printf("call now set_loudness_infos()\n");
#endif
			set_loudness_infos(Track_Nr);
		}
	else {
		for (Track_Nr = 0; Track_Nr < printonly; Track_Nr++) {
			valid = 0;      /* no TimeSig */
			valid2 = 0;     /* no KeySig  */

			/* following variables allow event begin only at fixed time-points */
			lastbartime = 0;
			bartime = 0;

			firstnotetimeflag = -1;
			diskrete_tz = taktzeit;
			read_track();
		}
		if ((printonly == 1) && (notes_flag == 0)) {
			fprintf(stderr, "sorry, first track is empty\n");
		}
		else {
			Track_Nr--;
			if (min[Track_Nr] > 0x1f) region[Track_Nr] = 1;
			else    if (max[Track_Nr] < 0x2a) region[Track_Nr] = 2;
				else region[Track_Nr] = 0;
#ifdef DEBUG
			printf("region: %x min: %x max: %x\n", region[Track_Nr], min[Track_Nr], max[Track_Nr]);
#endif
			split_notes(Track_Nr, head.per_quarter);
			detect_accords(region[Track_Nr], Track_Nr);
			insert_notetimes_and_slurs(region[Track_Nr], Track_Nr, head.per_quarter);
			detect_note_positions_with_signs(Track_Nr);
			set_slur_regions(Track_Nr);
			reset_treat_info(Track_Nr);
			beam_detect(region[Track_Nr], Track_Nr);
			set_beam_regions(Track_Nr);
			reset_treat_info(Track_Nr);
			slur_geometry(Track_Nr);
			detect_rests(Track_Nr, region[Track_Nr], head.per_quarter);
			reset_treat_info(Track_Nr);
			beam_geometry(Track_Nr);
			reset_treat_info(Track_Nr);
			calculate_rest_positions(Track_Nr);
			set_loudness_infos(Track_Nr);
		}
	}

#ifdef DEBUG
	debug_out();
#endif
	/* mainmark7 */
	if ((tfp = fopen(texfilename, "w")) ==  NULL) {
        	fprintf(stderr, "cannot open %s\n", texfilename);
        	exit(10);
        }

#ifdef DEBUG
	printf("writing texfile\n");
#endif
	write_texfile();
	fclose(fp); /* close MIDI-file */
	fclose(tfp); /* close TEX-file */
}