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

Dependencies:   SDFileSystem mbed BillyBass

Files at this revision

API Documentation at this revision

Comitter:
bikeNomad
Date:
Wed Jun 05 15:33:45 2013 +0000
Child:
1:f3f439154ab4
Commit message:
first attempt; still incomplete

Changed in this revision

SDFileSystem.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SDFileSystem.lib	Wed Jun 05 15:33:45 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/SDFileSystem/#c8f66dc765d4
--- /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(;;)
+        ;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Jun 05 15:33:45 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/b3110cd2dd17
\ No newline at end of file