Time Based LED switching while streaming Audio

Hello Community,

I am struggling with the following problem:

I'm using an Audio Shield http://www.elv.de/audio-shield-fuer-arduino-asa1-bausatz.html.
Basically the shield is reading mp3 Audio from an SD cart and sends it to an mp3 decoder (VS1011).
WHILE this is done I want to switch on/off LED's matching to the streamed Audio,

Here is the important part of the code:

loop()
{     
  //Create Buffer for the mp3 decoder
  unsigned char buffer[32];
  //open File
  if( File SoundFile = SD.open( "001.mp3", FILE_READ ) )
  {
    //play file as long as it is available.
    while( SoundFile.available())
    {
      //Read File chunk
      SoundFile.read( buffer, sizeof(buffer));
      //send File chunk to the mp3 decoder
      VS1011.Send32( buffer );
    }
}
}

When Audio is on everything has to be done inside the WHILE loop, right?
I'm switching the LEDs i'm using the millis() function (known from the blink without delay example http://arduino.cc/en/Tutorial/BlinkWithoutDelay):

loop()
{     
  //Create Buffer for the mp3 decoder
  unsigned char buffer[32];
  //open File
  if( File SoundFile = SD.open( "001.mp3", FILE_READ ) )
  {
    //play file as long as it is available.
    while( SoundFile.available())
    {
      //Read File chunk
      SoundFile.read( buffer, sizeof(buffer));
      //send File chunk to the mp3 decoder
      VS1011.Send32( buffer );
      current_1 = millis();
      if( (current_1 - previous_1) >= on_1_1) digitalWrite(blink_1, HIGH);
      if( (current_1 - previous_1) >= off_1_1)digitalWrite(blink_1, LOW);              
      if( (current_1 - previous_1) >= on_2_1) digitalWrite(blink_1, HIGH);
      if( (current_1 - previous_1) >= off_2_1) digitalWrite(blink_1, LOW);
      if( (current_1 - previous_1) >= on_3_1) digitalWrite(blink_1, HIGH);      
      if( (current_1 - previous_1) >= off_3_1) digitalWrite(blink_1, LOW);
    }
}
}

the problem is that if too many if() and millis() functions are used the time consumption is too big, so the mp3 decoder buffer can't be filled fast enough and the sound starts to stutter. I already lowered the mp3 bit rate, but i want to do many more LED switchings.

Do you have any idea how to optimize this? I think using hardware timer will also not work, because using Interrupts would also "interrupt" the data streaming?

Thanks so far. I'm using an Arduino Mega, so program size doesn't matte. But maybe it's simply too slow for this task.

You may wish to try speeding up the communication rate with the MP3 shield. I don't know if it uses I2C or SPI, but here is information on both:

http://playground.arduino.cc/Main/WireLibraryDetailedReference

For I2C, try changing the speed from 100kHz to 400kHz, or for SPI, try setting the clock divider to 2 instead of 4.

Also, you are doing a lot of arithmetic -- you are computing (current_1 - previous_1) six times, when you only need to do it once.

register byte timeDifference = millis() - previous_1;
if( timeDifference >= on_1_1) digitalWrite(blink_1, HIGH);

Yes, I know unsigned longs are normally used when dealing with millis(), but if your loop is looping at least once every 255 milliseconds, a byte should not overflow and byte arithmetic is approximately 4 times faster than long. Making it a 'register' byte will suggest to the compiler that you want future references to timeDifference to execute as fast as possible.

I'm not sure what data type on_1_1 is, but if it doesn't need to be a variable, make it a 'const' or a #define to speed execution. If on_1_1 does need to be a variable, at least see if you can use a byte (or failing that, an int).

blink_1 should also be a 'const byte' or a #define. Also, check out the digitalWriteFast2 library.

Finally, if you use 'else if' in your block of 'if' statements, the loop will take less time to execute when one of the top cases is true.

The SPI speed setting helped actually! Thanks for this, as I would never have found out by myself.

Actually I tried to use the TimerOne Library to read the mp3 file and send it to the Buffer every 1000 microseconds (256 kbit/s mp3 = 23 kbyte/s = 32 ByteBlock/1000 ms). But it doesn't work, and I think it's because of the variables "SoundFile" and "buffer[32]", they are not used by the Page_1_read() Function, right?
Maybe the problem is also that SoundFile.read( ) and VS1011.Send32( ) won't work in interrupted mode?
It would be nice this way, because than I could use the whole loop without worrying about the Buffer running out of Data...

#include <SD.h>
#include <SPI.h>
#include <AudioShield.h>
#include <TimerOne.h>

File SoundFile;
unsigned char buffer[32];

void setup() 
{  
  SPI.setClockDivider(2);
  Timer1.initialize();
  VS1011.begin();
  VS1011.SetVolume(50,50);
  VS1011.UnsetMute();
}


void loop()
{
   unsigned char buffer[32]; 
  if( File SoundFile = SD.open( "256.mp3", FILE_READ ) )
  {
    Timer1.attachInterrupt(Page_1_read,1000);
    while( SoundFile.available())
    {
      //Set LED's, Button Polling ...
    }
    Timer1.detachInterrupt();
    VS1011.Send2048Zeros();
    SoundFile.close();
  }
}


void Page_1_read()
{
      SoundFile.read( buffer, sizeof(buffer));
      VS1011.Send32( buffer );
}

I will change all the data types according to you tylernt, but I did not understand the register byte? how do you use it?

Do you have any suggestion how to switch the LED's on and off after a specific time? I have 4 LED's and I want to switch them for many many times while a 5 minute song is running. How would you do this in an effective way?

many thanks and good night!

himijendrix:
it doesn't work, and I think it's because of the variables "SoundFile" and "buffer[32]", they are not used by the Page_1_read() Function, right?

Right. To use a variable in an interrupt, you must define the variables as 'volatile':

volatile unsigned char buffer[32];

Since I'm assuming the File for SoundFile is an object rather than a variable, you may not be able to declare is as 'volatile'

Maybe the problem is also that SoundFile.read( ) and VS1011.Send32( ) won't work in interrupted mode?

Generally, communication cannot be done from within an interrupt service routine. There are special exceptions where you can, but I don't know if this is one of those.

It would be nice this way, because than I could use the whole loop without worrying about the Buffer running out of Data...

Well the good news is, it should be possible to accomplish this without an interrupt.

I will change all the data types according to you tylernt, but I did not understand the register byte? how do you use it?

This has to do with the internal workings of the CPU. Reading and writing things to/from RAM is actually a bit slow. The CPU has 32 special "slots" inside of it that can hold data like RAM, yet be read/written to more quickly than RAM. Normally, the compiler (when you click "Upload" in the Arduino IDE) decides what variables in your program to put into these faster registers and which to put into slower RAM -- for a very small sketch, it might automatically put ALL of your variables in nice fast registers. Unfortunately, most sketches have more variables than will fit into registers, so some have to go to RAM.

The 'register' declaration suggests to the compiler that you want that variable to stay in the faster register, if at all possible. The compiler will do it's best, but your variable might still wind up in RAM if the compiler just can't find a free register to put it in.

Don't use 'register' unless you really need to -- the compiler is pretty smart and will, usually, automatically put the most frequently used variables in registers for you.

Do you have any suggestion how to switch the LED's on and off after a specific time? I have 4 LED's and I want to switch them for many many times while a 5 minute song is running. How would you do this in an effective way?

Do you want any particular pattern? Here's one way you might do them sequentially. I'm assuming the 4 LEDs are on adjacent pins (for example, pins 3, 4, 5, and 6):

const byte ledInterval = 100; // change 100 to suit
unsigned long previousMillis = millis();
register byte currentPin = 3;
while( SoundFile.available())
  {
  SoundFile.read( buffer, sizeof(buffer));
  VS1011.Send32( buffer );
  if( millis() - previousMillis >= ledInterval ) // time to change LEDs!
    {
    previousMillis += ledInterval; // schedule next LED change to happen in the future
    digitalWrite(currentPin, LOW); // turn off whatever LED is on
    currentPin++; // select the next pin
    if(currentPin > 6) // make sure we stay...
      { currentPin = 3; } // ...in the right pin range
    digitalWrite(currentPin, HIGH); // turn on the next LED
    }
  }

The following would be a bit slower, but you could also flash the LEDs in an arbitrary order by declaring an array like

byte ledPin[4] = { 4, 5, 3, 6 };
register byte currentLED = 0;

Then flash them with something like:

digitalWrite(ledPin[currentLED], LOW); // turn off whatever LED is on
currentLED++; // select the next array element
if(currentLED > 3) // make sure we stay...
  { currentLED = 0; } // ...in the right array range
digitalWrite(ledPin[currentLED], HIGH); // turn on the next LED

Thanks so far, so I will leave the interrupt (I already thought it is better used for reading push buttons or other sensors ...).

The problem is that I don't want to flash the LED's in a strict pattern, they should rather fit to the sound (There's a dialogue between three monsters and one of the Led's will lighten up the monsters' face when it is saying something...)
So I'm going to record the Audio file and then write down a timeline when which LED is switched on/off)

So I guess what I will have to do is writing the ON/Off times into an Array, and than switch the LED's in a pattern with variable On/Off times which change after every step ... but from this point I can also try to use your example tylernt. So actually with the faster SPI connection it should work.

But one last question: Is there any list where the Arduino Functions (arithmetical operations, millis(), digitalread(),analogread()...) and their CPU time consumption are compared?

himijendrix:
The problem is that I don't want to flash the LED's in a strict pattern, they should rather fit to the sound (There's a dialogue between three monsters and one of the Led's will lighten up the monsters' face when it is saying something...)
So I'm going to record the Audio file and then write down a timeline when which LED is switched on/off)

So I guess what I will have to do is writing the ON/Off times into an Array, and than switch the LED's in a pattern with variable On/Off times which change after every step ... but from this point I can also try to use your example tylernt. So actually with the faster SPI connection it should work.

Sounds like fun. If you have a lot of on/off commands though, you will quickly fill up RAM. PROGMEM might be a better idea to store these in, as you have much more room.

#include <avr/pgmspace.h>

struct ledEvent
   {
   byte ledPin;
   unsigned long ledStart;
   unsigned long ledStop;
   };

 PROGMEM const ledEvent ledSequence[100] =
   {
     { 3, 0, 3000 },
     { 4, 3000, 4000 },
     { 5, 3500, 7000 },
     { 6, 7000, 8000 }
    };

You can refer to information in PROGMEM in your loop with:

pgm_read_byte(&(ledSequence[i].ledPin))

If you refer to i-1 (for previous LED stop time) as well as i (for current LED start time) in each loop iteration, you can even overlap LEDs (have two lit at the same time). Be aware that pgm_read_byte is a bit slower than RAM, so avoid excessive calls to it.

You should also be able to read 'long's from PROGMEM, but I haven't tried that myself. My guess would probably be something like

pgm_read_dword(&(ledSequence[i].ledStart))

More information here: PROGMEM - Arduino Reference

But one last question: Is there any list where the Arduino Functions (arithmetical operations, millis(), digitalread(),analogread()...) and their CPU time consumption are compared?

Not that I know of, but as a general rule:
Addition and subtraction are the fastest arithmetic.
Multiplication is slower.
Division is even slower.
Floating point arithmetic is VERY slow.

Byte/char is the fastest/smallest, followed by int/word, followed by long and float.

analogRead is slow (100 microseconds), digitalRead is much faster. The digitalWriteFast2 library is even faster, but usually only if your pin numbers are constants (not variables). Direct port manipulation is fastest, but a pain to work with.

Not sure on millis().

Thanks, tylernt. The Ram doesn't really matter, I am using an Mega 2560.
But I found a way to do program it and post it here, just in case someone has a similar problem to solve.
By the way, does it matter for the micro-controller if a function is used (Time Consumption...)? Or is it just for keeping the code tidy before compiling it?

#include <SD.h>
#include <SPI.h>
#include <AudioShield.h>

long started;
const int blink_1 = 27;
int blink_1_onstate = 0;
int blink_1_offstate = 0;
//Here You can Put the Times, when the LED should be switched on 
//In the End a Time which will not reached should be set , to avoid switching the LED on again, while still playing Audio
long blink_1_on [] = {
  300,500,10700,10900,11100,11300,11500,11700,11900,12100,12300,90000};  
long blink_1_off [] = {
  400,600,10800,11000,11200,11400,11600,11800,12000,12200,12400};

void setup() 
{  
  pinMode(blink_1, OUTPUT);
  SPI.setClockDivider(2);
  VS1011.begin();
  VS1011.SetVolume(50,50);
  VS1011.UnsetMute();
}


void loop()
{
  unsigned char buffer[32]; 
  if( File SoundFile = SD.open( "256.mp3", FILE_READ ) )
  { 
    started = millis();
    while( SoundFile.available())
    {
      SoundFile.read( buffer, sizeof(buffer));
      VS1011.Send32( buffer );
      ledpattern(blink_1,blink_1_onstate,blink_1_offstate,started,blink_1_on[blink_1_onstate],blink_1_off[blink_1_offstate]);
    }
    blink_1_onstate = 0;
    blink_1_offstate = 0;
    VS1011.Send2048Zeros();
    SoundFile.close();
  }
}


void ledpattern(int ledpin, int &onstate, int &offstate, long startedplaying, long on, long off)
{
  if( (onstate == offstate) && ((millis() - startedplaying) >= on)) 
  { 
    digitalWrite(ledpin, HIGH);
    onstate++;
  }

  else if((offstate < onstate) &&  ((millis() - startedplaying) >= off))
  {
    digitalWrite(ledpin, LOW);
    offstate++;
  }
}

Calling a function is pretty fast, but it does consume a little more time (slightly slower). As a general rule, the increase in code readability is well worth the tiny speed hit.

You can speed up a function call by defining it as "inline" though:

inline void ledpattern(int &onstate, int &offstate, long startedplaying, long on, long off)

Defining a function as "inline" will consume more flash, but you have plenty of flash, so this is not a problem.