Deadlock when playing MP3s and blinking lights

I'm having an issue with my code locking up during an attempt to play an MP3 file with the Sparkfun MP3 Player Shield (SparkFun MP3 Player Shield - DEV-12660 - SparkFun Electronics) on an Arduino Mega2560. I have a box full of 30 LEDs and 18 buttons that I have to also control at the same time.

Okay. so here is the box, to give you an idea of what the physical thing is all about:
https://dl.dropbox.com/u/9161524/kiosk.JPG (these images are pretty big, don't want to throw the post width out of whack)

You hit a button in the left bank and it plays an audio clip of someone talking. You make a guess as to what city that person is from (based on their accent) and hit a corresponding button in the right bank. Various blinkenlichten and other audio clips play along the way to direct you, give you instructions, and score your responses. If you get the answer correct, the green strobe on the top flashes and the score meter on the bottom increments. If you get the answer wrong, the red strobe flashes only. You're returned to the beginning of the selection process, this time with your previous choice unavailable, until you get through all 9 tracks.

Here is a basic schematic of what how this is built. The schematic doesn't include the MP3 Shield, which is connected in the usual way to the Arduino. I had to change some of the pin connections for the shield, as the mega puts SS, MOSI, MISO, and SCK pins in a different place than the shield expects. In this case, pins 9, 11, 12, and 13 on the shield need to be wired to pins 53, 51, 50, and 52, respectively.
https://dl.dropbox.com/u/9161524/mp3-kiosk.png

My code is a little too long to post in the thread, but I will pull out excerpts that I think are important. Here is the full code: https://dl.dropbox.com/u/9161524/KioskSketch.zip

I'm using a 12v wall-wart power supply, with a 5v voltage regulator to use for power some of my LEDs and Vref for my buttons. All of the LEDs are prepackaged with the necessary resistors to have them run on 12v. The strobes also run on 12v. The buttons in the two banks are momentary, normally open switches. They have space inside of them to hold some of my LEDs (in this case, I'm running these at 5v because they are too bright at 12v for the buttons).

The MP3 Shield runs out to an amp of some kind. I'm not sure what exactly the amp is, as my project partner did that part, but the issue I'm having happened long before we integrated an amp into the circuit, running just off of the headphone jack on the MP3 shield.

The LEDs along the bottom are "score indicators", and they are running on the full 12v, with NPN BJTs switching them to ground. Whoops, forgot to draw in my schematic that each of those transistors has a 39k ohm resistor between its base pin and the Arduino pin that controls it.

The buttons are read through the analog pins via what I've been calling a "selectable voltage divider". Maybe this has a different name and I just don't know what it is called. Each button in a single column switches a different value resistor for that column. Different combinations of the buttons in that column result in distinct voltage values that I can then detect with the analog input pin and figure out which buttons were pressed. Here is the basic idea for one column of buttons:

This is an older image. That last resistor is a 4.7K, not a 1K.

The code is an implementation of a Finite State Machine. The main setup and loop manage the FSM but don't do any of the logic of the game on its own

#include <SPI.h>
#include <SdFat.h>
#include <SdFatUtil.h> 
#include <SFEMP3Shield.h>
#include "kiosk.h"
#include "attract.h"
#include "intro.h"
#include "select.h"
#include "play.h"
#include "answer.h"
#include "game_over.h"

//#define MP3_DISABLE

States currentState, nextState;
GameState game;
int stateStarted, currentFrame;
// Enter_function and body_function are function pointer types
enter_function enter[NUM_STATES] = {&enter_ATTRACT, &enter_INTRO, &enter_SELECT_TRACK, &enter_PLAY_TRACK, &enter_SELECT_REGION, &enter_PLAY_REGION, &enter_CORRECT_ANSWER, &enter_INCORRECT_ANSWER, &enter_GAME_OVER};
body_function body[NUM_STATES] = { &body_ATTRACT, &body_INTRO, &body_SELECT_TRACK, &body_PLAY_TRACK, &body_SELECT_REGION, &body_PLAY_REGION, &body_CORRECT_ANSWER, &body_INCORRECT_ANSWER, &body_GAME_OVER};


void setup() 
{
  static int i;
  currentState = NONE;
  nextState = ATTRACT;
#ifndef MP3_DISABLE  
  game.mp3.begin();
#endif
  for(i = MIN_OUTPUT_PIN; i < MAX_OUTPUT_PIN; ++i)
  {
    pinMode(i, OUTPUT);
  }	
}

void enterState()
{
  if(enter[currentState] != NULL)
    (*enter[currentState])(game);
}
States updateState(int dt)
{
  if(body[currentState] != NULL)
    return (*body[currentState])(dt, game);
  return currentState;
}
void loop() 
{
  if(nextState != currentState)
  {
#ifndef MP3_DISABLE
    if(game.mp3.isPlaying())
    {
      game.mp3.stopTrack();
    }
#endif
    allLightsOff();
    delay(500); // give the user some time to let go of the button
    currentState = nextState;
    stateStarted = millis();
    enterState();
  }
  readInput();
  currentFrame = millis();  
  nextState = updateState(currentFrame - stateStarted);
}

When I wrote the code, the hardware hadn't been finished yet, so I had some regular, resistorless LEDs that I was using to debug the display, while setting up the code to select the first available option with a few delays to simulate options being selected and let the code progress through its various states. Running in this way, the MP3s all played fine and my LEDs lit up as expected.

Building the button array and the "selectable voltage dividers", I wrote a small sketch that tested the combinations of button presses, and that worked exactly as expected. Figuring out the button combinations took some math but it's not that hard to do by testing, either.

int readBank(int column)
{
  static int voltage;
  voltage = analogRead(column);
  // these need to be selected based on the resistors in the analog button encoders
  if(voltage < 209) return 0;
  else if(voltage < 465) return 1;
  else if(voltage < 577) return 2;
  else if(voltage < 670) return 3;
  else if(voltage < 727) return 4;
  else if(voltage < 766) return 5;
  else if(voltage < 794) return	 6;
  return 7;
}

This has to be ran for each of the 6 columns of buttons, connected to one each of the analog input pins, in this case 8 through 13, because the MP3 shield covered up pins the first 8 pins.

Integrating the button and score LEDs into the circuit, I wrote a small sketch that just lit each light in sequence, then lit all of them and turned each off in sequence. That worked exactly as expected as well. The LEDs inside of the buttons are only ran at 5v, both because they were too bright at 12v and because my project partner and I didn't want to wire up transistors for each of them. Looking back now, the transistors should have been used anyway, just to keep everything consistent, and still run the LEDs on 5v. I'm afraid my issue is related to too much current draw from doing these LEDs in this way, but not really sure and don't really know how to properly test this idea. Because of this, the code for turning the LEDs on and off is a little different than normal

void ledOff(int p)
{
  // this sets the pin into a high-impedance state, essentially "disconnecting" it from the circuit
  pinMode(p, INPUT);
  digitalWrite(p, LOW);
}
void ledOn(int p)
{
  pinMode(p, OUTPUT);
  //S1 is the first score indicator LED, which is controlled by a BJT instead of directly
  digitalWrite(p, p < S1 ? LOW : HIGH); 
}

In the case of the button LEDs, I'm grounding the desired LED through the Arduino to turn it on, then "disconnecting" the LED cathode to turn it off. It's not pretty, should have done the BJT, but it works okay for now.

The first state in the FSM is ATTRACT. It animates the lights and plays an audio clip to try to draw attention to itself (this is going to be setup in a room full of people in an art gallery). ATTRACT mode starts up fine, animates as expected, and plays the MP3 as expected. The ATTRACT code is pretty simple, it's just running back and forth through all of the LEDs, turning them on and off in sequence:

#include <arduino.h>
#include <SFEMP3Shield.h>
#include "attract.h"
#include "kiosk.h"

unsigned int ATTRACT_currentFrame;
unsigned int ATTRACT_lastFrame;

void enter_ATTRACT(GameState& game)
{
  ATTRACT_currentFrame = MIN_OUTPUT_PIN;
  ATTRACT_lastFrame = 0;
  // strobes run on their own, just turn them on.
  lightButton(2, 12);
  lightButton(2, 13);
#ifndef MP3_DISABLE
  game.mp3.playMP3("attract.mp3");
#endif
}

States body_ATTRACT(int dt, GameState& game)
{
  States next = ATTRACT;
  if((dt - ATTRACT_lastFrame) >= ATTRACT_MILLIS_PER_FRAME)
  {
    ATTRACT_lastFrame = dt;
    ledOff(ATTRACT_currentFrame);
    ++ATTRACT_currentFrame;
    if(ATTRACT_currentFrame > S12)
      ATTRACT_currentFrame = MIN_OUTPUT_PIN;
    ledOn(ATTRACT_currentFrame);
  }
  if(wasAnyButtonPressed())
  {
    next = INTRO;
  }
  return next;
}

whoops, hit the character limit. Continued...

...continued

Hitting any button in attract mode switches the state to INTRO. INTRO initializes the game state, plays an MP3 file that gives instructions to the user on how to run the machine, and waits for the user to hit a button. INTRO used to play the MP3 fine, until I hooked up all of the hardware, now it plays nothing, but the SFEMP3Shield object reports that the track played fine (0 return value from SFEMP3Shield::playMP3(char*)). The INTRO code is really simple:

#include <arduino.h>
#include "intro.h"
#include "kiosk.h"

void enter_INTRO(GameState& game)
{
  static int track;
  game.score = 0;
  for(track = 0; track < NUM_TRACKS; ++track)
    game.trackPlayed[track] = false;
  game.tracksPlayed = 0;
#ifndef MP3_DISABLE
  game.mp3.playMP3("intro.mp3");
#endif
}

States body_INTRO(int dt, GameState& game)
{
  if(wasAnyButtonPressed()
#ifndef MP3_DISABLE
  || game.mp3.isPlaying()
#endif
  )
  {
    return SELECT_TRACK;
  }
  return INTRO;
}

Once the button is hit, it goes into SELECT TRACK. It's supposed to light up all of the buttons in the first bank, play an audio track of a person saying "select a track", and then wait for the user to hit a button. Instead, it lights up the first column of buttons and then freezes in the middle of calling SFEMP3Shield::playMP3. It specifically freezes on a line in which the library calls attachInterrupt to wait for the MP3 shield to request more data to load off of the SD card.

I read a few posts about serial output and some interrupts possibly competing for resources and creating a potential race condition, so I removed all of the serial output, but that did nothing.

As you can see in some of the code, I have a precompiler directive to switch on and off actually doing anything with the MP3 shield. Disabling the MP3 code does nothing to get past the deadlock, it still freezes in the SELECT TRACK state. I don't know exactly where in the code it is freezing yet, as I was trying to debug this over the phone with my project partner. But the fact that it still freezes in the same general location withouth that call to attachInterrupt drives me up a wall.

I'm clueless at this point. We've rechecked all of the hardware and it works fine when controlled by other sketches. It seems like there is something in this code in particular that is making it freeze. I'm of half a mind to rewrite the code from the ground up, this time with just Switches in place of the function-pointer based FSM.