/*
 * midigen.c - MIDI traffic generator
 *
 * Written 2011 by Werner Almesberger
 * Copyright 2011 Werner Almesberger
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */


#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <alsa/asoundlib.h>


#define	NAME "midigen"


static void send_cc(snd_seq_t *midi, snd_seq_event_t *ev, int chan)
{
	static unsigned v = 0;

	snd_seq_ev_set_controller(ev, chan, (v >> 7) & 127, v & 127);
	v++;
	snd_seq_event_output(midi, ev);
	snd_seq_drain_output(midi);
}


static void generate(snd_seq_t *midi, int port, int chan, int t_s, int t_us)
{
	snd_seq_event_t ev;
	struct timeval next, now;
	int dt_us;

	snd_seq_ev_clear(&ev);
	snd_seq_ev_set_source(&ev, port);
	snd_seq_ev_set_subs(&ev);
	snd_seq_ev_set_direct(&ev);

	gettimeofday(&next, NULL);
	while (1) {
		next.tv_sec += t_s;
		next.tv_usec += t_us;
		if (next.tv_usec > 999999) {
			next.tv_sec++;
			next.tv_usec -= 1000000;
		}
		gettimeofday(&now, NULL);
		dt_us = (next.tv_sec-now.tv_sec)*1000000+
		    next.tv_usec-now.tv_usec;
		if (dt_us > 0)
			usleep(dt_us);
		send_cc(midi, &ev, chan);
	}
}

static void usage(const char *name)
{
	fprintf(stderr, "usage: %s [-c channel] rate_hz\n", name);
	exit(1);
}


int main(int argc, char **argv)
{
	snd_seq_t *midi;
	int c;
	int chan = 0, t_us;
	int port;

	while ((c = getopt(argc, argv, "c:")) != EOF)
		switch (c) {
		case 'c':
			chan = atoi(optarg);
			break;
		default:
			usage(*argv);
		}

	switch (argc-optind) {
	case 1:
		t_us = 1000000/atof(argv[optind]);
		break;
	default:
		usage(*argv);
	}

	if (snd_seq_open(&midi, "hw", SND_SEQ_OPEN_OUTPUT,0) < 0) {
		fprintf(stderr, "can't open ALSA sequencer\n");
		exit(1);
	}
	snd_seq_set_client_name(midi, NAME);
	port = snd_seq_create_simple_port(midi, NAME,
              SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
              SND_SEQ_PORT_TYPE_APPLICATION);
	if (port < 0) {
		fprintf(stderr, "can't create sequencer port\n");
		exit(1);
	}

	generate(midi, port, chan, t_us/1000000, t_us % 1000000);

	return 0;
}
