Big Mouth Billy Bass player that takes raw wavefiles and decision list text files from an SD card

Dependencies:   SDFileSystem mbed BillyBass

Revision:
0:0944c3654ded
Child:
1:f3f439154ab4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Jun 05 15:33:45 2013 +0000
@@ -0,0 +1,219 @@
+#include "mbed.h"
+#include "SDFileSystem.h"
+#include <list>
+#include <cmath>
+
+#define SD_NAME "sd"
+#define SD_ROOT "/" SD_NAME
+#define BASS_DIRECTORY SD_ROOT
+
+// Power:
+// Power GND  J9/14
+// Vin (6V)   J9/16
+
+// Digital:
+DigitalOut tail(PTA13);     // J3/2
+DigitalOut mouth(PTC12);    // J3/1
+DigitalOut head(PTC13);     // J3/3
+
+DigitalIn pushbutton(PTD5); // J3/4
+
+PwmOut redLED(LED_RED);
+PwmOut greenLED(LED_GREEN);
+PwmOut blueLED(LED_BLUE);
+
+// Analog:
+// GND   J3/14
+// VrefH J3/16
+AnalogOut speaker(PTE30);   // J10/11
+
+// PTD0 D10 – Used for CS of SPI
+// PTD2 D11 – Used for MOSI of SPI
+// PTD3 D12 – Used for MISO of SPI
+// PTC5 J1/9  Used for SCLK of SPI
+
+//              MOSI, MISO, SCLK, CS, name
+SDFileSystem sd(PTD2, PTD3, PTC5, PTD0, SD_NAME);
+Serial pc(USBTX, USBRX);
+
+typedef int16_t Sample_t;   // 16-bit raw, LE samples
+
+const size_t BUFFER_SIZE = 512;
+const float SAMPLE_RATE_HZ = 8000.0;
+const size_t SAMPLES_PER_BUFFER = BUFFER_SIZE / sizeof(Sample_t);
+const float SECONDS_PER_CHUNK = SAMPLES_PER_BUFFER / SAMPLE_RATE_HZ;
+
+struct Action {
+    float actionTime;
+    bool desiredState;
+    DigitalOut *output;
+
+    bool operator < (Action const &other) const {
+        return actionTime < other.actionTime;
+    }
+
+    bool isValid() {
+        return actionTime >= 0.0 && output != 0;
+    }
+
+    Action() : actionTime(-1.0), desiredState(false), output(0) {
+    }
+
+    bool parseLine(char const *line) {
+        // TODO
+        return true;
+    }
+};
+
+#define MAX_BASENAME_LENGTH 30
+#define MAX_ACTION_LINE_LENGTH 100
+
+struct Song {
+    char basename[ MAX_BASENAME_LENGTH + 1 ];
+    unsigned sequenceNumber;
+    unsigned whichFish;
+    std::list<Action> actions;
+
+    Song() : sequenceNumber(0), whichFish(3) {
+        basename[0] = 0;
+    }
+
+    bool readWaveFile(char const *_waveFileName) {
+        if (!parseFilename(_waveFileName, sequenceNumber, whichFish, basename)) return false;
+        char txtFileName[ FILENAME_MAX ];
+        sprintf(txtFileName, "%u_%u_%s.txt", sequenceNumber, whichFish, basename);
+        FILE *txtfile = fopen(txtFileName, "rt");
+        if (!txtfile) return false; // TODO
+        // read actions from file
+        char actionLine[ MAX_ACTION_LINE_LENGTH + 1 ];
+        while (fgets(actionLine, sizeof(actionLine), txtfile)) {
+            Action action;
+            if (action.parseLine(actionLine)) {
+                actions.push_back(action);
+            }
+        }
+        return true;
+    }
+
+    static bool parseFilename(char const *_name, unsigned &_num1, unsigned &_num2, char *_basename) {
+        char basename[ MAX_BASENAME_LENGTH + 1 ];
+        unsigned num1, num2;
+        char extension[ 4 ];
+        int nItems = sscanf(_name, "%u_%u_%30[^_.].%3[a-zA-Z]", &num1, &num2, basename, extension);
+        if (nItems != 4) return false;
+        if (num2 > 2) return false;
+        if (strcasecmp("raw", extension)) return false;
+        _num1 = num1;
+        _num2 = num2;
+        strcpy(_basename, basename);
+        return true;
+    }
+
+    // return true if filename is of proper format
+    // <num>_<num>_<name>.raw
+    // and there is a corresponding .txt file
+    static bool isValidWaveFileName(char const *_name) {
+        unsigned int num1, num2;
+        char basename[ 31 ];
+        return parseFilename(_name, num1, num2, basename);
+    }
+};
+
+class SongPlayer;
+
+struct SampleBuffer {
+    Sample_t volatile buf[ SAMPLES_PER_BUFFER ];
+    size_t volatile samplesRemaining;
+    Sample_t volatile * volatile nextSample;
+
+    bool isDone() {
+        return !samplesRemaining;
+    }
+
+    float remainingDuration() {
+        return samplesRemaining / SAMPLE_RATE_HZ;
+    }
+
+    // return true if we read any samples
+    bool loadFrom(FILE *fp) {
+        samplesRemaining = fread((void *)buf, sizeof(Sample_t), SAMPLES_PER_BUFFER, fp);
+        nextSample = buf;
+        return samplesRemaining > 0;
+    }
+
+    SampleBuffer() : samplesRemaining(0), nextSample(buf) { }
+};
+
+struct SongPlayer {
+    SampleBuffer * volatile playing;
+    SampleBuffer * volatile loading;
+    SampleBuffer buffer[2];
+    FILE *fp;
+    size_t nChunks;
+    size_t volatile chunksRemaining;
+
+    bool startSong(char const *name) {
+        if (fp) fclose(fp);
+        fp = fopen(name, "rb");
+        if (!fp) return false;
+        if (fseek(fp, 0, SEEK_END)) return false;
+        long fileSize = ftell(fp);
+        if (fileSize < 0) return false;
+        if (fseek(fp, 0, SEEK_SET)) return false;
+        chunksRemaining = nChunks = fileSize / BUFFER_SIZE;
+        loading = &buffer[0];
+        playing = &buffer[1];
+        return loadNextChunk();
+    }
+
+    // swap loading/playing buffers;
+    // decrement chunksRemaining
+    void swapBuffers() {
+        SampleBuffer * volatile tmp = playing;
+        if (tmp == buffer + 0)
+            playing = buffer + 1;
+        else
+            playing = buffer + 0;
+        loading = tmp;
+        if (chunksRemaining)
+            chunksRemaining--;
+    }
+
+    // get next chunk of file into *loading
+    // to prepare for eventual swap.
+    // returns true if more samples remain
+    bool loadNextChunk() {
+        if (! chunksRemaining) return false;
+        bool notDone = loading->loadFrom(fp);
+        return notDone;
+    }
+
+    bool isDone() {
+        return !chunksRemaining;
+    }
+
+};
+
+std::list<Song> songs;
+
+int main()
+{
+    // read the directory
+    DIR *bassDir = opendir(BASS_DIRECTORY);
+    if (bassDir) {
+        while (dirent *dir = bassDir->readdir()) {
+            pc.printf("%s", dir->d_name);
+            // if this is a valid wave filename
+            if (Song::isValidWaveFileName(dir->d_name)) {
+                pc.printf("\tvalid\r\n");
+            } else {
+                pc.printf("\tnot valid\r\n");
+            }
+        }
+    } else {
+        pc.printf("Error opening " BASS_DIRECTORY);
+    }
+
+    for(;;)
+        ;
+}