/*
 * Program to read in a 2-d table and emit scores per the rules of Mozart's
 * dice game in MIDI format.
 *
 * Copyright (C) 2006 Darrick Wong
 *
 * Last update: 2006-11-26
 */
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <arpa/inet.h>

/* Random number */
static int seed_random(void) {
	int fp;
	long seed;

	fp = open("/dev/urandom", O_RDONLY);
	if (fp < 0) {
		perror("/dev/urandom");
		return 0;
	}

	if (read(fp, &seed, sizeof(seed)) != sizeof(seed)) {
		perror("read random seed");
		return 0;
	}

	close(fp);
	srand(seed);

	return 1;
}

static unsigned long randnum(unsigned long min, unsigned long max)
{
	return min + (unsigned long)(((float)max) *
		(rand() / (RAND_MAX + 1.0)));
}

#define max(a, b) ((a) > (b) ? (a) : (b))

/* This code interprets the table data files */
struct dice_table {
	unsigned long *table;
	unsigned long cpm;
	unsigned long measures;
};

/*
 * File format:
 * choices_per_measure (x)
 * num_measures (y)
 * x by y table (these had all better be numbers...)
 */
#define LINE_SIZE 1024
static int read_table(FILE *fp, struct dice_table *dt)
{
	unsigned long i, j;
	char buf[LINE_SIZE+1];
	char *ptr;
	
	/* read choices per measure */
	if (!fgets(buf, LINE_SIZE, fp))
		return 0;
	dt->cpm = strtoul(buf, NULL, 0);
	if (errno)
		return 0;

	/* read number of measures */
	if (!fgets(buf, LINE_SIZE, fp))
		return 0;
	dt->measures = strtoul(buf, NULL, 0);
	if (errno)
		return 0;

	if (!(dt->cpm * dt->measures)) {
		errno = EINVAL;
		return 0;
	}

	/* read big fat array */
	dt->table = calloc(dt->cpm * dt->measures, sizeof(unsigned long));
	if (!dt->table)
		return 0;

	for (i = 0; i < dt->measures; i++) {
		if (!fgets(buf, LINE_SIZE, fp))
			goto out;
		ptr = buf;
		for (j = 0; j < dt->cpm; j++) {
			dt->table[i * dt->cpm + j] = strtoul(ptr, &ptr, 0);
			if (errno)
				goto out;
			ptr++;
		}
	}

	return 1;
out:
	free(dt->table);
	dt->table = NULL;
	return 0;
}

static void free_dice_table(struct dice_table *dt, unsigned int count)
{
	unsigned int i;

	for (i = 0; i < count; i++)
		if (dt[i].table)
			free(dt[i].table);
	free(dt);
}

/* This code interprets MIDI files. */
typedef unsigned long var_int;

/* Read a MIDI time from a stream and decode it */
static unsigned int read_var_int(FILE *fp, var_int *val)
{
	unsigned char inbyte;
	unsigned int read = 0;
	var_int value = 0;

	errno = 0;
	while (1) {
		if (!fread(&inbyte, sizeof(inbyte), 1, fp))
			return read;
		read++;
		value <<= 7;
		value |= inbyte & 0x7F;
		if (!(inbyte & 0x80))
			break;
	}

	*val = value;
	return read;
}

/* Encode a MIDI time and write it */
static unsigned int write_var_int(FILE *fp, var_int time)
{
	var_int buffer = time & 0x7F;
	unsigned int written = 0;
	unsigned char outbyte;

	errno = 0;
	while (time >>= 7) {
		buffer <<= 8;
		buffer |= (time & 0x7F) | 0x80;
	}

	while (1) {
		outbyte = (unsigned char)(buffer & 0xFF);
		if (!fwrite(&outbyte, sizeof(outbyte), 1, fp))
			return written;
		written++;
		if (!(buffer & 0x80))
			break;
		buffer >>= 8;
	}

	return written;
}

#define EVENT_TYPE_MASK		0xF0
#define CHANNEL_MASK		0x0F

#define NOTE_OFF_EVENT		0x80
#define NOTE_ON_EVENT		0x90
#define NOTE_AFTER_EVENT	0xA0
#define CONTROLLER_EVENT	0xB0
#define PGRM_CHANGE_EVENT	0xC0
#define CHAN_AFTER_EVENT	0xD0
#define PITCH_BLEND_EVENT	0xE0
struct midi_channel_event {
	uint8_t		channel;
	uint8_t		p1;
	uint8_t		p2;
};

#define META_EVENT		0xFF
#define TEXT_META_MASK		0x07
#define META_TEXT		0x01
#define META_COPYRIGHT		0x02
#define META_EOF		0x2F
struct meta_event {
	uint8_t		subcode;
	var_int	length;
	uint8_t		*data;
};

#define SYSEX_EVENT		0xF0
#define SYSEX_AUTH_EVENT	0xF7
struct sysex_event {
	var_int	length;
	uint8_t		*data;
};

struct event {
	var_int	time;
	uint8_t type;

	union {
		struct midi_channel_event	midi;
		struct meta_event		meta;
		struct sysex_event		sysx;
	};
	struct event	*next;
};

#define MTRK_MAGIC	"MTrk"
#define MTRK_SIZE	8
struct mtrk {
	unsigned char	magic[4];
	uint32_t	length;

	struct event	*event;
};

#define MTHD_MAGIC	"MThd"
#define MTHD_SIZE	14
#define MTHD_DATA_SIZE	6
struct mthd {
	unsigned char	magic[4];
	uint32_t	length;
	uint16_t	type;
	uint16_t	tracks;
	uint16_t	time_division;

	/* XXX: We don't support multiple tracks! */
	struct mtrk	*track;
};

static void dump_event(struct event *evt, unsigned long num)
{
	unsigned long y;
	printf("Event %3lu time %5lu type %x", num, evt->time, evt->type);
	switch (evt->type) {
	case META_EVENT:
		printf(" subcode %u", evt->meta.subcode);
		if (evt->meta.subcode <= TEXT_META_MASK) {
			char buf[256];
			memset(buf, 0, 256);
			memcpy(buf, evt->meta.data, max(255, evt->meta.length));
			printf(" \"%s\"\n", buf);
		} else {
			printf(" {");
			for (y = 0; y < evt->meta.length; y++)
				printf("%s%.2x", (y ? " " : ""), evt->meta.data[y]);
			printf("}\n");
		}
		break;
	case SYSEX_EVENT:
	case SYSEX_AUTH_EVENT:
		printf(" {");
		for (y = 0; y < evt->sysx.length; y++)
			printf("%s%.2x", (y ? " " : ""), evt->sysx.data[y]);
		printf("}\n");
		break;
	case PGRM_CHANGE_EVENT:
	case CHAN_AFTER_EVENT:
		printf(" chan %u p1 %u\n", evt->midi.channel, evt->midi.p1);
		break;
	case NOTE_OFF_EVENT:
	case NOTE_ON_EVENT:
	case NOTE_AFTER_EVENT:
	case CONTROLLER_EVENT:
	case PITCH_BLEND_EVENT:
		printf(" chan %u p1 %u p2 %u\n", evt->midi.channel, evt->midi.p1, evt->midi.p2);
		break;
	default:
		abort();
		break;
	}
}

static void dump_events(struct mtrk *track)
{
	unsigned long x = 1;
	struct event *evt = track->event;

	while (evt) {
		dump_event(evt, x++);
		evt = evt->next;
	}
}

static void dump_mthd(struct mthd *m)
{
	printf("MIDI file type %u tracks %u time %u\n", m->type, m->tracks, m->time_division);
	dump_events(m->track);
}

/* Convert absolute to relative timestamps */
static void unresolve_timestamps(struct mtrk *track)
{
	var_int running;
	struct event *evt = track->event;

	if (!track->event)
		return;
	running = track->event->time;

	while (evt) {
		evt->time -= running;
		running += evt->time;
		evt = evt->next;
	}
}

/* Convert relative to absolute timestamps */
static void resolve_timestamps(struct mtrk *track)
{
	var_int running = 0;
	struct event *evt = track->event;

	while (evt) {
		evt->time += running;
		running = evt->time;
		evt = evt->next;
	}
}

static void free_mthd(struct mthd *mthd)
{
	struct event *evt, *x;

	if (mthd->track) {
		evt = mthd->track->event;
		while (evt) {
			switch (evt->type) {
			case META_EVENT:
				if (evt->meta.data)
					free(evt->meta.data);
				break;
			case SYSEX_EVENT:
			case SYSEX_AUTH_EVENT:
				if (evt->sysx.data)
					free(evt->sysx.data);
				break;
			}
			x = evt->next;
			free (evt);
			evt = x;
		}
		free (mthd->track);
	}

	free (mthd);
}

/* Read all MIDI events */
static int read_midi_events(FILE *fp, struct mtrk *track)
{
	struct event *evt;
	unsigned long i = 0;
	unsigned long j;
	struct event **prevnext = &track->event;

	while (i < track->length) {
		var_int x = 0;
		i += read_var_int(fp, &x);
		if (errno)
			goto out;
		evt = calloc(1, sizeof(*evt));
		if (!evt)
			goto out;
		*prevnext = evt;
		prevnext = &evt->next;
		evt->time = x;
		j = fread(&evt->type, sizeof(evt->type), 1, fp);
		if (!j)
			goto out;
		i += j;
		switch (evt->type & EVENT_TYPE_MASK) {
		case PGRM_CHANGE_EVENT:
		case CHAN_AFTER_EVENT:
		case NOTE_OFF_EVENT:
		case NOTE_ON_EVENT:
		case NOTE_AFTER_EVENT:
		case CONTROLLER_EVENT:
		case PITCH_BLEND_EVENT:
			evt->midi.channel = evt->type & CHANNEL_MASK;
			evt->type &= EVENT_TYPE_MASK;
		}

		switch (evt->type) {
		case META_EVENT:
			j = fread(&evt->meta.subcode, sizeof(evt->meta.subcode), 1, fp);
			if (!j)
				goto out;
			i += j;
			i += read_var_int(fp, &x);
			if (errno)
				goto out;
			evt->meta.length = x;
			evt->meta.data = calloc(evt->meta.length, sizeof(unsigned char));
			if (!evt->meta.data)
				goto out;
			j = fread(evt->meta.data, 1, evt->meta.length, fp);
			if (j < evt->meta.length)
				goto out;
			i += j;
			break;
		case SYSEX_EVENT:
		case SYSEX_AUTH_EVENT:
			i += read_var_int(fp, &x);
			if (errno)
				goto out;
			evt->sysx.length = x;
			evt->sysx.data = calloc(evt->sysx.length, sizeof(unsigned char));
			if (!evt->sysx.data)
				goto out;
			j = fread(evt->sysx.data, 1, evt->sysx.length, fp);
			if (j < evt->sysx.length)
				goto out;
			i += j;
			break;
		case PGRM_CHANGE_EVENT:
		case CHAN_AFTER_EVENT:
			/* one-param midi */
			j = fread(&evt->midi.p1, sizeof(evt->midi.p1), 1, fp);
			if (!j)
				goto out;
			i += j;
			break;
		case NOTE_OFF_EVENT:
		case NOTE_ON_EVENT:
		case NOTE_AFTER_EVENT:
		case CONTROLLER_EVENT:
		case PITCH_BLEND_EVENT:
			/* two-param midi commands */
			j = fread(&evt->midi.p1, sizeof(evt->midi.p1), 1, fp);
			if (!j)
				goto out;
			i += j;
			j = fread(&evt->midi.p2, sizeof(evt->midi.p2), 1, fp);
			if (!j)
				goto out;
			i += j;
			break;
		default:
			abort();
			break;
		}
	}

	return 1;
out:
	perror(__FUNCTION__);
	return 0;
}

static struct mthd *read_midi(const char *fname)
{
	struct mthd *hdr;
	FILE *fp;

	fp = fopen(fname, "r");
	if (!fp)
		return NULL;

	/* read header */
	hdr = calloc(1, sizeof(*hdr));
	if (!hdr)
		goto out;

	if (!fread(hdr, MTHD_SIZE, 1, fp))
		goto out2;
	hdr->length = ntohl(hdr->length);
	hdr->type = ntohs(hdr->type);
	hdr->tracks = ntohs(hdr->tracks);
	hdr->time_division = ntohs(hdr->time_division);
	if (memcmp(hdr->magic, MTHD_MAGIC, 4)) {
		hdr->length = 0;
		printf("%s: Invalid header magic %s\n", fname, hdr->magic);
		goto out2;
	}
	if (hdr->length != MTHD_DATA_SIZE ||
	    hdr->type != 0 ||
	    hdr->tracks != 1) {
		printf("%s: Don't know what to do with length %lu type %u "
		       "tracks %u\n", fname, (unsigned long)hdr->length, hdr->type,
		       hdr->tracks);
		goto out2;
	}

	/* read track */
	hdr->track = calloc(1, sizeof(*hdr->track));
	if (!hdr->track)
		goto out2;

	if (!fread(hdr->track, MTRK_SIZE, 1, fp))
		goto out3;
	hdr->track->length = ntohl(hdr->track->length);

	/* read events */
	if (!read_midi_events(fp, hdr->track))
		goto out4;

	resolve_timestamps(hdr->track);

	fclose(fp);
	return hdr;
out4:
out3:
out2:
out:
	free_mthd(hdr);
	fclose(fp);
	return NULL;
}

static struct mthd *create_new_midi(void)
{
	struct mthd *x = calloc(1, sizeof (*x));
	if (!x)
		return NULL;

	x->length = MTHD_DATA_SIZE;
	x->type = 0;
	x->tracks = 1;
	x->time_division = 480;
	memcpy(x->magic, MTHD_MAGIC, 4);

	x->track = calloc(1, sizeof(*x->track));
	if (!x->track) {
		free(x);
		return NULL;
	}
	memcpy(x->track->magic, MTRK_MAGIC, 4);

	return x;
}

static unsigned int write_midi_events(FILE *fp, struct mtrk *track)
{
	struct event *evt = track->event;
	unsigned long j;
	unsigned int written = 0;
	uint8_t fake_type;

	errno = 0;
	while (evt) {
		written += write_var_int(fp, evt->time);
		if (errno)
			goto out;
		fake_type = evt->type;
		switch (evt->type & EVENT_TYPE_MASK) {
		case PGRM_CHANGE_EVENT:
		case CHAN_AFTER_EVENT:
		case NOTE_OFF_EVENT:
		case NOTE_ON_EVENT:
		case NOTE_AFTER_EVENT:
		case CONTROLLER_EVENT:
		case PITCH_BLEND_EVENT:
			fake_type &= EVENT_TYPE_MASK;
			fake_type |= evt->midi.channel & CHANNEL_MASK;
		}
		j = fwrite(&fake_type, sizeof(fake_type), 1, fp);
		if (!j)
			goto out;
		written += j;
		switch (evt->type) {
		case META_EVENT:
			j = fwrite(&evt->meta.subcode, sizeof(evt->meta.subcode), 1, fp);
			if (!j)
				goto out;
			written += j;
			written += write_var_int(fp, evt->meta.length);
			j = fwrite(evt->meta.data, 1, evt->meta.length, fp);
			if (j < evt->meta.length)
				goto out;
			written += j;
			break;
		case SYSEX_EVENT:
		case SYSEX_AUTH_EVENT:
			written += write_var_int(fp, evt->sysx.length);
			j = fwrite(evt->sysx.data, 1, evt->sysx.length, fp);
			if (j < evt->sysx.length)
				goto out;
			written += j;
			break;
		case PGRM_CHANGE_EVENT:
		case CHAN_AFTER_EVENT:
			j = fwrite(&evt->midi.p1, sizeof(evt->midi.p1), 1, fp);
			if (!j)
				goto out;
			written += j;
			break;
		case NOTE_OFF_EVENT:
		case NOTE_ON_EVENT:
		case NOTE_AFTER_EVENT:
		case CONTROLLER_EVENT:
		case PITCH_BLEND_EVENT:
			j = fwrite(&evt->midi.p1, sizeof(evt->midi.p1), 1, fp);
			if (!j)
				goto out;
			written += j;
			j = fwrite(&evt->midi.p2, sizeof(evt->midi.p2), 1, fp);
			if (!j)
				goto out;
			written += j;
			break;
		default:
			abort();
			break;
		}
		evt = evt->next;
	}

	return written;
out:
	perror(__FUNCTION__);
	return written;
}

static int write_midi(struct mthd *hdr, const char *fname)
{
	int res;
	unsigned long x;
	FILE *fp;

	res = 0;
	fp = fopen(fname, "w");
	if (!fp) {
		perror(fname);
		return 0;
	}

	hdr->length = htonl(hdr->length);
	hdr->type = htons(hdr->type);
	hdr->tracks = htons(hdr->tracks);
	hdr->time_division = htons(hdr->time_division);
	if (!fwrite(hdr, MTHD_SIZE, 1, fp))
		goto out;

	hdr->track->length = htonl(hdr->track->length);
	if (!fwrite(hdr->track, MTRK_SIZE, 1, fp))
		goto out2;

	unresolve_timestamps(hdr->track);

	x = write_midi_events(fp, hdr->track);
	if (errno)
		goto out3;

	hdr->track->length = htonl(x);
	if (fseek(fp, -(x + MTRK_SIZE), SEEK_CUR))
		goto out3;
	if (!fwrite(hdr->track, MTRK_SIZE, 1, fp))
		goto out3;

	res = 1;

out3:
	resolve_timestamps(hdr->track);
out2:
	hdr->track->length = ntohl(hdr->track->length);
out:
	if (!res)
		perror(__FUNCTION__);
	hdr->length = ntohl(hdr->length);
	hdr->type = ntohs(hdr->type);
	hdr->tracks = ntohs(hdr->tracks);
	hdr->time_division = ntohs(hdr->time_division);
	fclose(fp);
	return res;
}

static void remove_meta_events(struct mtrk *track)
{
	struct event *next;
	struct event **prevptr = &track->event;
	struct event *evt = track->event;

	while (evt) {
		next = evt->next;
		if (evt->type == META_EVENT) {
			*prevptr = next;
			if (evt->meta.data)
				free(evt->meta.data);
			free(evt);
		} else
			prevptr = &evt->next;
		evt = next;
	}
}

static void remove_time_indices(struct mtrk *track, var_int start, var_int end)
{
	struct event *evt, *next, *prev;

	var_int delta = end - start;
	if (end <= start)
		return;

	evt = track->event;
	prev = NULL;
	while (evt) {
		next = evt->next;
		if (evt->time >= start && evt->time > end) {
			prev = evt;
			evt->time -= delta;
		} else if (evt->time >= start && evt->time <= end) {
			if (evt->type == META_EVENT && evt->meta.data)
				free(evt->meta.data);
			else if ((evt->type == SYSEX_EVENT ||
				 evt->type == SYSEX_AUTH_EVENT) &&
				 evt->sysx.data)
				free(evt->sysx.data);
			free(evt);
			if (prev)
				prev->next = next;
			else
				track->event = next;
		} else
			prev = evt;
		evt = next;
	}
}

static void remove_text_meta_events(struct mtrk *track)
{
	struct event *next;
	struct event **prevptr = &track->event;
	struct event *evt = track->event;

	while (evt) {
		next = evt->next;
		if (evt->type == META_EVENT && evt->meta.subcode <= TEXT_META_MASK) {
			*prevptr = next;
			if (evt->meta.data)
				free(evt->meta.data);
			free(evt);
		} else
			prevptr = &evt->next;
		evt = next;
	}
}

#define DJWONG_INFO	"A randomly generated minuet and trio via Mozart's dice game"
#define DJWONG_COPY	"Programming by Darrick J. Wong, 2006; playing by John Chuang, 1995, 1996."
static void add_djwong_bits(struct mtrk *track)
{
	struct event *next = track->event;

	track->event = calloc(1, sizeof(*track->event));
	if (!track->event)
		goto put_te_back;
	track->event->type = META_EVENT;
	track->event->meta.subcode = META_COPYRIGHT;
	track->event->meta.length = strlen(DJWONG_COPY);
	track->event->meta.data = malloc(track->event->meta.length);
	if (!track->event->meta.data)
		goto kill_te;
	memcpy(track->event->meta.data, DJWONG_COPY, track->event->meta.length);
	track->event->next = next;

	next = track->event;
	track->event = calloc(1, sizeof(*track->event));
	if (!track->event)
		goto put_te_back;
	track->event->type = META_EVENT;
	track->event->meta.subcode = META_TEXT;
	track->event->meta.length = strlen(DJWONG_INFO);
	track->event->meta.data = malloc(track->event->meta.length);
	if (!track->event->meta.data)
		goto kill_te;
	memcpy(track->event->meta.data, DJWONG_INFO, track->event->meta.length);
	track->event->next = next;

	return;
kill_te:
	free(track->event);
put_te_back:
	track->event = next;
}

static int set_instrument(struct mtrk *track, uint8_t channel,
			  uint8_t instrument)
{
	struct event *evt = track->event;
	int need_one = 1;

	channel &= CHANNEL_MASK;
	while (evt) {
		if (evt->type == PGRM_CHANGE_EVENT &&
		    evt->midi.channel == channel) {
			evt->midi.p1 = instrument;
			need_one = 0;
		}
		evt = evt->next;
	}

	if (need_one) {
		evt = calloc(1, sizeof (*evt));
		if (!evt)
			return 0;
		evt->type = PGRM_CHANGE_EVENT;
		evt->midi.channel = channel;
		evt->midi.p1 = instrument;
		evt->next = track->event;
		track->event = evt;
	}

	return 1;
}

/* Remove all EOF markers and place one at the end */
static int fix_ending(struct mtrk *dst)
{
	struct event *evt, *prev, *next;

	/* Now strip out extraneous end-of-events */
	evt = dst->event;
	prev = NULL;
	while (evt) {
		next = evt->next;
		if (evt->type == META_EVENT && evt->meta.subcode == META_EOF) {
			if (prev)
				prev->next = evt->next;
			else
				dst->event = evt->next;
			free(evt);
		} else
			prev = evt;
		evt = next;
	}

	/* And put one on the end */
	evt = calloc(1, sizeof (*evt));
	if (!evt) {
		perror(__FUNCTION__);
		return 0;
	}
	evt->type = META_EVENT;
	evt->meta.subcode = META_EOF;

	if (prev) {
		evt->time = prev->time;
		prev->next = evt;
	} else
		dst->event = evt;

	return 1;
}

static int merge_events(struct mtrk *dst, struct mtrk *src)
{
	int i;
	struct event *devt, *sevt, *dnext, *sprev;

	/* Check easy cases */
	if (!dst->event) {
		dst->event = src->event;
		src->event = NULL;
		return fix_ending(dst);
	}

	if (!src->event)
		return fix_ending(dst);

	devt = dst->event;
	sevt = src->event;
	sprev = NULL;
	i = 0;
	/* Walk dst and insert src's items if they're between us and the next event */
	while (sevt && sevt->time < devt->time) {
		if (!i)
			dst->event = sevt;
		sprev = sevt;
		sevt = sevt->next;
		i++;
	}
	if (i)
		sprev->next = devt;
	while (devt) {
		i = 0;
		dnext = devt->next;
		while (sevt && sevt->time >= devt->time && (!dnext || sevt->time < dnext->time)) {
			if (!i)
				devt->next = sevt;
			sprev = sevt;
			sevt = sevt->next;
			i++;
		}
		if (i)
			sprev->next = dnext;
		devt = dnext;
	}

	src->event = NULL;
	return fix_ending(dst);
}

static int concatenate_events(struct mtrk *dst, struct mtrk *src)
{
	struct event *evt;
	var_int now = 0;
	struct event *prev = NULL;

	/* Check easy cases */
	if (!src->event)
		return fix_ending(dst);

	if (!dst->event) {
		dst->event = src->event;
		src->event = NULL;
		return fix_ending(dst);
	}

	evt = dst->event;
	/* Find the highest absolute time in dst */
	while (evt) {
		now = evt->time;
		prev = evt;
		evt = evt->next;
	}

	/* Adjust the incoming events to be on this new scale */
	evt = src->event;
	while (evt) {
		evt->time += now;
		evt = evt->next;
	}

	/* Update links */
	if (prev)
		prev->next = src->event;
	else
		dst->event = src->event;
	src->event = NULL;

	return fix_ending(dst);
}

static void change_channel(struct mtrk *track, unsigned int old, unsigned int new)
{
	struct event *evt = track->event;

	old &= CHANNEL_MASK;
	new &= CHANNEL_MASK;
	while (evt) {
		switch (evt->type) {
		case META_EVENT:
		case SYSEX_EVENT:
		case SYSEX_AUTH_EVENT:
			break;
		case PGRM_CHANGE_EVENT:
		case CHAN_AFTER_EVENT:
		case NOTE_OFF_EVENT:
		case NOTE_ON_EVENT:
		case NOTE_AFTER_EVENT:
		case CONTROLLER_EVENT:
		case PITCH_BLEND_EVENT:
			if (evt->midi.channel == old)
				evt->midi.channel = new;
			break;
		default:
			abort();
			break;
		}
		evt = evt->next;
	}
}

/* Symphony */
struct symphony {
	unsigned long num;
	unsigned long *instruments;
};

/* Load instrument table */
static int load_symphony(const char *fname, struct symphony *s)
{
	unsigned long x;
	FILE *fp;
	char buf[256];

	fp = fopen(fname, "r");
	if (!fp) {
		perror(fname);
		return 0;
	}

	if (!fgets(buf, 255, fp)) {
		perror(fname);
		goto out;
	}

	s->num = strtoul(buf, NULL, 0);
	if (errno) {
		perror(buf);
		goto out;
	}
	if (s->num > 127) {
		printf("A full symphony can have 127 possible instruments!\n");
		goto out;
	}

	s->instruments = calloc(s->num, sizeof (unsigned long));
	if (!s->instruments) {
		perror("calloc");
		goto out;
	}

	for (x = 0; x < s->num; x++) {
		if (!fgets(buf, 255, fp)) {
			perror(fname);
			goto out2;
		}
		s->instruments[x] = strtoul(buf, NULL, 0);
		if (errno) {
			perror(buf);
			goto out2;
		}
		if (s->instruments[x] > 127) {
			printf("MIDI only defines 127 instruments!\n");
			goto out;
		}
	}

	fclose(fp);
	return 1;
out2:
	free(s->instruments);
	s->instruments = NULL;
out:
	fclose(fp);
	return 0;
}

static void free_symphony(struct symphony *s){
	if (s->instruments)
		free(s->instruments);
	free(s);
}

/* Score */
struct part {
	uint8_t instrument;
	unsigned long num_measures;
	unsigned long *measures;
};

struct score {
	unsigned long num_parts;
	struct part *parts;
};

static void free_score(struct score *s)
{
	unsigned long i;

	if (!s->parts)
		goto no_parts;

	for (i = 0; i < s->num_parts; i++)
		if (s->parts[i].measures)
			free(s->parts[i].measures);			

	free (s->parts);

no_parts:
	free(s);
}

static int instrument_in_use(struct score *s, unsigned long instrument,
			     unsigned long score_size)
{
	unsigned long i;

	for (i = 0; i < score_size; i++)
		if (s->parts[i].instrument == instrument)
			return 1;
	return 0;
}

/*
 * Create a score with random measures and a number of randomly selected
 * instruments
 */
static int generate_score(struct score *s, struct dice_table *dt,
			   unsigned long num_dts, struct symphony *symph)
{
	unsigned long i, j, k, l, m;

	if (s->num_parts > symph->num) {
		printf("Score has more parts than instruments??\n");
		return 0;
	}

	printf("Generating score:\n");
	for (i = 0; i < s->num_parts; i++) {
		do {
			m = symph->instruments[randnum(0, symph->num)];
		} while (instrument_in_use(s, m, i));
		s->parts[i].instrument = m;

		printf("%u: ", s->parts[i].instrument);
		m = 0;
		for (j = 0; j < num_dts; j++) {
			for (k = 0; k < dt[j].measures; k++) {
				if (j || k)
					printf(" ");
				l = randnum(0, dt[j].cpm);
				s->parts[i].measures[m] = dt[j].table[k * dt[j].cpm + l];
				printf("%lu", s->parts[i].measures[m]);
				m++;
			}
		}
		printf("\n");
	}

	return 1;
}

/* Turn the score into a big midi file */
static int assemble_midi_files(struct score *score, const char *outfile)
{
	struct mthd *mainfile;
	int res;
	unsigned long i, j;
	int channel = 0;

	/* Create new midi file (0) */
	mainfile = create_new_midi();
	if (!mainfile) {
		return 0;
	}

	/* For each part in the score... */
	for (i = 0; i < score->num_parts; i++) {
		/* Create new midi file (1) */
		struct mthd *midi_part = create_new_midi();

		/* For each measure... */
		for (j = 0; j < score->parts[i].num_measures; j++) {
			char buf[256];
			struct mthd *midi_measure;

			/* Locate the MIDI file */
			if (snprintf(buf, 255, "measures/M%lu.mid", score->parts[i].measures[j]) < 0) {
				perror("snprintf");
				return 0;
			}

			/* Load the midi file */
			midi_measure = read_midi(buf);
			if (!midi_measure) {
				perror(buf);
				return 0;
			}

			/* Remove the first measure (which is a rest) */
			remove_time_indices(midi_measure->track, 1, 1400);

			/* Strip out all all meta events */
			if (!i && !j)
				remove_text_meta_events(midi_measure->track);
			else
				remove_meta_events(midi_measure->track);

			/* Concatenate to (1) */
			concatenate_events(midi_part->track, midi_measure->track);
			free_mthd(midi_measure);
		}

		/* Set the instrument */
		set_instrument(midi_part->track, 0, score->parts[i].instrument);
		set_instrument(midi_part->track, 1, score->parts[i].instrument);

		/* Change the channel of (1)*/
		change_channel(midi_part->track, 0, channel++);
		change_channel(midi_part->track, 1, channel++);

		/* Merge (1) with (0) */
		merge_events(mainfile->track, midi_part->track);

		/* Discard midi (1) */
		free_mthd(midi_part);
	}

	/* Add djwong stamp of approval */
	add_djwong_bits(mainfile->track);

	/* Write midi file */
	res = write_midi(mainfile, outfile);

	free_mthd(mainfile);
	return res;
}

#define ARGV_TABLE_OFFSET 4
static int run_dice(int argc, char *argv[])
{
	unsigned long iterations;
	unsigned long i, j;
	FILE *fp;
	struct score *score;
	struct dice_table *dt;
	struct symphony *symph;
	unsigned long num_dts;

	if (argc < 5) {
		printf("Usage: %s instruments symphony_config outfile tablefile[s]\n", argv[0]);
		return 1;
	}

	/* Load symphony config */
	symph = calloc(1, sizeof (*symph));
	if (!symph) {
		perror("calloc");
		return 12;
	}

	if (!load_symphony(argv[2], symph)) {
		return 13;
	}

	/* How many instruments do we want? */
	iterations = strtoul(argv[1], NULL, 0);
	if (errno) {
		perror(argv[1]);
		return 5;
	}

	num_dts = argc - ARGV_TABLE_OFFSET;
	dt = calloc(num_dts, sizeof (*dt));
	if (!dt) {
		perror("calloc");
		return 7;
	}

	/* read the tables */
	for (i = ARGV_TABLE_OFFSET; i < argc; i++) {
		if (!strcmp(argv[i], "-"))
			fp = stdin;
		else
			fp = fopen(argv[i], "r");
		if (!fp) {
			perror(argv[i]);
			return 2;
		}

		if (!read_table(fp, &dt[i - ARGV_TABLE_OFFSET])) {
			perror(argv[i]);
			fclose(fp);
			return 3;
		}

		fclose(fp);
	}

	/* initialize random number generator */
	if (!seed_random()) {
		perror(argv[1]);
		return 4;
	}
	
	/* set up the score */
	score = calloc(1, sizeof (*score));
	if (!score) {
		perror("calloc");
		return 8;
	}
	score->num_parts = iterations;
	score->parts = calloc(score->num_parts, sizeof (struct part));
	if (!score->parts) {
		perror("calloc");
		return 9;
	}
	j = 0;
	for (i = 0; i < num_dts; i++)
		j += dt[i].measures;
	for (i = 0; i < score->num_parts; i++) {
		score->parts[i].num_measures = j;
		score->parts[i].measures = calloc(score->parts[i].num_measures, sizeof(unsigned long));
		if (!score->parts[i].measures) {
			perror("calloc");
			return 10;
		}
	}

	/* create a score */
	if (!generate_score(score, dt, num_dts, symph))
		return 14;

	free_dice_table(dt, num_dts);
	dt = NULL;
	free_symphony(symph);
	symph = NULL;

	/* create the midi file */
	if (!assemble_midi_files(score, argv[3]))
		return 11;

	free_score(score);
	score = NULL;

	return 0;
}

static int run_dump(int argc, char *argv[])
{
	int a;
	struct mthd *m148;

	if (argc < 2) {
		printf("Usage: %s midifiles\n", argv[0]);
		return 0;
	}

	for (a = 1; a < argc; a++) {
		m148 = read_midi(argv[a]);
		if (!m148)
			continue;
		dump_mthd(m148);
		free_mthd(m148);
	}

	return 0;
}

static int run_merge(int argc, char *argv[])
{
	int a;
	struct mthd *m148, *moo;
	int channel = 0;

	if (argc < 4) {
		printf("Usage: %s outfile midi1 midifiles\n", argv[0]);
		return 0;
	}

	moo = read_midi(argv[2]);
	if (!moo)
		return 1;

	for (a = 3; a < argc; a++) {
		m148 = read_midi(argv[a]);
		if (!m148)
			continue;
		remove_meta_events(m148->track);
		change_channel(m148->track, 0, channel++);
		change_channel(m148->track, 1, channel++);
		merge_events(moo->track, m148->track);
		free_mthd(m148);
	}

	add_djwong_bits(moo->track);
	write_midi(moo, argv[1]);
	free_mthd(moo);

	return 0;
}

static int run_concat(int argc, char *argv[])
{
	int a;
	struct mthd *m148, *moo;

	if (argc < 4) {
		printf("Usage: %s outfile midi1 midifiles\n", argv[0]);
		return 0;
	}

	moo = read_midi(argv[2]);
	if (!moo)
		return 1;

	for (a = 3; a < argc; a++) {
		m148 = read_midi(argv[a]);
		if (!m148)
			continue;
		remove_meta_events(m148->track);
		concatenate_events(moo->track, m148->track);
		free_mthd(m148);
	}

	add_djwong_bits(moo->track);
	write_midi(moo, argv[1]);
	free_mthd(moo);

	return 0;
}

static int run_setinstrument(int argc, char *argv[])
{
	int a;
	unsigned long instr, channel;
	struct mthd *m148;

	if (argc < 4) {
		printf("Usage: %s instrument channel midifiles\n", argv[0]);
		return 0;
	}

	instr = strtoul(argv[1], NULL, 0);
	if (errno) {
		perror(argv[1]);
		return 3;
	}
	if (instr > 127) {
		printf("MIDI instruments stop at 127.\n");
		return 2;
	}

	channel = strtoul(argv[2], NULL, 0);
	if (errno) {
		perror(argv[2]);
		return 5;
	}
	if (instr > 15) {
		printf("MIDI channels stop at 15.\n");
		return 4;
	}

	for (a = 3; a < argc; a++) {
		m148 = read_midi(argv[a]);
		if (!m148)
			continue;
		set_instrument(m148->track, channel, instr);
		write_midi(m148, argv[a]);
		free_mthd(m148);
	}

	return 0;
}

struct main_func {
	const char *name;
	int (*fn)(int argc, char *argv[]);
} main_funcs[] = {
	{"dice", run_dice},
	{"dump", run_dump},
	{"mcat", run_concat},
	{"mmerge", run_merge},
	{"msi", run_setinstrument},
	{NULL, NULL}
};

int main(int argc, char *argv[])
{
	struct main_func *mf = main_funcs;

	while (mf->name) {
		if (strstr(argv[0], mf->name))
			return mf->fn(argc, argv);
		mf++;
	}

	return run_dice(argc, argv);
}
