Taiko Trainer

Teaching and feedback device for drumming students

Full instructions provided1,396
Taiko Trainer

Things used in this project

Hardware components

47 nanoF Capacitor
×1
470 nanoF Capacitor
×1
Push buttons
×3
Seven Segment Display
×1
NPN (2N3904) Transistors
×2
Piezzo Speaker Sensors
×3
LED (generic)
LED (generic)
×8
1 MOhm Resistor
×1
10 MOhm Resistor
×1
100 kOhm Resistor
×2
1 kOhm Resistor
×1
4.3 kOhm Resistor
×1
1.5 kOhm Resistors
×2
330 Ohm Resistors
×22
100 Ohm Resistor
×1
68 Ohm Resistor
×1
Schottky Diodes
×4
Zener Diodes
×2
100 microF Capacitor
×1
Seven Segment Display Driver
×1
LM358 Op Amp chip
×1
IR LEDs
×2
IR (TSOP34156) Receiver
×1
Switch - DIP4
×1
Piezzo rubber
×1
Clamps
×2
Velcro
×1
Foam
×1
ATmega1284p Microcontroller
×1
Target Board
×1
Target Board Pins
×36
9V battery (generic)
9V battery (generic)
×1

Story

Read more

Code

main.c

C/C++
main.c
/*
File: main.c
Date: November 28th, 2012

Description: This file implements the Japanese Taiko Drum Sensei.
-----------
See Bruce Land's "CORNELL ECE 4760" Website for description of project:
< http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/ >

Authors:
--------
	Adam Harris <awh49>
	Adam Jelfo <asj42>
	Lucas Nissenbaum <ln226>
	Gabriel Soares <gs368>
*/

#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdlib.h> 
#include <string.h>
#include <math.h> 
#define F_CPU 16000000UL
#include <util/delay.h>  
#include <avr/sleep.h>

//Defines for scopes
#define begin {
#define end   }

//Boolean variable
typedef unsigned char bool;
#define TRUE 1
#define FALSE 0


#define SAMPLE_TIME 4000		// @16MHz, 4000 cycles -> 4Khz (1 sample per 250us)
#define ADC_BUFFER_SIZE 5000	// 4000 samples at 4KHz = 1sec 
#define BEAT_TOTAL 1000			// Maximum number of beats that are saved

// Analog-to-Digital converter thresholds
// To prevent noise, BEAT_UP_THRESH > BEAT_DOWN_THRESH
#define BEAT_UP_THRESH 20		// 51 == 0.6V on Oscill

#define BEAT_DOWN_THRESH 25		//parameter used to set fraction of peak height
#define N_DOWN_SAMPLES 50		// parameter used to prevent double triggers -- use 50 for most drums

// Beat classification parameters
#define BEAT_LOW_THRESH 110
#define N_RIM_THRESH 100  // 140

// Define threshold for each time comparison in 0.25 ms units
#define TORTOISE_THRESH 800
#define SLOTH_THRESH 80
#define HORSE_THRESH -80
#define CHEETAH_THRESH -800

// Define threshold for last beat in repeat-after-me
#define LAST_BEAT_THRESH_REPEAT_AFTER_ME (3*TORTOISE_THRESH)

// Define LED port
#define LED_PORT PORTC

// Define pin relative to each beat type
#define LED_SOFTSHOT 0x01
#define LED_HARDSHOT 0x02
#define LED_RIMSHOT  0x04

// Define pins for time comparison
#define LED_TORTOISE	 0x08
#define LED_SLOTH		 0x10
#define LED_BEAR		 0x20
#define LED_HORSE		 0x40
#define LED_CHEETAH		 0x80

// Define pin relative to miss beat
#define LED_MISS_BEAT 0xD8

// Define pin relative to start metronome mode
#define LED_METRONOME 0x20

// Define pin relative to wrong beat type of repeat-after-me
#define LED_WRONG_TYPE_REPEAT_AFTER_ME 0x02
#define LED_RIGHT_TYPE_REPEAT_AFTER_ME 0x01

// Define pin relative to cycle of repeat-after-me
#define LED_CYCLE_REPEAT_AFTER_ME 0x04

// Define pin relative to reference of repeat-after-me
#define LED_REFERENCE_REPEAT_AFTER_ME 0x20

// Define pin relative to initialization of repeat-after-me
#define LED_INITIALIZATION_REPEAT_AFTER_ME 0x20

// Define pin relative to start calibration
#define LED_INITIALIZATION_CALIBRATE 0x08

// Define pin relative to first beat OK
#define LED_FIRST_CALIBRATE 0x10

// Define pin relative to second beat OK
#define LED_SECOND_CALIBRATE 0x80

// Define pin relative to third beat OK
#define LED_THIRD_CALIBRATE 0x40

// Define pin relative to fourth beat
#define LED_FOURTH_CALIBRATE 0x20

// Define pin relative to LED leader follow-the-leader acknowledgement
#define LED_LEADER_FOLLOW_THE_LEADER 0x18

// Define pin relative to LED player follow-the-leader acknowledgement
#define LED_PLAYER_FOLLOW_THE_LEADER 0x20

// Define button port
#define BUTTON_PORT PINB

// Define bit relative to each button
#define BUTTON_ACTIVE_MODE 0x20
#define BUTTON_START_STOP 0x40
#define BUTTON_RESET 0x80

// Define button check flag
#define BUTTON_CHECK (BUTTON_ACTIVE_MODE|BUTTON_START_STOP|BUTTON_RESET)

// Define dip-switch port
#define DIP_PORT PINA

// Define bit relative to dip switches
#define BUTTON_DIP_SWITCHES 0xF0

// Metronome light LED length
#define METRONOME_LED_DURATION 200 // Turn on LED for 50 ms

// Define port for 7 segment display
#define SEGMENT_PORT PORTD

// Metronome default period
#define METRONOME_DEFAULT_PERIOD 20000UL

// Calibrate peak factor macro
#define CALIBRATE_PEAK_FACTOR(value) (value*4)/3

// Calibrate rim threshold macro
#define CALIBRATE_N_RIM_THRESH(value) (value*5)/4

// Define IR pin
#define IRPIN 0x02

// Follow the leader duration
#define FOLLOW_THE_LEADER_WAIT_DURATION 2000

// Follow the leader infrared debounce
#define FOLLOW_THE_LEADER_IR_DEBOUNCE 800	// 200 ms

// Beat type enumeration
enum _beat_type_
{
	inactive,
	rimshot, 		// represented by yellow LED, port B.0
	softbeat,		// represented by green LED, port B.2
	hardbeat,		// represented by red LED, port B.1
	received		// represented by reception of a signal
};

// Beat structure
typedef struct _beat_
{
	// Time of the beat
	long time; 
	// Time of other beat
	long compare_time;
	// Type of the beat
	enum _beat_type_ type; 
	// Type of other beat
	enum _beat_type_ compare_type;
} beat;

//Analog-to-Digital Converter Buffer
//	- weak filter used to measure length of beat
//	- strong filter used to detect beat
volatile unsigned char weak_filter_ADC_samples[ADC_BUFFER_SIZE];
volatile unsigned int  index_ADC_samples = 0;
volatile unsigned char strong_filter_ADC_sample;

// Used to detect duration of samples
volatile unsigned char  peak_signal = 0;
volatile unsigned int  max_low_samples = N_DOWN_SAMPLES;
volatile unsigned int  current_low_samples = 0;
volatile unsigned char low_thresh = BEAT_DOWN_THRESH;

// Global time
volatile unsigned long  global_time = 0;

// Beat parameter
volatile beat beat_vector[BEAT_TOTAL];
volatile unsigned int beat_index = 0;

// Used to signal when we are within a beat
volatile char sample_flag = 0;	
volatile enum _sample_sm_
{
	off_beat,
	on_beat
} sample_state;	// indicates state of sampling state machine

// Mode variable
volatile enum _active_mode_
{
	calibrate_mode,
	follow_the_leader_mode,
	metronome_mode,
	repeat_after_me_mode,
	free_beat_mode
} active_mode;

// Debounce state machine
volatile enum _debounce_sm_
{
	not_pressed_button,
	pressed_debounce_button,
	pressed_button,
	not_pressed_debounce_button
} button_state;

// Last pressed button
volatile unsigned char last_pressed_button;

// Change mode variable
volatile bool changed_mode = FALSE;

// Button activity flag
volatile bool button_activity_flag = FALSE;

// Jump beat flag
volatile bool jump_beat_flag = FALSE;

// External interrupt flag
volatile bool external_interrupt_flag = FALSE;

// Reset flag
volatile bool reset_flag = FALSE;

// Metronome state machine
enum _metronome_sm_
{
	wait_metronome,
	average_metronome,
	active_metronome
} metronome_state;

// Metronome variables
volatile unsigned long metronome_period = METRONOME_DEFAULT_PERIOD; // Period of the metronome
bool metronome_start_flag; 					   			   			// Metronome start/stop flag variable
volatile unsigned long metronome_len = 0;						   	// Measures number of "already active" samples

// Repeat-after-me state machine
enum _repeat_after_me_sm_
{
	set_repeat_after_me,
	wait_repeat_after_me,
	repeat_repeat_after_me
} repeat_after_me_state;

// Repeat-after-me variables
bool beat_type_flag;
unsigned int repeat_after_me_len;

// Calibrate variables
unsigned char calibrate_peak;

// Calibration threshold variables
unsigned char beat_low_thresh = BEAT_LOW_THRESH;
unsigned char n_rim_thresh = N_RIM_THRESH;

// Board ID
unsigned char board_id;

// Follow-the-leader state machine
enum _follow_the_leader_sm_
{
	initial_follow_the_leader,
	wait_leader_follow_the_leader,
	wait_player_follow_the_leader
} follow_the_leader_state;


// Follow-the-leader event flags
volatile bool follow_the_leader_receiver_beat;

// Follow-the-leader leader beat index
unsigned int follow_the_leader_leader_beat_index;

// Receiver sample state
volatile unsigned char previous_receiver_sample;

/*******************************************************
FUNCTION DECLARATIONS -- All calls for functions below
*******************************************************/

void display_segment_id (void);
void display_segment_mode (void);
void press_button (unsigned char button_input);
void display_delay (long time_difference);
void display_beat (int to_index);
void calibrate (void);
bool get_beat (bool ir_flag);
unsigned long calculate_average_time (void);
void metronome_beat();
void compare_and_display();
void repeat_after_me_beat ();
void free_beat();
void read_dip_switches();
void follow_the_leader_leader_beat();
void follow_the_leader_player_beat();
void initialize (void);


/********************************************************
Function to display board id in 7 segment display
********************************************************/

void display_segment_id ()
begin
	// Clear display
	SEGMENT_PORT &= 0x87;
	// Set up appropriate pins
	SEGMENT_PORT |= (board_id<<3);
end

/********************************************************
Function to display current mode in 7 segment display
********************************************************/

void display_segment_mode ()
begin
	// Clear display
	SEGMENT_PORT &= 0x87;
	// Set up appropriate pins
	SEGMENT_PORT |= (active_mode)<<3;
end

/********************************************************
Function to detect pressed button and act upon it
*********************************************************/

void press_button (unsigned char button_input)
begin
	last_pressed_button = button_input;
	button_activity_flag = TRUE;
	// Check mode input
	if((button_input&BUTTON_ACTIVE_MODE) != 0)
	begin
		changed_mode = TRUE;
		if(active_mode != free_beat_mode)
			active_mode++;
		else
			active_mode = calibrate_mode;
		display_segment_mode();
	end

	// Check start input
	if((button_input&BUTTON_START_STOP) != 0)
	begin
		// Each mode should deal with start stop as it wishes to
	end

	if((button_input&BUTTON_RESET) != 0)
		reset_flag = TRUE;
		
end

/********************************************************
Function to display time difference
********************************************************/

void display_delay (long time_difference)
begin
	// Check for time difference
	if(time_difference > TORTOISE_THRESH)
		LED_PORT |= LED_TORTOISE;
	else if(time_difference > SLOTH_THRESH)
		LED_PORT |= LED_SLOTH;
	else if(time_difference < CHEETAH_THRESH)
		LED_PORT |= LED_CHEETAH;
	else if(time_difference < HORSE_THRESH)
		LED_PORT |= LED_HORSE;
	else if(active_mode == follow_the_leader_mode)
		LED_PORT = LED_BEAR;
end

/*********************************************************
ISR samples the ADC at a constant rate set by SAMPLE_TIME
**********************************************************/

ISR (TIMER1_COMPA_vect)
begin
	// Update timestamp
	global_time++;

	// Check if time is multiple of 8ms
	if((global_time&(0x0000001F)) == 0)
	begin
		// If yes, then check button and run button state machine
		// Get pin B.0
		unsigned char button_input = (BUTTON_PORT)&BUTTON_CHECK;

		// Run button debounce state machine
		switch(button_state)
		begin
		case not_pressed_button:
			if(button_input != 0)
				button_state = pressed_debounce_button;				
			break;
		case pressed_debounce_button:
			if(button_input != 0)
			begin
				button_state = pressed_button;
				press_button(button_input);
			end
			else
				button_state = not_pressed_button;
			break;
		case pressed_button:
			if(button_input == 0)
				button_state = not_pressed_debounce_button;
			break;
		case not_pressed_debounce_button:
			if(button_input == 0)
				button_state = not_pressed_button;
			else
				button_state = pressed_button;
			break;
		default:
			button_state = not_pressed_button;
			break;
		end
	end

	// Check if it is in active metronome mode
	if((active_mode == metronome_mode)&&(metronome_state == active_metronome))
	begin
		// If it is, blink every metronome_period seconds
		if(metronome_len == 0)
			LED_PORT |= LED_METRONOME;

		if(metronome_len == METRONOME_LED_DURATION)
			LED_PORT &= ~(LED_METRONOME);
		
		metronome_len++;
		if(metronome_len == metronome_period)
			metronome_len = 0;
	end

	// Check if time for beat was reached for repeat-after-me
	if((active_mode == repeat_after_me_mode)&&(repeat_after_me_state == repeat_repeat_after_me)&&(sample_state == off_beat))
	begin
		signed long current_reference_time = global_time - beat_vector[0].time;
			
		// Check if it is time to turn on reference LED
		if(current_reference_time == beat_vector[beat_index].compare_time + HORSE_THRESH)
			LED_PORT |= LED_REFERENCE_REPEAT_AFTER_ME;
		else if(current_reference_time == beat_vector[beat_index].compare_time)
			LED_PORT &= ~LED_REFERENCE_REPEAT_AFTER_ME;
			
		if(beat_index < repeat_after_me_len - 1)
		begin
			// Check if user has "missed" the beat
			if(current_reference_time == (beat_vector[beat_index].compare_time + beat_vector[beat_index+1].compare_time)/2)
			begin
				jump_beat_flag = TRUE;
				beat_vector[beat_index].type = inactive;
				beat_vector[beat_index].time = 0;
				beat_index++;
			end
		end
		else
		begin
			// Check if user "missed" last beat
			if(current_reference_time == (beat_vector[beat_index].compare_time + LAST_BEAT_THRESH_REPEAT_AFTER_ME))
			begin
				jump_beat_flag = TRUE;
				beat_vector[beat_index].type = inactive;
				beat_vector[beat_index].time = 0;
				beat_index++;
			end
		end
	end

	if((active_mode == follow_the_leader_mode) && (board_id != 0))
	begin
		// Sample pin 
		unsigned char current_receiver_sample = (PIND&0x01);
		
		// Check if current sample is transition
		if((current_receiver_sample == 0) && (previous_receiver_sample == 1))
		begin
			// Check if previous transition was less than 100 ms before to debounce IR
			if((follow_the_leader_leader_beat_index != 0)&&(global_time - beat_vector[follow_the_leader_leader_beat_index - 1].compare_time <= FOLLOW_THE_LEADER_IR_DEBOUNCE))
				;
			else
			begin
				// There was IR reception, set new beat's time
				beat_vector[follow_the_leader_leader_beat_index].compare_time = global_time;
	
				// Set type to active
				beat_vector[follow_the_leader_leader_beat_index].compare_type = received;

				// Increment index
				follow_the_leader_leader_beat_index++;

				// Activate external interrupt flag
				external_interrupt_flag = TRUE;
			end
		end

		previous_receiver_sample = current_receiver_sample;
	end

	// Check if time for beat was reached on leader for follow-the-leader
	if((active_mode == follow_the_leader_mode) && (board_id != 0) && (follow_the_leader_state == wait_leader_follow_the_leader) && (sample_state == off_beat))
	begin
		// Check if time is bigger than strict timeout and user did an extra beat
		signed long current_reference_time = global_time - beat_vector[follow_the_leader_leader_beat_index].time;
		
		// If reference time is too long, jump sample
		if(current_reference_time == FOLLOW_THE_LEADER_WAIT_DURATION)
		begin
			jump_beat_flag = TRUE;
			beat_vector[follow_the_leader_leader_beat_index].compare_type = inactive;
			beat_vector[follow_the_leader_leader_beat_index].compare_time = 0;
			follow_the_leader_leader_beat_index++;
		end
	end

	// Check if time for beat was reached on player for follow-the-leader
	if((active_mode == follow_the_leader_mode) && (board_id != 0) && (follow_the_leader_state == wait_player_follow_the_leader) && (sample_state == off_beat))
	begin
		// Check if time is bigger than strict timeout and user did an extra beat
		signed long current_reference_time = global_time - beat_vector[beat_index].compare_time;
		if(current_reference_time == FOLLOW_THE_LEADER_WAIT_DURATION)
		begin
			jump_beat_flag = TRUE;
			beat_vector[beat_index].type = inactive;
			beat_vector[beat_index].time = 0;
			beat_index++;
		end
	end
	

	// Check type of sample
	switch(sample_state)
	begin
	case on_beat:
		// In case of a weak filter sample
		
		// Sample the ADC from weak filter and store to vector
		weak_filter_ADC_samples[index_ADC_samples] = ADCH;
		ADCSRA |= (1<<ADSC);	

		// Check if peak was found

		if(weak_filter_ADC_samples[index_ADC_samples] > peak_signal)
		begin
			// If it is peak, reset peak value
			peak_signal = weak_filter_ADC_samples[index_ADC_samples];
			current_low_samples = 0;
		end
		else
		begin
			// If not peak, check if below threshold
			if(weak_filter_ADC_samples[index_ADC_samples] < low_thresh)
			begin
				current_low_samples++;
				if(current_low_samples > max_low_samples)
				begin
					// Beat is over.
					// First, classify beat
					if(peak_signal < beat_low_thresh)
						beat_vector[beat_index++].type = softbeat;
					else if(index_ADC_samples < n_rim_thresh)
						beat_vector[beat_index++].type = rimshot;
					else
						beat_vector[beat_index++].type = hardbeat;

					// Second, set mode back to off
					sample_state = off_beat;

					// Third, change ADC sampling channel
					ADMUX ^= 0x01;

					// Reset variables
					peak_signal = 0;
					current_low_samples = 0;
					index_ADC_samples = 0;
				end
			end
			else
				// If not below threshold, reset low samples
				current_low_samples = 0;
		end
		
		index_ADC_samples++;
		break;

	case off_beat:

		//Sample the ADC and store to vector
		strong_filter_ADC_sample = ADCH;
		ADCSRA |= (1<<ADSC);	

		// Check if signal is on
		if(strong_filter_ADC_sample > BEAT_UP_THRESH)
		begin
			// If yes, save timestamp
			beat_vector[beat_index].time = global_time;

			// Change to weak filter mode
			sample_state = on_beat;

			// Change ADC sampling channel
			ADMUX ^= 0x01;
		end
	end
end

/******************************************************
Calibration function
********************************************************/
void calibrate ()
begin
	// Reset beat index
	beat_index = 0;

	// Signal initialization of calibrate and soft beat
	LED_PORT = LED_INITIALIZATION_CALIBRATE|LED_SOFTSHOT;

	// First, two soft beats and save their peaks
	calibrate_peak = 0;
	if(get_beat(FALSE) == FALSE)
		return;
	
	unsigned char first_peak_signal = calibrate_peak;
	
	// Signal first beat as ok
	LED_PORT = LED_FIRST_CALIBRATE|LED_SOFTSHOT;

	calibrate_peak = 0;
	if(get_beat(FALSE) == FALSE)
		return;


	unsigned char second_peak_signal = calibrate_peak;

	// Given two samples, treat peak as calibration
	beat_low_thresh = CALIBRATE_PEAK_FACTOR((((unsigned int)first_peak_signal + (unsigned int)second_peak_signal)/2));


	// Signal second beat is OK
	LED_PORT = LED_SECOND_CALIBRATE|LED_RIMSHOT;

	// Sample twice for rimshots
	if(get_beat(FALSE) == FALSE)
		return;

	unsigned long first_rim_thresh = global_time - beat_vector[beat_index-1].time;

	// Signal third beat is OK
	LED_PORT = LED_THIRD_CALIBRATE|LED_RIMSHOT;
	if(get_beat(FALSE) == FALSE)
		return;

	unsigned long second_rim_thresh = global_time - beat_vector[beat_index-1].time;

	// Given two samples, treat peak as calibration
	n_rim_thresh = CALIBRATE_N_RIM_THRESH((((unsigned int)first_rim_thresh + (unsigned int)second_rim_thresh)/2));

	LED_PORT = LED_FOURTH_CALIBRATE;

	// Set mode as free beat mode
	while(get_beat(FALSE) == TRUE);
end

/*******************************************************
Get beat function
*******************************************************/

bool get_beat(bool ir_flag)
begin
	button_activity_flag = FALSE;
	jump_beat_flag = FALSE;
	external_interrupt_flag = FALSE;

	// Wait for beat
	while(sample_state == off_beat)
	begin
		if(button_activity_flag == TRUE)
			return FALSE;
		if(jump_beat_flag == TRUE)
			return FALSE;
		if(external_interrupt_flag == TRUE)
			return FALSE;
	end

	// Clear LED's at beat detection
	LED_PORT &= ~(LED_RIMSHOT + LED_HARDSHOT + LED_SOFTSHOT);

	// Turn IR on to send beat information
	if(ir_flag == TRUE)
		PORTD |= IRPIN; 

	// Wait until beat capture is done
	while(sample_state == on_beat)
	begin
		// Get calibrate peak
		if(peak_signal > calibrate_peak)
			calibrate_peak = peak_signal;
	end

	// Turn off IR as soon as beat is over
	if(ir_flag == TRUE)
		PORTD &= ~IRPIN;

	return TRUE;
end

/*******************************************************
Display beat type
*******************************************************/

void display_beat (int to_index)
begin
	// Show classified beat
	switch(beat_vector[(to_index)].type)
	begin
	case rimshot:
		LED_PORT |= LED_RIMSHOT;
		break;
	case hardbeat:
		LED_PORT |= LED_HARDSHOT;
		break;
	case softbeat:
		LED_PORT |= LED_SOFTSHOT;
		break;
	default:
		LED_PORT |= LED_RIMSHOT + LED_HARDSHOT + LED_SOFTSHOT; // ERROR case
		break;
	end

end

/*************************************************
Average between samples if possible
***********************************************/
unsigned long calculate_average_time ()
begin
	// Check if any sample exists
	// If yes, set as average
	// Otherwise, set as 12 bpm or 0.2 bps
	if(beat_index > 1)
		return ((beat_vector[beat_index - 1].time - beat_vector[0].time)/(beat_index-1));
	else
		return METRONOME_DEFAULT_PERIOD;
end

/****************************************************
Function to play metronome mode
***************************************************/

void metronome_beat()
begin
	// Check current state
	switch(metronome_state)
	begin
	case wait_metronome:
		// For testing purposes, turn on LED indicating waiting mode
		LED_PORT = 0x08;
		// Set to get first beat
		beat_index = 0;
		// Waits until first beat
		if(get_beat(FALSE) == FALSE)
		begin
			if(last_pressed_button != BUTTON_START_STOP)
			begin
				LED_PORT = 0x00;
				return;
			end
			else
			begin
				// If it is a start stop button, check new state
				metronome_period = calculate_average_time();
				metronome_state = active_metronome;
				metronome_len = 0;
			end
		end
		else
			metronome_state = average_metronome;

		break;
	case average_metronome:
		LED_PORT = 0x10;
		if(get_beat(FALSE) == FALSE)
		begin
			//If there is a button press, check the kind of press
			if(last_pressed_button != BUTTON_START_STOP)
			begin
				metronome_state = wait_metronome;
				beat_index = 0;
				LED_PORT = 0x00;
				return;
			end
			else
			begin
				// If it is a start stop button, check new state
				metronome_period = calculate_average_time();
				metronome_state = active_metronome;
				metronome_len = 0;
			end
		end
		break;
	case active_metronome:
		LED_PORT = 0x40;
		// Get beats until button press
		while(get_beat(FALSE) == TRUE)
			display_beat(beat_index - 1);
		
		metronome_state = wait_metronome;
		metronome_period = 0;
		beat_index = 0;
		LED_PORT = 0x00;
		break;
	end
end

/********************************************************
Compare and display
********************************************************/

void compare_and_display()
begin
	// Check if beat is available
	if((beat_vector[beat_index - 1].type == inactive)||(beat_vector[beat_index - 1].compare_type == inactive))
	begin
		LED_PORT = LED_MISS_BEAT;
		return;
	end 

	// Check beat type
	if((beat_vector[beat_index - 1].type == beat_vector[beat_index - 1].compare_type)||(beat_vector[beat_index - 1].compare_type == received))
		LED_PORT = LED_RIGHT_TYPE_REPEAT_AFTER_ME;
	else
		LED_PORT = LED_WRONG_TYPE_REPEAT_AFTER_ME;
	
	if((active_mode == repeat_after_me_mode)&&(beat_index != 1))
	begin
		signed long time_difference = (signed long)beat_vector[beat_index - 1].time - (signed long)beat_vector[0].time;
		display_delay(-(time_difference - (signed long)beat_vector[beat_index - 1].compare_time));
	end

	if(active_mode == follow_the_leader_mode)
	begin
		display_delay((signed long)beat_vector[beat_index - 1].compare_time -(signed long)beat_vector[beat_index - 1].time);
	end

end

/********************************************************
Repeat after me mode
********************************************************/

void repeat_after_me_beat ()
begin
	// Check current repeat_after_me_state
	switch(repeat_after_me_state)
	begin
	case set_repeat_after_me:
		// Reset beat index and settle mode
		beat_index = 0;

		LED_PORT = LED_INITIALIZATION_REPEAT_AFTER_ME;

		// Wait for button
		while(get_beat(FALSE) == TRUE)
			display_beat(beat_index - 1);

		LED_PORT = 0;

		if(last_pressed_button != BUTTON_START_STOP)
			return;

		if(beat_index < 2)
			return;

		// Copy all information to compare variables
		int i = 0;
		for(; i < beat_index; i++)
		begin
			beat_vector[i].compare_time = beat_vector[i].time - beat_vector[0].time;
			beat_vector[i].compare_type = beat_vector[i].type;
		end
		
		repeat_after_me_len = beat_index;
		repeat_after_me_state = wait_repeat_after_me;	
		break;
	case wait_repeat_after_me:
		// Reset beat index to get first beat
		beat_index = 0;

		// Turn on LED indicating cycle
		LED_PORT |= LED_CYCLE_REPEAT_AFTER_ME;

		// Wait for beat
		if(get_beat(FALSE) == FALSE)
		begin
			// If button press, set state back to set_repeat_after_me
			repeat_after_me_state = set_repeat_after_me; 
			return;
		end
		else
		begin
			// Move to next state
			repeat_after_me_state = repeat_repeat_after_me;
			compare_and_display();
		end
		break;

	case repeat_repeat_after_me:
		while(beat_index < repeat_after_me_len)
		begin
			// Get beat
			if(get_beat(FALSE) == FALSE)
			begin
				// Check if button
				if(button_activity_flag == TRUE)
				begin
					repeat_after_me_state = set_repeat_after_me;
					return;	
				end
			end
			
			// Display beat even if missed
			compare_and_display();
		end
		repeat_after_me_state = wait_repeat_after_me;
		break;
	end
end

/*******************************************************
Execute free beat mode
*******************************************************/
void free_beat()
begin
	// Turn off LED's
	LED_PORT = 0;

	// Wait for beats and signal them
	while(get_beat(FALSE) == TRUE)
		display_beat(beat_index - 1);
end

/********************************************************
Read dip switches
*********************************************************/
void read_dip_switches()
begin
	// Check DIP switch locations
	unsigned char read_dips = (DIP_PORT&BUTTON_DIP_SWITCHES);

	//Following lines reverse the bit orders (due to way it is connected on board)g
	read_dips = (read_dips & 0x0F) << 4 | (read_dips & 0xF0) >> 4;
 	read_dips = (read_dips & 0x33) << 2 | (read_dips & 0xCC) >> 2;
 	read_dips = (read_dips & 0x55) << 1 | (read_dips & 0xAA) >> 1;

	board_id = read_dips;
end

/********************************************************
Follow the leader - leader board
********************************************************/
void follow_the_leader_leader_beat()
begin
	// First, reset beat index
	beat_index = 0;

	// Signal current user
	LED_PORT = LED_LEADER_FOLLOW_THE_LEADER;

	// While there is no button press
	while(get_beat(TRUE) == TRUE)
		display_beat(beat_index - 1);
end

/********************************************************
Follow the leader - player board
*********************************************************/
void follow_the_leader_player_beat ()
begin
...

This file has been truncated, please download it to see its full contents.

Credits

Adam Jelfo

Adam Jelfo

1 project • 1 follower
Gabriel Soares

Gabriel Soares

1 project • 2 followers
Lucas Nissenbaum

Lucas Nissenbaum

1 project • 1 follower
Adam Harris

Adam Harris

1 project • 1 follower

Comments