How to stop a long function with a button?

Hi,

I’m pretty new and learning to program while going along some projects. I got an idea, based on this video:

It plays the Imperial March from Star Wars with a piezo element. I got the code for the audio but what I want to do is use a button to stop the audio from playing. When I press the button again, it should start playing from the beginning again.

I can’t get my head around how and where to break the long march() function in the code below. Any help would be appreciated.

int ledPin = 13;//led for visualization (use 13 for built-in led)
int speakerPin = 11; //speaker connected to one of the PWM ports
int inputPin = 2; //button
int val = 0;
int old_val = 0;
int state = 0; //0=OFF 1=ON
 
#define c 261
#define d 294
#define e 329
#define f 349
#define g 391
#define gS 415
#define a 440
#define aS 455
#define b 466
#define cH 523
#define cSH 554
#define dH 587
#define dSH 622
#define eH 659
#define fH 698
#define fSH 740
#define gH 784
#define gSH 830
#define aH 880
//frequencies for the tones we're going to use
//used http://home.mit.bme.hu/~bako/tonecalc/tonecalc.htm to get these
 
void setup()    
{        
  pinMode(ledPin, OUTPUT);
  // sets the ledPin to be an output
  pinMode(speakerPin, OUTPUT);  
  //sets the speakerPin to be an output
  pinMode(inputPin,INPUT);
}        
         
void loop()     // run over and over again
{
  val = digitalRead(inputPin);

  if((val == HIGH)&&(old_val==LOW)){
    state=1-state;
    delay(10);
  }
  
  old_val=val;
  
  if(state==1){
    march();
  }else{
    digitalWrite(speakerPin, LOW);
  }
}        
         
void beep (unsigned char speakerPin, int frequencyInHertz, long timeInMilliseconds)
{
    digitalWrite(ledPin, HIGH);  
    //use led to visualize the notes being played
   
    int x;      
    long delayAmount = (long)(1000000/frequencyInHertz);
    long loopTime = (long)((timeInMilliseconds*1000)/(delayAmount*2));
    for (x=0;x<loopTime;x++)    
    {    
        digitalWrite(speakerPin,HIGH);
        delayMicroseconds(delayAmount);
        digitalWrite(speakerPin,LOW);
        delayMicroseconds(delayAmount);
    }    
   
    digitalWrite(ledPin, LOW);
    //set led back to low
   
    delay(20);
    //a little delay to make all notes sound separate
}        
         
void march()
{        
    //for the sheet music see:
    //http://www.musicnotes.com/sheetmusic/mtd.asp?ppn=MN0016254
    //this is just a translation of said sheet music to frequencies / time in ms
    //used 500 ms for a quart note
   
    beep(speakerPin, a, 500);
    beep(speakerPin, a, 500);    
    beep(speakerPin, a, 500);
    beep(speakerPin, f, 350);
    beep(speakerPin, cH, 150);
   
    beep(speakerPin, a, 500);
    beep(speakerPin, f, 350);
    beep(speakerPin, cH, 150);
    beep(speakerPin, a, 1000);
    //first bit
   
    beep(speakerPin, eH, 500);
    beep(speakerPin, eH, 500);
    beep(speakerPin, eH, 500);    
    beep(speakerPin, fH, 350);
    beep(speakerPin, cH, 150);
   
    beep(speakerPin, gS, 500);
    beep(speakerPin, f, 350);
    beep(speakerPin, cH, 150);
    beep(speakerPin, a, 1000);
    //second bit...
   
    beep(speakerPin, aH, 500);
    beep(speakerPin, a, 350);
    beep(speakerPin, a, 150);
    beep(speakerPin, aH, 500);
    beep(speakerPin, gSH, 250);
    beep(speakerPin, gH, 250);
   
    beep(speakerPin, fSH, 125);
    beep(speakerPin, fH, 125);    
    beep(speakerPin, fSH, 250);
    delay(250);
    beep(speakerPin, aS, 250);    
    beep(speakerPin, dSH, 500);  
    beep(speakerPin, dH, 250);  
    beep(speakerPin, cSH, 250);  
    //start of the interesting bit
   
    beep(speakerPin, cH, 125);  
    beep(speakerPin, b, 125);  
    beep(speakerPin, cH, 250);      
    delay(250);
    beep(speakerPin, f, 125);  
    beep(speakerPin, gS, 500);  
    beep(speakerPin, f, 375);  
    beep(speakerPin, a, 125);
   
    beep(speakerPin, cH, 500);
    beep(speakerPin, a, 375);  
    beep(speakerPin, cH, 125);
    beep(speakerPin, eH, 1000);
    //more interesting stuff (this doesn't quite get it right somehow)
   
    beep(speakerPin, aH, 500);
    beep(speakerPin, a, 350);
    beep(speakerPin, a, 150);
    beep(speakerPin, aH, 500);
    beep(speakerPin, gSH, 250);
    beep(speakerPin, gH, 250);
   
    beep(speakerPin, fSH, 125);
    beep(speakerPin, fH, 125);    
    beep(speakerPin, fSH, 250);
    delay(250);
    beep(speakerPin, aS, 250);    
    beep(speakerPin, dSH, 500);  
    beep(speakerPin, dH, 250);  
    beep(speakerPin, cSH, 250);  
    //repeat... repeat
   
    beep(speakerPin, cH, 125);  
    beep(speakerPin, b, 125);  
    beep(speakerPin, cH, 250);      
    delay(250);
    beep(speakerPin, f, 250);  
    beep(speakerPin, gS, 500);  
    beep(speakerPin, f, 375);  
    beep(speakerPin, cH, 125);
           
    beep(speakerPin, a, 500);            
    beep(speakerPin, f, 375);            
    beep(speakerPin, c, 125);            
    beep(speakerPin, a, 1000);      
    //and we're done \ó/    
}

Thanks again,

Frank

This sort-of works:

int ledPin = 13;//led for visualization (use 13 for built-in led)
int speakerPin = 11; //speaker connected to one of the PWM ports
int inputPin = 2; //button
int val = 0;
int old_val = 0;
int state = 0; //0=OFF 1=ON
 
#define c 261
#define d 294
#define e 329
#define f 349
#define g 391
#define gS 415
#define a 440
#define aS 455
#define b 466
#define cH 523
#define cSH 554
#define dH 587
#define dSH 622
#define eH 659
#define fH 698
#define fSH 740
#define gH 784
#define gSH 830
#define aH 880
//frequencies for the tones we're going to use
//used http://home.mit.bme.hu/~bako/tonecalc/tonecalc.htm to get these
 
bool playing;

void setup()    
{        
  pinMode(ledPin, OUTPUT);
  // sets the ledPin to be an output
  pinMode(speakerPin, OUTPUT);  
  //sets the speakerPin to be an output
  pinMode(inputPin,INPUT);
  digitalWrite (inputPin, HIGH);  // enable pull-up
}        
         
void loop()     // run over and over again
{
  val = digitalRead(inputPin);

  if((val == HIGH)&&(old_val==LOW)){
    state=1-state;
    delay(10);
  }
  
  old_val=val;
  
  if(state==1){
    march();
    delay (10);  // debounce
  }else{
    digitalWrite(speakerPin, LOW);
  }
}        
         
void beep (unsigned char speakerPin, int frequencyInHertz, long timeInMilliseconds)
{
    if (digitalRead(inputPin) == LOW)
      playing = false;

    if (!playing)
      return;
      
    digitalWrite(ledPin, HIGH);  
    //use led to visualize the notes being played
   
    int x;      
    long delayAmount = (long)(1000000/frequencyInHertz);
    long loopTime = (long)((timeInMilliseconds*1000)/(delayAmount*2));
    for (x=0;x<loopTime;x++)    
    {    
        digitalWrite(speakerPin,HIGH);
        delayMicroseconds(delayAmount);
        digitalWrite(speakerPin,LOW);
        delayMicroseconds(delayAmount);
    }    
   
    digitalWrite(ledPin, LOW);
    //set led back to low
   
    delay(20);
    //a little delay to make all notes sound separate
}        
       
void mydelay (unsigned long interval)
  {
  if (!playing)
    return;

  delay (interval);    
  }
  
void march()
{        
   playing = true;
   
    //for the sheet music see:
    //http://www.musicnotes.com/sheetmusic/mtd.asp?ppn=MN0016254
    //this is just a translation of said sheet music to frequencies / time in ms
    //used 500 ms for a quart note
   
    beep(speakerPin, a, 500);
    beep(speakerPin, a, 500);    
    beep(speakerPin, a, 500);
    beep(speakerPin, f, 350);
    beep(speakerPin, cH, 150);
   
    beep(speakerPin, a, 500);
    beep(speakerPin, f, 350);
    beep(speakerPin, cH, 150);
    beep(speakerPin, a, 1000);
    //first bit
   
    beep(speakerPin, eH, 500);
    beep(speakerPin, eH, 500);
    beep(speakerPin, eH, 500);    
    beep(speakerPin, fH, 350);
    beep(speakerPin, cH, 150);
   
    beep(speakerPin, gS, 500);
    beep(speakerPin, f, 350);
    beep(speakerPin, cH, 150);
    beep(speakerPin, a, 1000);
    //second bit...
   
    beep(speakerPin, aH, 500);
    beep(speakerPin, a, 350);
    beep(speakerPin, a, 150);
    beep(speakerPin, aH, 500);
    beep(speakerPin, gSH, 250);
    beep(speakerPin, gH, 250);
   
    beep(speakerPin, fSH, 125);
    beep(speakerPin, fH, 125);    
    beep(speakerPin, fSH, 250);
    mydelay(250);
    beep(speakerPin, aS, 250);    
    beep(speakerPin, dSH, 500);  
    beep(speakerPin, dH, 250);  
    beep(speakerPin, cSH, 250);  
    //start of the interesting bit
   
    beep(speakerPin, cH, 125);  
    beep(speakerPin, b, 125);  
    beep(speakerPin, cH, 250);      
    mydelay(250);
    beep(speakerPin, f, 125);  
    beep(speakerPin, gS, 500);  
    beep(speakerPin, f, 375);  
    beep(speakerPin, a, 125);
   
    beep(speakerPin, cH, 500);
    beep(speakerPin, a, 375);  
    beep(speakerPin, cH, 125);
    beep(speakerPin, eH, 1000);
    //more interesting stuff (this doesn't quite get it right somehow)
   
    beep(speakerPin, aH, 500);
    beep(speakerPin, a, 350);
    beep(speakerPin, a, 150);
    beep(speakerPin, aH, 500);
    beep(speakerPin, gSH, 250);
    beep(speakerPin, gH, 250);
   
    beep(speakerPin, fSH, 125);
    beep(speakerPin, fH, 125);    
    beep(speakerPin, fSH, 250);
    mydelay(250);
    beep(speakerPin, aS, 250);    
    beep(speakerPin, dSH, 500);  
    beep(speakerPin, dH, 250);  
    beep(speakerPin, cSH, 250);  
    //repeat... repeat
   
    beep(speakerPin, cH, 125);  
    beep(speakerPin, b, 125);  
    beep(speakerPin, cH, 250);      
    mydelay(250);
    beep(speakerPin, f, 250);  
    beep(speakerPin, gS, 500);  
    beep(speakerPin, f, 375);  
    beep(speakerPin, cH, 125);
           
    beep(speakerPin, a, 500);            
    beep(speakerPin, f, 375);            
    beep(speakerPin, c, 125);            
    beep(speakerPin, a, 1000);      
    //and we're done \ó/    
}

It stops when you ground the button, it seems to take a couple of presses to get it started again, but you might want to look at your button logic in loop to fix that.

Basically I put a test in "beep" to see if the input pin was low, and if so set a boolean, so that all subsequent calls to beep just returned. I also made a "mydelay" function that didn't delay if the switch had been pressed.

Probably a better way would be to build the entire tune into an array (ie. note, duration). Then the march function could just loop through the array, playing each note one by one. And if it detects the button press, it just exits the loop. You could build in pauses by having a note value of 0 as a special case.

Would an 'interupt' work for you? Jeremy Blum has good tutorials on youtube.

I thought of interrupts, but basically they just help you know a button has been pressed. They aren't so good at stopping a function executing mid-stream. So you still have the issue of stopping doing all those "beep" function calls.

I think turning the whole "play a tune" thing into a simple loop of an array would be best. Then each time, before you choose to play another note, you just check if the button is pressed.

Hi Nick

Thanks, that worked. I am studying the code and I seem to understand how it works. I am fairly new to this all. Just had my Blinking Light example finished :slight_smile:
What I don't understand is the use of the mydelay function. I can't see it called anywhere else.

Thanks for your help, I appreciate it!

Frank

frankmeeuwsen:
What I don't understand is the use of the mydelay function. I can't see it called anywhere else.

Anywhere else from what? It's just used here to eliminate the delays (rests) once you press the button.

[quote author=Nick Gammon link=topic=77119.msg582660#msg582660 date=1319865983]
I thought of interrupts, but basically they just help you know a button has been pressed. They aren't so good at stopping a function executing mid-stream. So you still have the issue of stopping doing all those "beep" function calls.[/quote]

What if the interrupt vector had a goto statement to the beginning of loop? Or if the interrupt just called sei() and acted as the main program?

No, no, no.

It's been tried before, believe me. :slight_smile:

What was wrong with my suggestion of making a table of notes? Once you start thinking of goto's and doing everything inside an interrupt, your design is wrong.

Here, I'll show you:

int ledPin = 13;//led for visualization (use 13 for built-in led)
int speakerPin = 11; //speaker connected to one of the PWM ports
int inputPin = 2; //button
 
#define c 261
#define d 294
#define e 329
#define f 349
#define g 391
#define gS 415
#define a 440
#define aS 455
#define b 466
#define cH 523
#define cSH 554
#define dH 587
#define dSH 622
#define eH 659
#define fH 698
#define fSH 740
#define gH 784
#define gSH 830
#define aH 880
//frequencies for the tones we're going to use
//used http://home.mit.bme.hu/~bako/tonecalc/tonecalc.htm to get these
 
// each note has a pitch and duration
// pitch of 0 means a rest
// ends when pitch and duration are both zero

typedef struct note {
    int pitch;
    int duration;
} note;

// The tune

note song [] = {
  {a, 500},
  {a, 500},    
  {a, 500},
  {f, 350},
  {cH, 150},
  
  {a, 500},
  {f, 350},
  {cH, 150},
  {a, 1000},
  //first bit
  
  {eH, 500},
  {eH, 500},
  {eH, 500},    
  {fH, 350},
  {cH, 150},
  
  {gS, 500},
  {f, 350},
  {cH, 150},
  {a, 1000},
  //second bit...
  
  {aH, 500},
  {a, 350},
  {a, 150},
  {aH, 500},
  {gSH, 250},
  {gH, 250},
  
  {fSH, 125},
  {fH, 125},    
  {fSH, 250},
  {0, 250},
  {aS, 250},    
  {dSH, 500},  
  {dH, 250},  
  {cSH, 250},  
  //start of the interesting bit
  
  {cH, 125},  
  {b, 125},  
  {cH, 250},      
  {0, 250},
  {f, 125},  
  {gS, 500},  
  {f, 375},  
  {a, 125},
  
  {cH, 500},
  {a, 375},  
  {cH, 125},
  {eH, 1000},
  //more interesting stuff (this doesn't quite get it right somehow)
  
  {aH, 500},
  {a, 350},
  {a, 150},
  {aH, 500},
  {gSH, 250},
  {gH, 250},
  
  {fSH, 125},
  {fH, 125},    
  {fSH, 250},
  {0, 250},
  {aS, 250},    
  {dSH, 500},  
  {dH, 250},  
  {cSH, 250},  
  //repeat... repeat
  
  {cH, 125},  
  {b, 125},  
  {cH, 250},      
  {0, 250},
  {f, 250},  
  {gS, 500},  
  {f, 375},  
  {cH, 125},
       
  {a, 500},            
  {f, 375},            
  {c, 125},            
  {a, 1000}, 
   
  {0, 0}  // end of table marker
};  // end of song

        
void beep (unsigned char speakerPin, int frequencyInHertz, long timeInMilliseconds)
{
     
    //use led to visualize the notes being played
    digitalWrite(ledPin, HIGH);  
   
    int x;      
    long delayAmount = (long)(1000000/frequencyInHertz);
    long loopTime = (long)((timeInMilliseconds*1000)/(delayAmount*2));
    for (x=0;x<loopTime;x++)    
    {    
        digitalWrite(speakerPin,HIGH);
        delayMicroseconds(delayAmount);
        digitalWrite(speakerPin,LOW);
        delayMicroseconds(delayAmount);
    }    
   
    //set led back to low
    digitalWrite(ledPin, LOW);
   
    //a little delay to make all notes sound separate
    delay(20);
}   // end of beep    

void march()
{        
   int pos = 0;
   
   // play until switch pressed
   while (digitalRead(inputPin) == HIGH)
     {
     if (song [pos].pitch == 0 && song [pos].duration == 0)
       return;  // end of song
     
     if (song [pos].pitch == 0)
       delay (song [pos].duration);  // rest
     else
       beep (speakerPin, song [pos].pitch, song [pos].duration);
       
     pos++;  // next note
       
     }
   
     // wait for switch release
  while (digitalRead(inputPin) == LOW)
    delay (50);

}  // end of march


void setup()    
{        
  pinMode(ledPin, OUTPUT);
  // sets the ledPin to be an output
  pinMode(speakerPin, OUTPUT);  
  //sets the speakerPin to be an output
  pinMode(inputPin,INPUT);
  digitalWrite (inputPin, HIGH);  // enable pull-up
}  // end of setup      
         
void loop()     // run over and over again
{
  
  // wait for switch press
  while (digitalRead(inputPin) == HIGH)
    delay (50);
    
  // wait for switch release
  while (digitalRead(inputPin) == LOW)
    delay (50);
      
  march();
}   // end of loop