OK Here's my idea. Basically all of your flashes will be defined in a big array,
For every flash we store:
The pin that the LED is attached to
The start time (in milliseconds from the start of the song
The attack (this is how long in milliseconds that it takes to reach full power from the start of the flash)
The decay (this is how long, it takes in milliseconds to fade out back out again.)
It's still a bit rough but with some modifications we could also include routines for regular patterns (instead of single flashes).
We could also build a little interface to get our input from a midi keyboard, rather than tediously defining every single flash
It doesn't matter which pins you use, as long as you don't go over 15 distinct pins. The data I've put in should (very approximately) flash "bah bah black sheep have you any wool" (on pin 12)
//This just defines the structure that each LED event takes
typedef struct
{
int pin;
unsigned long when;//millis from start of song
unsigned long attack;//how quick to go from 0 to full brightness
unsigned long decay;//how long to take to go from full power back to 0
}
LedFlash;
void getLEDEvent(int index,LedFlash* pointer);
//this holds the status activity of each LED
//we initialise it here to ALL leds inactive
LedFlash channel[15]={
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0,
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0
};
//This is where the data for all the flashes goes
//I've only put a bit of sample data in and it's just using pin 13 (because I haven't got any more LEDS handy)
const LedFlash PROGMEM song[] = {
//Pin, When, fadeUp, FadeDown
13, 2000, 100, 400,
13, 2500, 100, 400,
13, 3000, 100, 400,
13, 3500, 100, 400,
13, 4000, 50, 190,
13, 4250, 50, 190,
13, 4500, 50, 190,
13, 4750, 50, 190,
13, 5000, 400, 900,
//Add more lines like those above using your own pins and data
//The next two lines are the end marker of the song
0,0,0,0
}
;//End of song data
void setup()
{
}
//Bookmark used to index through the flash events
int songIndex=0;
void loop()
{
int n;
LedFlash tempFlash;
unsigned long t=millis();
getLEDEvent(songIndex,&tempFlash);
while((tempFlash.when<t)&&(tempFlash.pin))
{//find a vacant channel (or one already used for this pin) for this flash
for(n=0;n<15;n++)
if((channel[n].pin==0)||(channel[n].pin==tempFlash.pin))//or a channel being used by this pin
{
channel[n]=tempFlash;
break;
}
getLEDEvent(++songIndex,&tempFlash);
}
maintainFades();
}
//This function moves a flash event from PROGMEM space into ram
void getLEDEvent(int index,LedFlash* pointer)
{
pointer->pin=pgm_read_byte(&(song[songIndex].pin));
pointer->when=pgm_read_word(&(song[songIndex].when));
pointer->attack=pgm_read_word(&(song[songIndex].attack));
pointer->decay=pgm_read_word(&(song[songIndex].decay));
}
//This function works out how bright every LED should be at the moment and updates them
void maintainFades()
{
byte brightness=0;
unsigned long elapsed;
unsigned long t=millis();
int n;
for(n=0;n<15;n++){
if(channel[n].pin)
{
if (t>channel[n].when)//check if this flash has started yet
{
elapsed=t-channel[n].when;//work out how long ago this flash started
if(elapsed<channel[n].attack)//if it hasn't reached full power yet
{
brightness=map(elapsed,0,channel[n].attack,0,255);//work out it's current power
}
else
{
elapsed -= channel[n].attack;//work out when it started fading
if(elapsed < channel[n].decay) //if it hasn't finished fading yet
{
brightness=map(elapsed,0,channel[n].decay,255,0);//work out how bright it should be now
}
}
analogWrite(channel[n].pin,brightness);
}//end of if flash started
}//end if(channel pin)
}//end for loop
}
BTW Each flash is independent so you can have flashes on separate pins starting at the same time, or overlapping with each other.