Erica BarnettMary Beth Galanko
Published

“Move Your Music” Song Conductor

An armband that controls the volume and speed of pre-recorded songs.

Full instructions provided892
“Move Your Music” Song Conductor

Things used in this project

Hardware components

Breadboard (generic)
Breadboard (generic)
×1
Computer speakers
×1
Large solder board
×1
Mega1284p
×1
Arm band: fabric, Velcro, etc
×1
Foam
×1
Cardboard
×1
Ceramic Capacitors 22µF
×12
Ceramic Capacitors 47µF
×10
Aluminum Capacitors 39µF
×12
Target Board
×1
9V Power Supply
×1

Story

Read more

Code

move_your_music.c

C/C++
move_your_music.c
//Final Project ECE 4760, Cornell University
//Erica Barnett, Mary Galanko 
//Mega644 version

#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>


//Clock frequency
#define F_CPU 16000000UL

#include <util/delay.h>
#include <stdio.h>
#include <math.h> // for sine


//Headers necessary for LCD operation
#include <avr/pgmspace.h>
#include <stdlib.h>
#include <string.h>
#include "uart.h"


// UART file descriptor
//putchar and getchar are in uart.c
FILE uart_str = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);

//I like these 
#define begin {
#define end   }

//Variable sound that holds sign wave samples
volatile  char sound;

// Variables used to compute the avearge sample for each person
volatile unsigned long total = 0;
volatile unsigned long total2 = 0;
unsigned char average, average2;


//State machine state names
#define Release 1 
#define Debounce 2
#define StillPushed 3
#define DebounceRelease 4
#define DetectTerm 5
#define DebounceTerm 6
#define StillTerm 7
#define Done 8

//the task subroutines

void statemachine(void);	//debounced button 1
void debounce(void);	//handles key press to stop playing
void initialize(void); //all the usual mcu stuff

//FM synthesis
volatile unsigned int acc_main1, acc_fm1 ;
volatile unsigned char high_main1, high_fm1 ;
volatile unsigned int inc_main1, inc_fm1;
volatile signed char fm1;

volatile unsigned int time1;	 //counts number of ms elapsed
volatile unsigned int lasttime1;	//holds a previous time1 value for comparision
volatile unsigned char ms;	 //countdown of 62.5kHz periods to 1 ms
volatile unsigned char lastms;	 //last countdown value used to generate millisecond 
volatile unsigned char	s_int;	 //syllable interval variable

volatile unsigned char level; //read from ADC
volatile unsigned int mod;	 //how much to change DAC output frequency

unsigned char PushState;	//state machine

unsigned char startFlag;	//=1 when processing a start press; =2 when song
//ready to start; =0 otherwise

unsigned char stopFlag;	 //=1 when processing a stop press; =0 otherwise

unsigned char maybe; //holds button press number for debouncing 

volatile unsigned char last_peak1 = 0; //variable used to save last peak value from Chan1
volatile unsigned char last_peak2 = 0; //variable used to save last peak value from Chan2
volatile unsigned char last_fall = 0; //variable used to save last fall value from Chan1
volatile unsigned char peak1 = 0; //variable used for number of peak detection from Chan1
volatile unsigned char peak2 = 0; //variable used for number of peak detection from Chan2
volatile unsigned char fall = 0; //variable used for number of fall detection
volatile unsigned char print1 = 0; //variable used to flag peak1
volatile unsigned char print2 = 0; //variable used to flag peak2
volatile unsigned char print3 = 0; //variable used to flag fall

// tables for DDS 
volatile signed char sineTable[256] ;

// index for sine table build
unsigned int i;

//ADC saving variables
volatile unsigned char chan;
volatile unsigned char count;
volatile unsigned char sample[4][256];// = sineTable * zero;
volatile unsigned char q; //index of ADC channel vector
volatile unsigned char first = 0;
volatile unsigned char scnd = 0;
volatile unsigned char upflag1 = 0; //1 if signal was incremented recently (peak detection flag) in Chan1
volatile unsigned char upflag2 = 0; //1 if signal was incremented recently (peak detection flag) in Chan2
volatile unsigned char downflag = 0; //1 if signal was incremented recently(fall detection flag) in Chan 1

/////Song variables
// The DDS variables
volatile unsigned char decay_fm1, decay_main, depth_fm1, rise_main ;
volatile unsigned int amp_main, amp_fm1 ;
volatile unsigned int rise_phase_main, amp_rise_main, amp_fall_main ;
#define max_amp 32767
unsigned char ch1_vol = 4;

// trigger
volatile char pluck;

// Time variables
// the volitile is needed because the time is only set in the ISR
// time counts mSec, sample counts DDS samples (62.5 KHz)
volatile unsigned int time, lasttime;

unsigned int beat = 1600; //should be about 2500 for The Way You Make Me Feel
unsigned int beats[10] ={800, 1000, 1200, 1400, 1600, 1800, 2000, 2400, 2800, 3200};
unsigned char b=0;
unsigned int beatct; //tracks ISR executions to count beat
float notes1[16] = {164.8, 164.8, 196, 246.9, 392, 329.6, 0, 329.6, 370, 0, 329.6, 293.7, 0 , 293.7, 0 , 246.9}; //notes to Beat It
float notes2[24]= {440, 0 , 440, 466.2, 0, 466.2, 523.3, 0 , 523.3, 440, 440, 0, 349.2, 0, 349.2, 392, 392, 0, 349.2, 0 , 349.2, 349.2, 0, 349.2};// notes for The Way You Make Me Feel
//float notes3[32]= {246.9, 293.7, 370, 370, 329.6, 370, 329.6, 370, 370, 293.7, 370, 370, 392, 370, 329.6, 329.6, 0, 293.7, 370, 370, 329.6, 370, 329.6, 370, 0, 370, 329.6, 370, 392, 370, 329.6, 0};// for You Rock My World
//float notes3[16]= {196, 146.8, 174.6, 196, 174.6, 146.8, 130.8, 146.8, 98, 146.8, 174.6, 196, 174.6, 146.8, 130.8, 146.8}; //notes to Billie Jean intro
float notes3[32]= {196, 196, 146.8, 0, 174.6, 0, 196, 196, 174.6, 0, 146.8, 0, 130.8, 0, 146.8, 0, 98, 98, 146.8, 0, 174.6, 0, 196, 196, 174.6, 0, 146.8, 0, 130.8, 0, 146.8, 0}; //notes to Billie Jean intro
float freq; //holds current frequency
float freq_last; // holds the last frequency
unsigned char j = 0; //holds notes1 vector index
unsigned char k = 0; //holds notes2 vector index
unsigned char l = 0; //holds notes3 vector index
unsigned char notesnum; //used to rotate between songs
unsigned char changed = 0; //tracks song change for single button push
unsigned char push; //hold read of pins

///// INFORMATION FOR UART PRINTING


int8_t sound_buffer[12];  //to print sound
int8_t level_buffer[12]; //DEBUG: print current state
/////


//**********************************************************
//timer 0
ISR (TIMER2_OVF_vect) //generates PWM and millisecond signals
begin

// Decide when to save ADC sample
if (count==10) {
if (chan==1) {
sample[1][q++] = ADCH; //saves channel 1 info at 100Hz

if (first<255) { //initial buffer at start
first++;

total = total + sample[1][q-1]; //using total for average


if(first == 255) {

average =  (int) total/256; //compute average ADC value and print, use as base case for Chan1
fprintf(stdout,"average1\n\r");
sprintf(level_buffer, "%i\r\n", average);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");
}
}


// Condition for falling signal passed a specified threshold
else if ((sample[1][q-2]>(average-10))&&(sample[1][q-1]<(average-10))){//&&(downflag==0)&&(upflag1==0)){
//last_fall = fall;
fall = fall + 1; //keep track of the number of falls
print3= 1;	 // set flag to be used in Main
downflag=1;	 //detection flag
}


// Condition for peaking signal passed a specified threshold
else if ((sample[1][q-2]<(average+12))&&(sample[1][q-1]>(average+12))){//&&(upflag1==0)&&(downflag==0)){
//last_peak1 = peak1;
peak1 = peak1 + 1; //keep track of the number of peaks
print1= 1;	   // set flag to be used in Main
upflag1=1;	   //detection flag
}


if ((sample[1][q-1]>(average))&&(downflag==1)) {
//Signal has returned to average; reset increase flag
downflag = 0;
}


if ((sample[1][q-1]<average)&&(upflag1==1)) {
//Signal has returned to average; reset increase flag
upflag1 = 0;
}


//Reset count variable - ONLY when ADC channel is 1
count = 0;
}
else if (chan==2) {
sample[2][q] = ADCH; //saves channel 2 info at 100Hz

if (scnd<255) { //initial buffer at start

scnd++;
total2 = total2 + sample[2][q]; //using total2 for average


if(scnd == 255) {
//sprintf(sound_buffer, "%ld\r\n", total);//For Debugging Purposes
//fprintf(stdout,sound_buffer);
//fprintf(stdout,"\n\r");
average2 =  (int) total2/256; //compute average2 ADC value and print, use as base case for Chan2
fprintf(stdout,"average2\n\r"); 
sprintf(level_buffer, "%i\r\n", average2);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");
}
}

// Condition for peaking signal passed a specified threshold
if ((sample[2][q-1]<(average2+15))&&(sample[2][q]>(average2+15))) {//&&(upflag2==0)){
//last_peak2 = peak2;
peak2 = peak2 + 1; //keep track of the number of peaks
print2= 1;	 // set flag to be used in Main
upflag2=1;	 //detection flag
}



if ((sample[2][q-1]<30) && (upflag2==1)) {
//Signal has significantly decreased; reset increase flag
upflag2 = 0;
}
}
}

else if (chan==1) {
count++;
}


////This block required for beat shaping
if ((time & 0x0ff) == 0) begin
//fprintf(stdout,"Change ampl\r\n"); //Condition is being entered 
amp_fall_main = amp_fall_main - (amp_fall_main>>decay_main) ;
rise_phase_main = rise_phase_main - (rise_phase_main>>rise_main);
// compute exponential decay of FM depth of modulation
amp_fm1 = amp_fm1 - (amp_fm1>>decay_fm1) ;
end
////////

// form (1-exp(-t/tau)) for the attack phase
amp_rise_main =  max_amp - rise_phase_main;
// product of rise and fall exponentials is the amplitude envelope
amp_main = (amp_rise_main>>8) * (amp_fall_main>>8) ;



////This block required for beat shaping////
// Init the synth
if (pluck==1) begin
amp_fall_main = max_amp; 
rise_phase_main = max_amp ;
amp_rise_main = 0 ;
amp_fm1 = max_amp ;
// phase lock the synth
acc_fm1 = 0 ;
acc_main1 = 0;
pluck = 0;
end
//////////

//the FM DDR -- feeds into final DDR
acc_fm1 = acc_fm1 + inc_fm1 ;
high_fm1 = (char)(acc_fm1 >> 8) ;
fm1 = sineTable[high_fm1] ;


//the final output DDR 
// phase accum = main_DDR_freq + FM_DDR * (FM amplitude)
//acc_main1 = acc_main1 + inc_main1 + ((fm1)) ;
acc_main1 = acc_main1 + (inc_main1 );//+ (fm1*(amp_fm1>>depth_fm1))) ;//depth_fm1
high_main1 = (char)(acc_main1 >> 8) ;


// output the wavefrom sample
// scale amplitude to use only high byte and shift into range
// 0 to 255
sound = 128 + (int) (((amp_main>>ch1_vol) * (int)sineTable[high_main1])>>6) ;

//sound = 128 + ((((amp_main>>8) * (int)sineTable[high_main1])>>6)) ;
//NOTE:  In previous DDS unit, had to remove shift by 7

PORTB = sound;

//Increment counter to change frequencies if needed
beatct++;

time++; //increment counter used for decay & main() timing

//Increment next channel
if (chan<7) chan++;
else if (chan>=7) chan = 0;

//Choose next ADC channel as input
if (chan==1) ADMUX = 0x61; //Leave previous REFS0 and ADLAR settings
if (chan==2) ADMUX = 0x62;
_delay_us(10); //wait to settle
//Check:  print ADMUX

//Trigger next ADC sample
//_delay_ms(5);
ADCSRA |= (1<<ADSC) ;

end 

//**********************************************************     
//Entry point and task scheduler loop
int main(void)
begin  
 initialize();

 //main task scheduler loop 
 while(1)
 begin 


////// Peak Detection

if (print1 == 1) {
/*fprintf(stdout,"Peak1\n\r");//For Debugging Purposes
sprintf(level_buffer, "%i\r\n", peak1);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");*/
//PORTC^=0x04;

//Detected a peak in Chan1
if (ch1_vol<8) { // increase the volume
ch1_vol= ch1_vol + 1;

//Print Peak1ch1_vol
fprintf(stdout,"Peak Volume ");
sprintf(level_buffer, "%i\r\n", ch1_vol);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");
}


print1= 0; //reset flag
}

if (print2 == 1){
/*fprintf(stdout,"Peak2\n\r"); //For Debugging Purposes
sprintf(level_buffer, "%i\r\n", peak2);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");*/

//When change detected, toggle port...
PORTC^=0x04;

//Detected a peak in Chan2--> change beat speed
if (b==9) {
b=0;
beat = beats[b];
}
else beat = beats[++b];

//Print Beat Time
fprintf(stdout,"Beat time ");
sprintf(level_buffer, "%i\r\n", beat);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");
print2= 0; //reset flag
}

if (print3 == 1) { 
/*fprintf(stdout,"Fall\n\r"); //For Debugging Purposes
sprintf(level_buffer, "%i\r\n", fall);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");*/

print3= 0;//reset flag

//Detected a fall in Chan1
if (ch1_vol>0) {
ch1_vol= ch1_vol - 1; //decrease volume

//Print Fallch1_vol
fprintf(stdout,"Fall Volume ");
sprintf(level_buffer, "%i\r\n", ch1_vol);
fprintf(stdout,level_buffer);
fprintf(stdout,"\n\r");

}

}


////// Changing Songs
if (beatct>=beat) {
beatct = 0; //reset count
// Find new frequency

//Song: Beat It
if (notesnum == 0){
if (j==15) { //reached end of notes - go back to 0
j = 0;
freq_last = freq; //remember last freq
freq = notes1[j];
} 
else {
freq_last = freq; //remember last freq
freq = notes1[++j]; //move to next note
}
}

//Song: The Way You Making Me Feel
else if (notesnum == 1) {
if (k==23) { //reached end of notes - go back to 0
k = 0;
freq_last = freq; //remember last freq
freq = notes2[k];
} 
else {
freq_last = freq;//remember last freq
freq = notes2[++k]; // move to next note
} 
}


//Song: Billie Jean
else if (notesnum == 2){
if (l==31 ) { //reached end of notes - go back to 0
l = 0;
freq_last = freq; //remember last freq
freq = notes3[l];
} 
else {
freq_last = freq; //remember last freq
freq = notes3[++l]; // move to next note

}
}
/*else if ((beatct>=(beat/2))&&(notesnum==2)) {

if (l==31 ) { //reached end of notes - go back to 0
l = 0;
freq_last = freq; //remember last freq
freq = notes3[l];
} 
else {
freq_last = freq; //remember last freq
freq = notes3[++l]; // move to next note

}*/

// Calculate increment, etc. of new frequency
inc_main1 = (int)(8.389 * freq);//freq) ; 
//_delay_ms(200); //Attempted to replace print statements with delay
fprintf(stdout," "); //print for time delay
fprintf(stdout,"                   \r\n");
if (freq_last!=freq) pluck = 1;
}

////// Sense pushbutton press, debounce, and switch song
if ((time-lasttime)>=250) {
time = lasttime;
push = PINC;
debounce(); //Run state machine ~32x/sec
}

 end  
end ///****************!!

//*******************************   
//Debouncing stop button 
void debounce(void) 
begin

 switch (PushState)
 begin
    case Release:

if ((push&0x02) == 0x00) PushState = Debounce; //Pin C1 is pulled down
else PushState=Release;
break;
case Debounce:
      if ((push&0x02) == 0x00) { //was push==249
  	 fprintf(stdout,"button\r\n");
PushState=StillPushed;
}
       else PushState=Debounce;
       break;
    case StillPushed:  

       if ((push&0x02) == 0x00)
begin
PushState=StillPushed;
// Change song
if ((notesnum==0)&&(changed==0)) {
j = 0;
notesnum = 1;
}
else if ((notesnum==1)&&(changed==0)) {
k = 0;
notesnum = 2;
}
else if ((notesnum==2)&&(changed==0)) {
l = 0;
notesnum = 0;
}
changed = 1; //indicates song has been changed for this push
end
       else PushState=DebounceRelease; 
       break;
    case DebounceRelease:

       if ((push&0x02 == 0x00)) PushState=StillPushed; 
       else 
       begin
          PushState=Release;
  changed = 0;//clear flag for next button push
       end    
       break;
   end
end

//********************************************************** 
//Set it all up
void initialize(void)
begin
//set up the ports
 	DDRB = 0xff;	// PORT B is the output to DAC


TIMSK2= (1<<TOIE2);	//turn on timer 2 overflow ISR 
  	TCCR2B= (1<<CS21); //Timer 2 has prescalar of 8
// question: need to time ADC sample differently from DAC samples?

//Set up ADC
 	ADMUX |= ((1<<ADLAR) | (1<<REFS0)| 1); //(1<<1)); //Left adjust result; ADC1 AS INPUT
chan = 2;
//ADMUX &= 0xfe;  //ADC0 AS INPUT
 	ADCSRA = ((1<<ADEN) | (1<< ADSC) |(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)); //enable ADC; divide clock by 



DDRC=0x05;	// PORT C.0 and C.2 is an output; all others can stay inputs
PORTC=0x03;// Enable Port C.1 pull-up resistor

 	//init the UART -- uart_init() is in uart.c
 	uart_init();
 	stdout = stdin = stderr = &uart_str;
 	fprintf(stdout,"Starting timers...\n\r");

 	notesnum = 0; //Begin with song 1
freq = notes1[0]; //Initialize Freq to first note in song vector
pluck = 1;

// 2^16/(7812.5Hz)*freq = 8.389*freq
inc_main1 = (int)(8.389 * freq) ; //previously, for exactly 8 kHz:  (int)(8.192*freq);

//Bowed string
  decay_main = 5 ;
  rise_main = 4 ;
  inc_fm1 = (int)(8.389 * 300) ;
  depth_fm1 = 8 ;
  decay_fm1 = 6 ; 
//initial instructions
fprintf(stdout,"Welcome to Move Your Music!\n\r");
fprintf(stdout,"With this tool, You will be able to manipulate sound with arm movements\n\r");
fprintf(stdout,"To increase Volume, Thurst Hand Towards Body \n\r");
fprintf(stdout,"To Decrease Volume, Thurst Hand Towards Floor \n\r");
fprintf(stdout,"To Change Speed, Pump Fist Upwards \n\r");
fprintf(stdout,"To Change Song, Press PushButton \n\r");
fprintf(stdout,"Place Arm Band on, Wait until You See Average1 and Average2\n\r");
fprintf(stdout,"Then Begin \n\r"); 

//delay to read instructions
_delay_ms(4000);

//init the state machine
 	PushState = Release;

// init the sine table
 	for (i=0; i<256; i++)
  	begin
  	 sineTable[i] = (char)(127.0 * sin(6.283*((float)i)/256.0)) ;

  	end      
 	sei() ;

end 

Credits

Erica Barnett
1 project • 0 followers
Contact
Mary Beth Galanko
1 project • 0 followers
Contact

Comments

Please log in or sign up to comment.