/* Name: main.c
 * Project: 16-step 5-track drum pattern sequencer
 * Created by Naoki Iwakami on 28 Nov. 2008
 * Modified by Naoki Iwakami on 23 Mar. 2009 -- Add comments
 * License: GPL v2 or later.
 *
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>

/**
 * The target device is Atmel ATMega88.
 * ATMega48 works as well.
 *
 * TBD for the fuse bits.  You just enough to change from the device
 * initial setting as CKDIV8 unprogrammed and clock selectin
 * "external 20MHz".
 *
 */

//---------------------------------------------------------
// General definitions
//---------------------------------------------------------
#ifndef F_CPU
#define F_CPU    20000000	/*  20MHz processor */
#endif

//---------------------------------------------------------
// MIDI definitions and global variables
//---------------------------------------------------------
#define UART_BAUD_RATE 31250
#define UBRR ((F_CPU / (UART_BAUD_RATE * 16UL)) -1)

unsigned char note_table[] = {
    42, // open hi-hat
    46, // closed hi-hat
    45, // low tom
    38, // snare
    36
};

#define NOTE_CHANNEL 9 // for drum
#define NOTE_VELOCITY 0x70


//---------------------------------------------------------
// Sequencer definition and global variables
//---------------------------------------------------------
#define SEQUENCE_STEPS 16

unsigned char pattern_sequence[SEQUENCE_STEPS];
unsigned char last_input_state[SEQUENCE_STEPS];
unsigned char iscan;     // scanner index
unsigned char isequence; // sequencer index


//---------------------------------------------------------
// init functions
//---------------------------------------------------------

void pullup_read_ports();

/**
 * io_init()
 * Initialize the I/O ports
 */
void io_init()
{
	DDRD  = (1 << DDD0) // write bit 0
	      | (1 << DDD2) // scan bit0
          | (1 << DDD3) // scan bit1
          | (1 << DDD4) // scan bit2
          | (1 << DDD5) // scan bit3
          | (1 << DDD7); // write bit1

	DDRC  = (1 << DDC5); // write bit2
	//PORTD = 0x0;

    DDRB = (1 << DDB0) // write bit 3
	     | (1 << DDB1) // write bit 4
         | (1 << DDB2) // sequence bit3
         | (1 << DDB3) // sequence bit0
         | (1 << DDB4) // sequence bit1
         | (1 << DDB5); // sequence bit2
	PORTB = 0x00;
    pullup_read_ports();
}

/**
 * timer_init()
 * Initialize timers
 * timer0 -- used for the scanner timing control
 * timer1 -- used for the sequencer timing control
 */
void timer_init()
{
    // initializing timer0, used for the scanner
    TCCR0A = 0x0;
    TCCR0B = (1 << CS02); // timer enabled, clock/256
    TCNT0 = 0;
    TIMSK0 = (1 << OCIE0A); // Output Compare  Match A Interrupt enabled
    TIFR0 = (1 << OCF0A);
    OCR0A = 64;

    iscan = 0; // initialize the scanner index


    TCCR1A = 0x0;
    OCR1A = 4882; // 250ms
    TCNT1 = 0x0;
    TIMSK1 = (1 << OCIE1A); // Output Compare Match A Interrupt enabled
    TIFR1 = (1 << OCF1A);
    TCCR1B = (1 << CS12) | (1 << CS10); // timer enabled, clk/1024

    isequence = 0;
}

/**
 * usart_init()
 * Initialize the MIDI serial interface
 */
void usart_init(void)
	/* initialize uart */
{
    uint16_t ubrr;

    /* set baud rate */
    ubrr = 39;
    UBRR0H = (unsigned char)(ubrr >> 8);
    UBRR0L = (unsigned char)(ubrr);

	/* enable TxD */
    UCSR0B = 1 << TXEN0;
}

/**
 * sendbyte()
 * Send a single byte via serial output
 */
void sendbyte(unsigned char byte)
{
	while (!(UCSR0A & (1<<UDRE0)));
	    UDR0 = byte;
}

#if 0 // not used
/**
 * send_all_ch()
 * Send a MIDI message data to all channel
 */
void send_all_ch(unsigned char data)
{
	unsigned char i;
	for (i=0; i<16; i++) {
		sendbyte(0xB0+i);
		sendbyte(data);
		sendbyte(0x00);
	}
}

/**
 * send_all_off()
 * send ALL OFF message
 */
void send_all_off(void)
{
	send_all_ch(0x7B);
	send_all_ch(0x79);
}
#endif // not used

/**
 * note_on()
 * send a NOTE ON MIDI message via serial output
 */
void note_on(unsigned char ch, unsigned char note, unsigned char velo)
{
	sendbyte(0x90+ch);
	sendbyte(note);
	sendbyte(velo);
}

/**
 * note_off()
 * send a NOTE OFF MIDI message via serial output
 */
void note_off(unsigned char ch, unsigned char note) {
	sendbyte(0x80+ch);
	sendbyte(note);
	sendbyte(0x00);
}

/**
 * set_sequence()
 * sequence number -> I/O pins mapping
 */
#define SEQUENCE_PORT PORTB

#define SEQUENCE0_BIT DDB3
#define SEQUENCE1_BIT DDB4
#define SEQUENCE2_BIT DDB5
#define SEQUENCE3_BIT DDB2

void set_sequence(unsigned char step)
{
	unsigned char mask = 0x0f << DDB2;
 	unsigned char current = PORTB & ~mask;
  	PORTB = current | ((step & 0x07) << DDB3) | (((step >> 3) & 0x01) << DDB2);
}

/**
 * write_pattern()
 * write a bit pattern to the LED display (for a single step)
 */
void write_pattern(unsigned char pattern)
{
 	unsigned char current;
 	pattern &= 0x1f;

  	// port D
	current = PORTD & ~((1 << DDD0) | (1 << DDD7));
	PORTD = current | ((pattern & 0x01) << DDD0) | (((pattern >> 1) & 0x01) << DDD7);

    // port C
	current = PORTC & ~(1 << DDC5);
	PORTC = current | (((pattern >> 2) & 0x01) << DDC5);

	// port B
	current = PORTB & ~((1 << DDB0) | (1 << DDB1));
	PORTB = current | (((pattern >> 3) & 0x01) << DDB0) | (((pattern >> 4) & 0x01) << DDB1);
}

/**
 * set_scan()
 * scanner index -> scanner address pins mapping
 */
#define SCAN0_PORT PORTD
#define SCAN1_PORT PORTD
#define SCAN2_PORT PORTD
#define SCAN3_PORT PORTD

#define SCAN0_BIT DDD2
#define SCAN1_BIT DDD3
#define SCAN2_BIT DDD4
#define SCAN3_BIT DDD5

void set_scan(unsigned char step)
{
  unsigned char current = PORTD & ~(0x0f << SCAN0_BIT);
  PORTD = current | (step << SCAN0_BIT);
}

/**
 * read_pattern()
 * read a bit pattern from the switch array (by a single step)
 */ 
#define READ_PORT PORTC
#define READ_PIN PINC

#define READ0_BIT DDC0
#define READ1_BIT DDC1
#define READ2_BIT DDC2
#define READ3_BIT DDC3
#define READ4_BIT DDC4

void read_pattern(unsigned char* pattern)
{
  *pattern = ~(READ_PIN >> READ0_BIT) & 0x1f;
}

/**
 * pullup_read_ports()
 * Pulling up read ports.  Used for I/O initialization
 * We need this operation since the read pins do not have external pull-up register.
 */
void pullup_read_ports()
{
    READ_PORT |= (1 << READ0_BIT) | (1 << READ1_BIT) | (1 << READ2_BIT) | (1 << READ3_BIT) | (1 << READ4_BIT);
}

/**
 * update_pattern()
 * Check sequence switchs for any changes.
 * If any, toggle the corresponding pattern elemen on onset.
 */
void update_pattern(unsigned char input_new)
{
    unsigned char input_current = last_input_state[iscan];
    int ibit;
    char mask;
    char pattern_current;

    // do nothing on no change
    if (input_new == input_current) {
        return;
    }

    // toggle sequence pattern on set input
    pattern_current = pattern_sequence[iscan];
    for (ibit=0; ibit<8; ibit++) {
        mask = 1 << ibit;
        if ((input_new & mask) && ! (input_current & mask)) {
            pattern_current ^= mask;
        }
    }

    // update states
    pattern_sequence[iscan] = pattern_current;
    last_input_state[iscan] = input_new;
}

/**
 * The TIMER0 interrupt handler
 * The TIMER0 is used for sequence pattern scanner clock.
 * The scanner runs along steps axis, i.e. zero to 15.
 * The five tracks are read/written in parallel.
 */
ISR(TIMER0_COMPA_vect)
{
    volatile int j=0;
    int n;
    unsigned char thisPattern;

    /* reset the counter */
    TCNT0 = 0x0;

    /* increment the scanner index */
    if (++iscan == SEQUENCE_STEPS) {
        iscan = 0;
    }

    /* turn the light off before we roll the scanner switch over */
    write_pattern(0);

    /* update the address for the scanner switch */
    set_scan(iscan);

    /* wait a while for the scanner switch rolling over */
    for (n=0; n<64; n++) {
        j++;
    }

    read_pattern(&thisPattern);
    update_pattern(thisPattern);

    write_pattern(pattern_sequence[iscan]);

}

/**
 * send midi notes of the current pattern.
 */
void send_midi()
{
    unsigned char ibit;
    unsigned char pattern;

    // send stop notes for currently-enabled notes.
    pattern = pattern_sequence[(isequence-1)%SEQUENCE_STEPS];
    for (ibit=0; ibit<5; ibit++) {
        if (pattern & (0x01 << ibit)) {
            note_off(NOTE_CHANNEL, note_table[ibit]);
        }
    }

    // start notes of the current pattern
    pattern = pattern_sequence[isequence];
    for (ibit=0; ibit<5; ibit++) {
        if (pattern & (0x01 << ibit)) {
            note_on(NOTE_CHANNEL, note_table[ibit], NOTE_VELOCITY);
        }
    }
}

/**
 * The TIMER1 interrupt handler.
 * The TIMER1 is used for sequence pattern clock.
 * This interrupt handler updates the sequence counter and sequence indicator.
 * Then send out MIDI messages according to the current pattern.
 */
ISR(TIMER1_COMPA_vect)
{
    TCNT1 = 0x0;

    if (++isequence == SEQUENCE_STEPS) {
        isequence = 0;
    
    }
    set_sequence(isequence);

    send_midi();
}

/**
 * The main routine
 */
int main()
{
    io_init();
	usart_init();
    timer_init();

    sei();

    while (1) {
        sei();
        sleep_mode();
	}
}
