/*
 * Copyright(C) Paul und Scherer (mct.de/mct.net)
 *
 * This example demonstrates how to...
 *
 *  ... use TPU channel 2 to generate sound.
 */

#include <stdio.h>
#include <sys/time.h>
#include <target.h>
#include "../tty.h"

#define PERIOD	(hz? 16000000/32/hz: 0)		/* frequency to time (TPU clocks) */

/*
 * Beep at hz Hertz (0 =off) at TP2.
 */
static void
beep(unsigned hz)
{
	INTERN.tpu.tmcr	     = 0;		/* enable TPU */
	INTERN.tpu.c[2].p[0] = 0x80;		/* TP2 output channel */
	INTERN.tpu.c[2].p[3] = PERIOD;		/*     set period */
	INTERN.tpu.c[2].p[2] = PERIOD/2;	/*         50% duty cycle */
	INTERN.tpu.cfsr3    |= 0x900;		/*     PWM function  (CFS =  9) */
	INTERN.tpu.hsrr1    |= 0x20;		/*     initialize    (HSR =%10) */
	INTERN.tpu.cpr1	    |= 0x30;		/*     high priority (CPR =%11) */
}

/*
 * Play melody using beep() and _delay()
 * (connect TP2 via 20kOhm to line in).
 */
int
main(void)
{
	int hld = 1;				/* hold (=staccato off) */
	int dly = 3;				/* tempo */
	int oct = 1;				/* octave shift */

	puts("Playing...\n\n"
	     ". : toggle staccato\n"
	     "+ : faster\n"
	     "- : slower\n"
	     "> : octave up\n"
	     "< : octave down\n"
	);
	while (1) {
		static short const ftbl[] =	/* frequency table for notes */
		{				/*  (rounded to ints) */
			  0,			/*  - code =@ */
			220,			/* a0       A */
			247,			/* b0       B */
			262,			/* c1       C */
			294,			/* d1       D */
			330,			/* e1       E */
			349,			/* f1       F */
			392,			/* g1       G */
			440,			/* a1       H */
			494,			/* b1       I */
			523			/* c2       J */
		};

		/*
		 * melody
		 */
		char *p = "GGGFEGJDEEEECCJCHHHICIJHGGGGEEEEG@@FEGCDEED@C@JCDD@DC@B@CCCCC@@@@@@@"
			  "CCDDEEC@CCDDEEC@EEFFGGG@EEFFGGG@GHGFEECJGHGFE@CCJJGGJJJ@JJGGJJJ@@@@@"
			  "CEFGGGG@CEFGGGG@CEFGGEECCE@EDDD@DEDCC@JEEG@GFFF@FEFG@EECCDDCCCC@@@@@";

		int last;			/* last note */

		for (last = 0; *p; p++) {
			if (kbhit()) switch (getchar()) {
			case '.': hld = !hld; break;
			case '+': dly--;      break;
			case '-': dly++;      break;
			case '>': oct++;      break;
			case '<': oct--;      break;
			}

			if (dly > 9) dly = 0;
			if (dly < 0) dly = 9;
			if (oct > 3) oct = 0;
			if (oct < 0) oct = 3;

			if (*p != last) beep(ftbl[(last = *p)-'@']<<oct);
			if (!hld) _delay(60000), beep(0);
			_delay((dly+hld+1)*60000);
		}
		hld = !hld;			/* toggle staccato */
		dly--;				/* speed up */
		oct++;				/* shift up */
	}
}
