/*
 *  Simple event decoder
 */

static char *event_names[256] = {
	/* 0   */	"System",
	/* 1   */	"Result",
	/* 2   */	"Reserved 2",
	/* 3   */	"Reserved 3",
	/* 4   */	"Reserved 4",
	/* 5   */	"Note",
	/* 6   */	"Note On",
	/* 7   */	"Note Off",
	/* 8   */	"Key Pressure",
	/* 9   */	"Reserved 9",
	/* 10   */	"Controller",
	/* 11   */	"Program Change",
	/* 12   */	"Channel Pressure",
	/* 13   */	"Pitchbend",
	/* 14   */	"Control14",
	/* 15   */	"Nonregparam",
	/* 16   */	"Regparam",
	/* 17   */	"Reserved 17",
	/* 18   */	"Reserved 18",
	/* 19   */	"Reserved 19",
	/* 20   */	"Song Position",
	/* 21   */	"Song Select",
	/* 22   */	"Qframe",
	/* 23   */	"SMF Time Signature",
	/* 24   */	"SMF Key Signature",
	/* 25   */	"Reserved 25",
	/* 26   */	"Reserved 26",
	/* 27   */	"Reserved 27",
	/* 28   */	"Reserved 28",
	/* 29   */	"Reserved 29",
	/* 30   */	"Start",
	/* 31   */	"Continue",
	/* 32   */	"Stop",
	/* 33   */	"Set Position Tick",
	/* 34   */	"Set Position Time",
	/* 35   */	"Tempo",
	/* 36   */	"Clock",
	/* 37   */	"Tick",
	/* 38   */	"Reserved 38",
	/* 39   */	"Reserved 39",
	/* 40   */	"Tune Request",
	/* 41   */	"Reset",
	/* 42   */	"Active Sensing",
	/* 43   */	"Reserved 43",
	/* 44   */	"Reserved 44",
	/* 45   */	"Reserved 45",
	/* 46   */	"Reserved 46",
	/* 47   */	"Reserved 47",
	/* 48   */	"Reserved 48",
	/* 49   */	"Reserved 49",
	/* 50   */	"Echo",
	/* 51   */	"OSS",
	/* 52   */	"Reserved 52",
	/* 53   */	"Reserved 53",
	/* 54   */	"Reserved 54",
	/* 55   */	"Reserved 55",
	/* 56   */	"Reserved 56",
	/* 57   */	"Reserved 57",
	/* 58   */	"Reserved 58",
	/* 59   */	"Reserved 59",
	/* 60   */	"Client Start",
	/* 61   */	"Client Exit",
	/* 62   */	"Client Change",
	/* 63   */	"Port Start",
	/* 64   */	"Port Exit",
	/* 65   */	"Port Change",
	/* 66   */	"Port Subscribed",
	/* 67   */	"Port Used",
	/* 68   */	"Port Unsubscribed",
	/* 69   */	"Port Unused",
	/* 70   */	"Sample",
	/* 71   */	"Sample Cluster",
	/* 72   */	"Sample Start",
	/* 73   */	"Sample Stop",
	/* 74   */	"Sample Freq",
	/* 75   */	"Sample Volume",
	/* 76   */	"Sample Loop",
	/* 77   */	"Sample Position",
	/* 78   */	"Sample Private1",
	/* 79   */	"Reserved 79",
	/* 80   */	"Reserved 80",
	/* 81   */	"Reserved 81",
	/* 82   */	"Reserved 82",
	/* 83   */	"Reserved 83",
	/* 84   */	"Reserved 84",
	/* 85   */	"Reserved 85",
	/* 86   */	"Reserved 86",
	/* 87   */	"Reserved 87",
	/* 88   */	"Reserved 88",
	/* 89   */	"Reserved 89",
	/* 90   */	"User 0",
	/* 91   */	"User 1",
	/* 92   */	"User 2",
	/* 93   */	"User 3",
	/* 94   */	"User 4",
	/* 95   */	"User 5",
	/* 96   */	"User 6",
	/* 97   */	"User 7",
	/* 98   */	"User 8",
	/* 99   */	"User 9",
	/* 100  */	"Instr Begin",
	/* 101  */	"Instr End",
	/* 102  */	"Instr Info",
	/* 103  */	"Instr Info Result",
	/* 104  */	"Instr Finfo",
	/* 105  */	"Instr Finfo Result",
	/* 106  */	"Instr Reset",
	/* 107  */	"Instr Status",
	/* 108  */	"Instr Status Result",
	/* 109  */	"Instr Put",
	/* 110  */	"Instr Get",
	/* 111  */	"Instr Get Result",
	/* 112  */	"Instr Free",
	/* 113  */	"Instr List",
	/* 114  */	"Instr List Result",
	/* 115  */	"Instr Cluster",
	/* 116  */	"Instr Cluster Get",
	/* 117  */	"Instr Cluster Result",
	/* 118  */	"Instr Change",
	/* 119  */	"Reserved 119",
	/* 120  */	"Reserved 120",
	/* 121  */	"Reserved 121",
	/* 122  */	"Reserved 122",
	/* 123  */	"Reserved 123",
	/* 124  */	"Reserved 124",
	/* 125  */	"Reserved 125",
	/* 126  */	"Reserved 126",
	/* 127  */	"Reserved 127",
	/* 128  */	"Reserved 128",
	/* 129  */	"Reserved 129",
	/* 130  */	"Sysex",
	/* 131  */	"Bounce",
	/* 132  */	"Reserved 132",
	/* 133  */	"Reserved 133",
	/* 134  */	"Reserved 134",
	/* 135  */	"User Var0",
	/* 136  */	"User Var1",
	/* 137  */	"User Var2",
	/* 138  */	"User Var3",
	/* 139  */	"User Var4",
	/* 140  */	"IPC Shm",
	/* 141  */	"Reserved 141",
	/* 142  */	"Reserved 142",
	/* 143  */	"Reserved 143",
	/* 144  */	"Reserved 144",
	/* 145  */	"User IPC0",
	/* 146  */	"User IPC1",
	/* 147  */	"User IPC2",
	/* 148  */	"User IPC3",
	/* 149  */	"User IPC4",
	/* 150  */	"Reserved 150",
	/* 151  */	"Reserved 151",
	/* 152  */	"Reserved 152",
	/* 153  */	"Reserved 153",
	/* 154  */	"Reserved 154",
	/* 155  */	"Reserved 155",
	/* 156  */	"Reserved 156",
	/* 157  */	"Reserved 157",
	/* 158  */	"Reserved 158",
	/* 159  */	"Reserved 159",
	/* 160  */	"Reserved 160",
	/* 161  */	"Reserved 161",
	/* 162  */	"Reserved 162",
	/* 163  */	"Reserved 163",
	/* 164  */	"Reserved 164",
	/* 165  */	"Reserved 165",
	/* 166  */	"Reserved 166",
	/* 167  */	"Reserved 167",
	/* 168  */	"Reserved 168",
	/* 169  */	"Reserved 169",
	/* 170  */	"Reserved 170",
	/* 171  */	"Reserved 171",
	/* 172  */	"Reserved 172",
	/* 173  */	"Reserved 173",
	/* 174  */	"Reserved 174",
	/* 175  */	"Reserved 175",
	/* 176  */	"Reserved 176",
	/* 177  */	"Reserved 177",
	/* 178  */	"Reserved 178",
	/* 179  */	"Reserved 179",
	/* 180  */	"Reserved 180",
	/* 181  */	"Reserved 181",
	/* 182  */	"Reserved 182",
	/* 183  */	"Reserved 183",
	/* 184  */	"Reserved 184",
	/* 185  */	"Reserved 185",
	/* 186  */	"Reserved 186",
	/* 187  */	"Reserved 187",
	/* 188  */	"Reserved 188",
	/* 189  */	"Reserved 189",
	/* 190  */	"Reserved 190",
	/* 191  */	"Reserved 191",
	/* 192  */	"Reserved 192",
	/* 193  */	"Reserved 193",
	/* 194  */	"Reserved 194",
	/* 195  */	"Reserved 195",
	/* 196  */	"Reserved 196",
	/* 197  */	"Reserved 197",
	/* 198  */	"Reserved 198",
	/* 199  */	"Reserved 199",
	/* 200  */	"Reserved 200",
	/* 201  */	"Reserved 201",
	/* 202  */	"Reserved 202",
	/* 203  */	"Reserved 203",
	/* 204  */	"Reserved 204",
	/* 205  */	"Reserved 205",
	/* 206  */	"Reserved 206",
	/* 207  */	"Reserved 207",
	/* 208  */	"Reserved 208",
	/* 209  */	"Reserved 209",
	/* 210  */	"Reserved 210",
	/* 211  */	"Reserved 211",
	/* 212  */	"Reserved 212",
	/* 213  */	"Reserved 213",
	/* 214  */	"Reserved 214",
	/* 215  */	"Reserved 215",
	/* 216  */	"Reserved 216",
	/* 217  */	"Reserved 217",
	/* 218  */	"Reserved 218",
	/* 219  */	"Reserved 219",
	/* 220  */	"Reserved 220",
	/* 221  */	"Reserved 221",
	/* 222  */	"Reserved 222",
	/* 223  */	"Reserved 223",
	/* 224  */	"Reserved 224",
	/* 225  */	"Reserved 225",
	/* 226  */	"Reserved 226",
	/* 227  */	"Reserved 227",
	/* 228  */	"Reserved 228",
	/* 229  */	"Reserved 229",
	/* 230  */	"Reserved 230",
	/* 231  */	"Reserved 231",
	/* 232  */	"Reserved 232",
	/* 233  */	"Reserved 233",
	/* 234  */	"Reserved 234",
	/* 235  */	"Reserved 235",
	/* 236  */	"Reserved 236",
	/* 237  */	"Reserved 237",
	/* 238  */	"Reserved 238",
	/* 239  */	"Reserved 239",
	/* 240  */	"Reserved 240",
	/* 241  */	"Reserved 241",
	/* 242  */	"Reserved 242",
	/* 243  */	"Reserved 243",
	/* 244  */	"Reserved 244",
	/* 245  */	"Reserved 245",
	/* 246  */	"Reserved 246",
	/* 247  */	"Reserved 247",
	/* 248  */	"Reserved 248",
	/* 249  */	"Reserved 249",
	/* 250  */	"Reserved 250",
	/* 251  */	"Reserved 251",
	/* 252  */	"Reserved 252",
	/* 253  */	"Reserved 253",
	/* 254  */	"Reserved 254",
	/* 255  */	"None"
};

int decode_event(snd_seq_event_t * ev)
{
	char *space = "         ";

	printf("EVENT>>> Type = %d, flags = 0x%x", ev->type, ev->flags);
	switch (ev->flags & SND_SEQ_TIME_STAMP_MASK) {
		case SND_SEQ_TIME_STAMP_TICK:
			printf(", time = %d ticks",
			       ev->time.tick);
			break;
		case SND_SEQ_TIME_STAMP_REAL:
			printf(", time = %d.%09d",
			       (int)ev->time.time.tv_sec,
			       (int)ev->time.time.tv_nsec);
			break;
	}
	printf("\n%sSource = %d.%d, dest = %d.%d, queue = %d\n",
	       space,
	       ev->source.client,
	       ev->source.port,
	       ev->dest.client,
	       ev->dest.port,
	       ev->queue);

	printf("%sEvent = %s", space, event_names[ev->type]);
	/* decode actual event data... */
	switch (ev->type) {
		case SND_SEQ_EVENT_NOTE:
			printf("; ch=%d, note=%d, velocity=%d, off_velocity=%d, duration=%d\n",
			       ev->data.note.channel,
			       ev->data.note.note,
			       ev->data.note.velocity,
			       ev->data.note.off_velocity,
			       ev->data.note.duration);
			break;

		case SND_SEQ_EVENT_NOTEON:
		case SND_SEQ_EVENT_NOTEOFF:
		case SND_SEQ_EVENT_KEYPRESS:
			printf("; ch=%d, note=%d, velocity=%d\n",
			       ev->data.note.channel,
			       ev->data.note.note,
			       ev->data.note.velocity);
			break;
		
		case SND_SEQ_EVENT_CONTROLLER:
			printf("; ch=%d, param=%i, value=%i\n",
			       ev->data.control.channel,
			       ev->data.control.param,
			       ev->data.control.value);
			break;

		case SND_SEQ_EVENT_PGMCHANGE:
			printf("; ch=%d, program=%i\n",
			       ev->data.control.channel,
			       ev->data.control.value);
			break;
			
		case SND_SEQ_EVENT_CHANPRESS:
		case SND_SEQ_EVENT_PITCHBEND:
			printf("; ch=%d, value=%i\n",
			       ev->data.control.channel,
			       ev->data.control.value);
			break;
			
		case SND_SEQ_EVENT_SYSEX:
			{
				unsigned char *sysex = (unsigned char *) ev + sizeof(snd_seq_event_t);
				int c;

				printf("; len=%d [", ev->data.ext.len);

				for (c = 0; c < ev->data.ext.len; c++) {
					printf("%02x%s", sysex[c], c < ev->data.ext.len - 1 ? ":" : "");
				}
				printf("]\n");
			}
			break;
			
		case SND_SEQ_EVENT_QFRAME:
			printf("; frame=0x%02x\n", ev->data.control.value);
			break;
			
		case SND_SEQ_EVENT_CLOCK:
		case SND_SEQ_EVENT_START:
		case SND_SEQ_EVENT_CONTINUE:
		case SND_SEQ_EVENT_STOP:
			printf("; queue = %i\n", ev->data.queue.queue);
			break;

		case SND_SEQ_EVENT_SENSING:
			printf("\n");
			break;

		case SND_SEQ_EVENT_ECHO:
			{
				int i;
				
				printf("; ");
				for (i = 0; i < 8; i++) {
					printf("%02i%s", ev->data.raw8.d[i], i < 7 ? ":" : "\n");
				}
		 	}
		 	break;
			
		case SND_SEQ_EVENT_CLIENT_START:
		case SND_SEQ_EVENT_CLIENT_EXIT:
		case SND_SEQ_EVENT_CLIENT_CHANGE:
			printf("; client=%i\n", ev->data.addr.client);
			break;

		case SND_SEQ_EVENT_PORT_START:
		case SND_SEQ_EVENT_PORT_EXIT:
		case SND_SEQ_EVENT_PORT_CHANGE:
		case SND_SEQ_EVENT_PORT_SUBSCRIBED:
		case SND_SEQ_EVENT_PORT_USED:
		case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
		case SND_SEQ_EVENT_PORT_UNUSED:
			printf("; client=%i, port = %i\n", ev->data.addr.client, ev->data.addr.port);
			break;

		default:
			printf("; not implemented\n");
	}


	switch (ev->flags & SND_SEQ_EVENT_LENGTH_MASK) {
		case SND_SEQ_EVENT_LENGTH_FIXED:
			return sizeof(snd_seq_event_t);

		case SND_SEQ_EVENT_LENGTH_VARIABLE:
			return sizeof(snd_seq_event_t) + ev->data.ext.len;
	}

	return 0;
}

void event_decoder_start_timer(snd_seq_t *handle, int queue, int client, int port)
{
	int err;

	if ((err = snd_seq_start_queue(handle, queue, NULL))<0)
		fprintf(stderr, "Timer event output error: %s\n", snd_strerror(err));
	while (snd_seq_flush_output(handle)>0)
		sleep(1);
}

void event_decoder(snd_seq_t *handle, int argc, char *argv[])
{
	snd_seq_event_t *ev;
	snd_seq_port_info_t port;
	snd_seq_port_subscribe_t sub;
	fd_set in;
	int client, queue, max, err, v1, v2;
	char *ptr;

	if ((client = snd_seq_client_id(handle))<0) {
		fprintf(stderr, "Cannot determine client number: %s\n", snd_strerror(client));
		return;
	}
	printf("Client ID = %i\n", client);
	if ((queue = snd_seq_alloc_queue(handle))<0) {
		fprintf(stderr, "Cannot allocate queue: %s\n", snd_strerror(queue));
		return;
	}
	printf("Queue ID = %i\n", queue);
	if ((err = snd_seq_block_mode(handle, 0))<0)
		fprintf(stderr, "Cannot set nonblock mode: %s\n", snd_strerror(err));
	bzero(&port, sizeof(port));
	strcpy(port.name, "Input");
	port.capability = SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_READ; 
	port.capability |= SND_SEQ_PORT_CAP_SUBS_WRITE; 
	if ((err = snd_seq_create_port(handle, &port)) < 0) {
		fprintf(stderr, "Cannot create input port: %s\n", snd_strerror(err));
		return;
	}
	event_decoder_start_timer(handle, queue, client, port.port);

	bzero(&sub, sizeof(sub));
	sub.sender.client = SND_SEQ_CLIENT_SYSTEM;
	sub.sender.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
	sub.dest.client = client;
	sub.dest.port = port.port;
	sub.queue = queue;
	sub.exclusive = 0;
	sub.convert_time = 1;
	sub.realtime = 1;
	if ((err = snd_seq_subscribe_port(handle, &sub))<0) {
		fprintf(stderr, "Cannot subscribe announce port: %s\n", snd_strerror(err));
		return;
	}
	sub.sender.port = SND_SEQ_PORT_SYSTEM_TIMER;
	if ((err = snd_seq_subscribe_port(handle, &sub))<0) {
		fprintf(stderr, "Cannot subscribe timer port: %s\n", snd_strerror(err));
		return;
	}

	for (max = 0; max < argc; max++) {
		ptr = argv[max];
		if (!ptr)
			continue;
		sub.realtime = 0;
		if (tolower(*ptr) == 'r') {
			sub.realtime = 1;
			ptr++;
		}
		if (sscanf(ptr, "%i.%i", &v1, &v2) != 2) {
			fprintf(stderr, "Wrong argument '%s'...\n", argv[max]);
			return;
		}
		sub.sender.client = v1;
		sub.sender.port = v2;
		if ((err = snd_seq_subscribe_port(handle, &sub))<0) {
			fprintf(stderr, "Cannot subscribe port %i from client %i: %s\n", v2, v1, snd_strerror(err));
			return;
		}
	}
	
	while (1) {
		FD_ZERO(&in);
		FD_SET(max = snd_seq_file_descriptor(handle), &in);
		if (select(max + 1, &in, NULL, NULL, NULL) < 0)
			break;
		do {
			if ((err = snd_seq_event_input(handle, &ev))<0)
				break;
			if (!ev)
				continue;
			decode_event(ev);
			snd_seq_free_event(ev);
		} while (err > 0);
	}
}
