#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define MSG(s) fprintf(stderr, "%s() %s L%d : %s\n", __func__, __FILE__, __LINE__, s)
#define ERR(s) do{ fprintf(stderr, "ERR "); MSG(s); exit(1); }while(0)

int bk_buf = -1;

int
rd(void)
{
	int v;

	if((v = bk_buf) < 0) return getchar();
	bk_buf = -1;
	return v;
}

void
bk(int v)
{
	if(bk_buf >= 0) ERR("give up");
	bk_buf = v;
}

void
rd_str(int n, char *s)
{
	int i;

	for(i=0; i<n; i++) s[i] = rd();
	s[i] = '\0';
}

int
rd_str_chk(int n, char *s)
{
	char buf[256];

	rd_str(n, buf);
	return strcmp(buf, s) == 0;
}

int
rd_int(int n) /* big endian */
{
	int i, v;

	v = 0;
	for(i=0; i<n; i++) v = (v << 8) | rd();
	return v;
}

int
rd_delta(void)
{
	int v, c;

	v = 0;
	do{
		if((c = rd()) == EOF) return EOF;
		v = (v << 7) | (c & 0x7f);
	}while(c & 0x80);
	return v;
}


#define NOTE_BUF_N	256

struct note_rec{
	int ch;
	int note;
	double on_sec;
} note_buf[NOTE_BUF_N];

int smp_freq, smp_cnt;
int bit_len;
int sign;
int ch_num;

int
note_buf_is_free(int i)
{
	return note_buf[i].ch < 0;
}

void
note_buf_free(int i)
{
	note_buf[i].ch = -1;
}

void
note_buf_init(void)
{
	int i;

	for(i=0; i<NOTE_BUF_N; i++) note_buf_free(i);
}

int
note_buf_search(int ch, int note)
{
	int i;

	for(i=0; i<NOTE_BUF_N; i++) {
		if(note_buf[i].ch == ch && 
		   (ch < 0 || note_buf[i].note == note)) return i;
	}
	return -1;
}

int
header(void)
{
	int v;

	if(!rd_str_chk(4, "MThd")) ERR("header id");
	if(rd_int(4) != 6) ERR("header size");
	if(rd_int(2) != 0) ERR("header format type");
	if(rd_int(2) != 1) ERR("header track num");
	if((v = rd_int(2)) & 0x8000) ERR("header division");
	return v; /* div4 */
}

double
note_to_freq(int note)
{
	return 440 * pow(2, (note - 69) / 12.0);
}

struct out_rec{
	int base;
	int amp;
	int iv_min;
	int iv_max;
} otr;

void
out_init(struct out_rec *ot)
{
	ot->amp = bit_len == 8 ? 127 : 32767;
	ot->base = sign ? 0 : ot->amp + 1;
	ot->iv_min = ot->base - (ot->amp + 1);
	ot->iv_max = ot->base + ot->amp;
}

void
out_do(struct out_rec *ot, double v)
{
	int iv;

	iv = ot->base + (int)(v *ot->amp);
	if(iv > ot->iv_max) iv = ot->iv_max;
	if(iv < ot->iv_min) iv = ot->iv_min;
	switch(bit_len){
	case 8:
		putchar(iv);
		break;
	case 16:
		putchar(iv & 0xff);
		putchar((iv >> 8) & 0xff);
		break;
	}
}

void
note_onoff(int onoff, double evt_sec, int ch, int note)
{
	double v, sec, freq, dsec;
	double vl, vr, pan;
	int i;

	if(ch == 9) return;

	while((sec = (double)smp_cnt / smp_freq) < evt_sec){
		vl = vr = 0;
		for(i=0; i<NOTE_BUF_N; i++){
			if(note_buf_is_free(i)) continue;
			freq = note_to_freq(note_buf[i].note);
			dsec = sec - note_buf[i].on_sec;
			v = sin(2 * M_PI * freq * dsec);
			if(ch_num == 1){
				vl += v;
			}else{
				pan = note_buf[i].ch / 15.0;
				vl += (1 - pan) * v;
				vr += pan * v;
			}
		}
		out_do(&otr, vl / 16);
		if(ch_num > 1) out_do(&otr, vr / 16);
		smp_cnt++;
	}
	if(onoff){
		if((i = note_buf_search(-1, 0)) < 0){
			MSG("note_buf full");
			return;
		}
		note_buf[i].note = note;
		note_buf[i].ch = ch;
		note_buf[i].on_sec = evt_sec;
	}else{
		if((i = note_buf_search(ch, note)) < 0) return;
		note_buf_free(i);
	}
}

int
opt_idx(char *key, int ac, char **av)
{
	int i;

	for(i=1; i<ac; i++) if(strcmp(av[i], key) == 0) return i;
	return -1;
}

int
opt_int(char *key, int ac, char **av, int def)
{
	int i;

	i = opt_idx(key, ac, av);
	if(i < 0 || i+1 >= ac) return def;
	return strtol(av[i+1], NULL, 0);
}

int
main(int ac, char **av)
{
	int div4, delta_sum;
	int i, n, v, hi, low, note;
	double sec;

	smp_freq = opt_int("-r", ac, av, 8000);
	smp_cnt = 0;
	bit_len = opt_int("-b", ac, av, 8);
	if(bit_len != 8 && bit_len != 16) ERR("bit_len");
	sign = (opt_idx("-s", ac, av) >= 0);
	ch_num = opt_int("-c", ac, av, 1);
	out_init(&otr);
	note_buf_init();

	div4 = header();

	if(!rd_str_chk(4, "MTrk")) ERR("track id");
	v = rd_int(4); /* skip */

	hi = low = 0;
	delta_sum = 0;
	while((v = rd_delta()) != EOF){
		delta_sum += v;
		sec = (double)delta_sum / div4 * 0.5;
		
		if((v = rd()) & 0x80){
			hi = (v >> 4) & 0xf;
			low  = v & 0xf;
		}else bk(v);

		switch(hi){
		case 8:
		case 9:
			note = rd();
			rd(); /* skip velo */
			note_onoff(hi == 9, sec, low, note);
			break;
		case 0xa:
		case 0xb:
		case 0xe:
			rd();
			rd();
			break;
		case 0xc:		
		case 0xd:
			rd();
			break;
		case 0xf:
			rd();
			switch(low){
			case 0:
				while(rd() != 0xf7);
				break;
			case 1:
			case 3:
				rd();
				break;
			case 2:
				rd();
				rd();
				break;
			case 0xf:
				n = rd();
				for(i=0; i<n; i++) rd();
				break;
			default:
				break;
			}
			break;
		}
	}
	return 0;
}

/* EOF */