Building an audio device #3: prototypes, interactions and code

The box at its most simplest should be able to start, stop and pause audio tracks in a playlist. The code below does this based on a few simple interactions.

In this prototype, there are two switches, wired into the underside of the box. Put the box down on a surface and both buttons are toggled on. Lift it up and they are toggled off. These interactions fire the goToSleep() and waken() functions, which in this case play respective salutary and valedictory audio clips.

While the box is being carried, both buttons on the underside are in the off position. Pressing one of the buttons pauses or unpauses any audio clip that is currently playing (togglePauseButton(). Pressing the other button loads up and plays the next audio clip in the list (skipToNextFile()).

You can see the prototype in use during this little vignette of our trial run:


 

 

/* adapted from http://www.ladyada.net/make/waveshield/libraryhc.html */

#include "WaveUtil.h"
#include "WaveHC.h"

SdReader card;							// This object holds the information for the card
FatVolume vol;							// This holds the information for the partition on the card
FatReader root;							// This holds the information for the filesystem on the card
FatReader f;							// This holds the information for the file we're play

WaveHC wave;							// This is the only wave (audio) object, 
										// since we will only play one at a time

void setup() {
  Serial.begin(9600);   				// set up serial port
  
  pinMode(2, OUTPUT);					// Set the output pins for the DAC control. 
  pinMode(3, OUTPUT);					// These pins are defined in the library
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
 
  pinMode(13, OUTPUT);					// pin13 LED

  if (!card.init()) {         
    sdErrorCheck();
    while(1);                           // then 'halt' - do nothing!
  }
  
  card.partialBlockRead(true);			// enable optimize read - some cards may timeout.
  										// Disable if you're having problems

 
  uint8_t part;							// Now we will look for a FAT partition!
  for (part = 0; part < 5; part++) {	// we have up to 5 slots to look in
    if (vol.init(card, part)) 
      break;							// we found one, lets bail
  }
  if (part == 5) {						// if we ended up not finding one  :(
    sdErrorCheck();						// Something went wrong, lets print out why
    while(1);							// then 'halt' - do nothing!
  }
      
  if (!root.openRoot(vol)) {			// Try to open the root directory
    									// Something went wrong,
    while(1);							// so 'hang' - do nothing!
  }
  
										// Whew! We got past the tough parts.
}

/* device state flags */

int paused = 0;	 						// flag for whether we've activated a pause button
int awake = 0;							// flag for whether the device is awake

/* button state flags */

int buttonValue_1 = 0;					// value of button one's analogue input
int buttonState_1 = 0;					// 'on' or 'off' value of button one's state
int buttonValue_2 = 0;					// value of button two's analogue input
int buttonState_2 = 0;					// 'on' or 'off' value of button two's state

int bothButtonsOn = 0;					// default start value is all buttons off
int buttonOneOn = 0;					// these values help to track 
int buttonTwoOn = 0;					// the state of buttons over time 
int bothButtonsOff = 0;					// over time 

/* normal play mode for the device is to iterate over a predefined playlist */
										
const int NumberOfFiles = 9;			// number of files in the playlist 
char* fileList[NumberOfFiles] = {		// ordered list of filenames in the playlist 
  "F005.WAV",							// not only there does he see this sight, tim
  "F007.WAV",							// it's so easy to miss someone, tim
  "F009.WAV",							// his visitor, becka
  "F006.WAV",							// you always want to be somewhere else, tim
  "F012.WAV",							// lament, tim
  "F020.WAV",							// the haunter, becka
  "F024.WAV",							// we used to stand on the bridge, tim
  "F021.WAV", 							// the phantom horsewoman, becka
  "F001.WAV",							// a curl of hair, tim
};
											
int filePointer = -1; 					// reference to the file which should 
										// be played at any time
char* fileToPlay;						// reference to the name of file to play

/* device control functions */

void togglePauseButton() { 				// handles the pause button toggle

	if (paused < 1){					// state is 'unpaused'
		if (wave.isplaying){			// check if a wave is playing
			wave.pause();				// if it is, pause it
			paused = 1;					// store 'paused' state
		}
	} else if (paused > 0){				// state is 'paused'
		wave.resume();					// resume the paused wave
		paused = 0;						// store 'unpaused' state
	}
}

void skipToNextFile() {					// skips to next file in playlist

	filePointer++;						// increment the file reference
	if (filePointer == NumberOfFiles){	// reset if we exceed 
		filePointer = 0;				// the length of the playlist
	}
	
	if (wave.isplaying) {				// already playing something, so stop it!
		wave.stop();
	}
	
	fileToPlay = fileList[filePointer];	// pick the file to play
	playfile(fileToPlay);				// play it

}

void goToSleep() {
	
	if (awake) {						// if we're only now being sent to sleep,
		fileToPlay = "F027.WAV";		// pick a specific 'goodbye' sound
		playcomplete(fileToPlay);		// and play it
	}
	awake = 0;
	
}

void waken() {

	if (!awake) {						// if we're only just waking up,
		fileToPlay = "F026.WAV";		// pick a specific 'greeting' sound
		playcomplete(fileToPlay);		// and play it
	}				
	awake = 1;

}

void loop() {

	/* capture input */
	
	buttonValue_1 = analogRead(0) / 4; 	// read analogue pin 0
	buttonValue_2 = analogRead(1) / 4; 	// read analogue pin 1
										
	if (buttonValue_1 > 128) { buttonState_1 = 1; } else { buttonState_1 = 0; } 
	if (buttonValue_2 > 128) { buttonState_2 = 1; } else { buttonState_2 = 0; } 
										// convert to boolean
	
	
	if (buttonState_1 > 0 && buttonState_2 > 0) { 	
										// both buttons pressed

		if (bothButtonsOn) {			// both buttons have been held in 
										// so device has been put down
			goToSleep();				// send to sleep
			
		} else { 						// first detection 
	
			/* - do nothing for now */
	
		}
		
		bothButtonsOn 	= 1;			// remember button state for the next pass
		buttonOneOn 	= 0;			// all other buttons parameters are false
		buttonTwoOn 	= 0;		 
		bothButtonsOff 	= 0;
		
		delay(1000);					// wait a second

	} else if (buttonState_1 > 0) { 	// only button 1 pressed
		
		if (buttonOneOn) {				// button has been held in

			/* - we could add 'hold button one down' functionality here */
		
		} else {
	
			togglePauseButton();		// on first press, button 1 pauses / resumes
		
		}

		bothButtonsOn 	= 0;		
		buttonOneOn 	= 1;			// remember button state for the next pass
		buttonTwoOn 	= 0;			// all other buttons parameters are false
		bothButtonsOff 	= 0;	

		delay(1000);
		
	} else if (buttonState_2 > 0){		// only button 2 pressed
	
		if (buttonTwoOn) {				// button has been held in
		
			/* - we could add 'hold button two down' functionality here */

		} else {
	
			skipToNextFile();			// button two skips to next track
		
		}

		bothButtonsOn 	= 0;		
		buttonOneOn 	= 0;		
		buttonTwoOn 	= 1;			// remember button state for the next pass 
		bothButtonsOff 	= 0;			// all other buttons parameters are false

		delay(1000);
							
	} else {							// neither button pressed
													
		if (bothButtonsOff > 0){		// both buttons off for a second pass...
										// we have been lifted up!
			if (!awake){				// if we haven't already been woken up
				waken();				// wake up
			}
		
		} else {						// we must be inside the first pass
										// of both buttons being switched 'off'

			delay(1000);				// await the second pass
		
		}
		
		bothButtonsOn 	= 0;		
		buttonOneOn 	= 0;		
		buttonTwoOn 	= 0;		 
		bothButtonsOff 	= 1;	 		// remember button state for the next pass
										// all other buttons parameters are false
		
	}

}




void playcomplete(char *name) {			// Plays a full file from beginning to end 
										// pause is disabled
  playfile(name);						// call our helper to find and play this name
  while (wave.isplaying) {
  										// do nothing while its playing
  }
  										// now its done playing
}

void playfile(char *name) {
  if (wave.isplaying) {  				// see if the wave object is currently doing something
										// already playing something, so stop it!
    wave.stop(); 						// stop it
  }
  if (!f.open(root, name)) {  			// look in the root directory and open the file
										// do nothing, put an error message here
  }
  if (!wave.create(f)) {   				// OK read the file and turn it into a wave object

    return;
  }
  
  wave.play();	  						// ok time to play! start playback
}

int freeRam(void) 						// this handy function will return the number of bytes
										// currently free in RAM, great for debugging!   
{
  extern int  __bss_end; 
  extern int  *__brkval; 
  int free_memory; 
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end); 
  }
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval); 
  }
  return free_memory; 
} 

void sdErrorCheck(void)
{
  if (!card.errorCode()) return;		// no error; get on with it!
  while(1);								// there's an error with the card
  										// so 'hang'
}


Posted by joe

10 February, 2012

audio, video, playlist, prototype, playback, code.

Add a comment