/*
 * midi.c
 *
 *	Convert MIDI clock events into DIN sync for my 808.
 *
 * Atmel attiny2313 running at 8Mhz with lfuse set to 0xED.
 *
 * 	PB0 = led
 *	PB1 = Din clock out
 *	PB2 = Din start/stop
 *
 *	PD4 = pushbutton (active low-- needs pull-up).
 *		XXX: shoulda' put it on PD3 which can do interrupts.
 *
 *	RXD (PD0) = MIDI serial in
 */


#include <avr/io.h>
#define F_CPU 			8000000UL	/* 8 Mhz cpu */
#include <util/delay.h>

#define MIDI_BAUD		31250
#define DIN_PULSE_WIDTH		3000
#define MIDI_CHANNEL		14

/* PORTB definitions. */
#define LED_BIT			0x01
#define DIN_CLK_BIT		0x02
#define DIN_START_BIT		0x04

/* PORTD definitions. */
#define BUTTON_BIT		0x10

void PORT_Init()
{
    /* Initialize PORTB (three outputs) */
    PORTB = 0;
    DDRB = (LED_BIT | DIN_CLK_BIT | DIN_START_BIT);

    /* Initialize PORTD (pull up button input) */
    PORTD = BUTTON_BIT;
    DDRD = 0;

    /* Initialize USART.
     * Set the baud to 31250, 8 data bits, one stop bit.
     */
    UBRRH = 0;
    UBRRL = (F_CPU/MIDI_BAUD/8)-1;
    UCSRA = (1<<U2X);
    UCSRB = (1<<RXEN);
    UCSRC = (3<<UCSZ0);
}

/* When the button is pushed, start the drum machine
 * and play for 2 bars at approximately 130 bpm.
 */
void audition(void)
{
    int i, led_ct;

    /* Everything off for 10ms */
    PORTB &= ~(LED_BIT | DIN_START_BIT | DIN_CLK_BIT);
    _delay_ms(10);

    /* Go. */
    PORTB |= (LED_BIT | DIN_START_BIT);
    _delay_ms(10);

    /* Play 2 bars of beats at about 130 bpm.
     * Repeat if button still pressed.
     */
    do {
	led_ct = 0;
	for (i=0; i<192; i++) {
	    PORTB |= DIN_CLK_BIT;
	    _delay_ms(4);
	    PORTB &= ~DIN_CLK_BIT;
	    _delay_ms(15);
	    if (++led_ct == 3) {
		led_ct = 0;
		PORTB ^= LED_BIT;
	    }
	}
    } while ((PIND & BUTTON_BIT) == 0);

    PORTB &= ~(LED_BIT | DIN_START_BIT);
}

int main(void)
{	
    int i, j, led_ct;
    uint8_t c;
    uint8_t running = 0;
    uint16_t clk_pulse = 0;

    PORT_Init();

    audition();

    for (;;) {
	/* Are we pulse stretching clock? */
	if (clk_pulse > 0) {
	    if (--clk_pulse == 0)
		PORTB &= ~DIN_CLK_BIT;
	}

	/* Poll input for MIDI bytes. */
	if ((UCSRA & (1<<RXC)) != 0) {

	    c = UDR;

	    if ((c & 0xf8) == 0xf8) {
		/* Real-time messages. */

		if (c == 0xf8) {
		    /* Timing clock message. */
		    PORTB |= DIN_CLK_BIT;
		    clk_pulse = DIN_PULSE_WIDTH;
		    if (running && ++led_ct > 2) {
			led_ct = 0;
			PORTB ^= LED_BIT;
		    }
		}
		else if (c == 0xfa) {
		    /* Real-time Start */
		    PORTB |= (LED_BIT | DIN_START_BIT);
		    led_ct = 0;
		}
		else if (c == 0xfc || c == 0xff) {
		    /* Real-time Stop or Reset. */
		    PORTB &= ~(LED_BIT | DIN_CLK_BIT | DIN_START_BIT);
		    clk_pulse = 0;
		    running = 0;
		}

	    }
	}

	if ((PIND & BUTTON_BIT) == 0) {
	    /* Pushbutton. */
	    audition();
	    clk_pulse = 0;
	    running = 0;
	}
    }

    return 0;
}

