/*
 *	SCROLL - piano-roll-style display of a score file
 *
 *	Display the note events in a score file as a piano roll on a standard
 *	terminal.  Use 80 chars top represent 80 semitines (6 1/2 octaves).
 *
 *	- Scan the input for maximum and minimum note values
 *	- Run through the score representing the presence of a note by
 *	  a mark in that column - decide what length of time a char space
 *	  represents.  Use [ ,'|] for 2ce vres?
 */

#include <local/cscore.h>

/* width of output line in chars */
#define MAXWIDTH 80

static char *progname;

main(argc, argv)
char **argv;
{
	struct evlist *a;
	register int i;
	int nev;		/* Number of events in the score */
	float minpch, maxpch;	/* highest and lowest notes in score */
	int minnn, maxnn;	/* Minimum and maximum note numbers */
	int range;		/* total number of notes included in range */
	struct event *evp;	/* pointer to scan events */
	float t;		/* time, for scanning score during printing */
	float dt = 0.5;		/* how long does each screen line represent? */
	float maxtime;		/* latest event in score */

	progname = argv[0];

	a = lget();		/* Read in the score */

	/* Scan the score to find the max and min note values (in p2) */
	minpch = 16.00;	/* 8 octaves aboce middle C */
	maxpch = 0.00;	/* 8 below */
	maxtime = 0;

	nev = lcount(a);	/* find the number of events in it */

	for (i=0; i<nev; i++) {
		float pch;	/* note's pitch class */
		struct event *evp;

		evp = a->e[i];
		if (evp == NULL) continue;

		if (evp->op != 'i') continue;

		/* We have a note event. */
		/* p1 - instrument number
		 * p2 - time of event in beats
		 * p3 - duration in beats
		 * Scot provides:
		 * p4 - slur information
		 * p5 - pitch information as 8.00 for middle c, 9.02 for top d
		 */
		pch = evp->p[5];
		if (pch < minpch) minpch = pch;
		if (pch > maxpch) maxpch = pch;

		/* accumulate maximum time value also */
		t = evp->p[2];
		if (t > maxtime) maxtime = t;
	}

	if (maxpch == 0.00) die("No note events in score");

	minnn = pch_to_nn(minpch);
	maxnn = pch_to_nn(maxpch);
	range = maxnn - minnn + 1;	/* remember the last fencepost */

	if (range > MAXWIDTH) {
		fprintf(stderr, "%s: range of %d  semitones exceeds screen width of %d.  Truncating top end.\n", progname, range, MAXWIDTH);
	}

	printf("Range of piece is %d semitone keys.\n", range);

	/* Churn out the score, low notes on the left. */
	lsort(a);

	for (t=0; t < maxtime; t+=dt) {
		struct evlist *events;
		int nev;		/* Hides previous declaration */
		char oline[MAXWIDTH];	/* output line under construction */
		static float lines[] = {
			6.07, 6.11, 7.02, 7.05, 7.09,	/* Bottom GBDFA */
			8.04, 8.07, 8.11, 9.02, 9.05,	/* Top EGBDF */
		};

		/* clear the buffer */
		for (i=0; i<MAXWIDTH; i++) oline[i] = ' ';
		/* put a stripe down the manuscript lines */
		
		for (i=0; i<(sizeof(lines)/sizeof(lines[0])); i++) {
			int nn = pch_to_nn(lines[i]);
			if (nn >= minnn && nn <= maxnn) {
				oline[nn-minnn] = '|';
			}
		}

		/* Extract events for this timeslot and display them */
		events = lxtimev(a, t, t+dt);
		nev = lcount(events);
		for (i=0; i<nev; i++) {
			struct event *evp = events->e[i];
			if (evp == NULL) continue;
			if (evp->op != 'i') continue;
			oline[pch_to_nn(evp->p[5]) - minnn] = 'o';
		}
		lrelev(events);

		/* trim output line */
		for (i = MAXWIDTH-1; i>=0 && oline[i] == ' '; i--);
		printf("%.*s\n", i+1, oline);
	}

	exit(0);
}

die(s)
char *s;
{
	fprintf(stderr, "%s: %s\n", progname, s);
	exit(1);
}

/* Convert pitch in octave-class notation to note number (in semitones) */
int
pch_to_nn(pch)
float pch;
{
	int octave = (int)pch;
	int note = (pch - octave) * 100 + 0.5;

	return(octave * 12 + note);
}
