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

Dependencies:   SDFileSystem mbed BillyBass

Revision:
5:5b846ef42702
Parent:
4:babc37764bd3
Child:
6:e90a12ca056f
--- a/main.cpp	Fri Jun 14 05:08:54 2013 +0000
+++ b/main.cpp	Sat Jun 15 03:32:20 2013 +0000
@@ -1,20 +1,16 @@
-#include "mbed.h"
-#include "SDFileSystem.h"
-#include <list>
-#include <cmath>
-
-#define SD_NAME "sd"
-#define SD_ROOT "/" SD_NAME
-#define BASS_DIRECTORY SD_ROOT "/SD_Files"
+#include "billybass.hpp"
+#include "song.hpp"
+#include "player.hpp"
+#include "action.hpp"
 
 // 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
+DigitalOut tail(PTA13);                // J3/2
+DigitalOut mouth(PTC12);               // J3/1
+DigitalOut head(PTC13);                // J3/3
 
 DigitalIn pushbutton(PTD5); // J3/4
 
@@ -25,259 +21,48 @@
 // Analog:
 // GND   J3/14
 // VrefH J3/16
-AnalogOut speaker(PTE30);   // J10/11
+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
+// 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 unsigned SAMPLE_PERIOD_USEC = (unsigned)(1.0e6/SAMPLE_RATE_HZ);
-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, BASS_DIRECTORY "/%u_%u_%s", &num1, &num2, basename);
-        if (nItems != 3) {
-            pc.printf(" nItems=%d\r\n", nItems);
-            return false;
-        }
-        char *p = strrchr(basename, '.');
-        if (!p) {
-            pc.printf(" no extension\r\n");
-            return false;
-        }
-        strcpy(extension, p+1);
-        *p = 0;
-        if (num2 > 2) {
-            pc.printf(" num2=%d\r\n", num2);
-            return false;
-        }
-        if (strcasecmp("raw", extension)) {
-            pc.printf(" ext=%s\r\n", 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;
-    }
-
-    Sample_t getNextSample() {
-        --samplesRemaining;
-        return *++nextSample;
-    }
-
-    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;
-    float timeInSong;
-    Ticker sampleTicker;
-
-    SongPlayer() : playing(0), loading(0), fp(0), nChunks(0), chunksRemaining(0)
-    {}
-
-    // interrupt handler
-    void playNextSample(void) {
-        if (playing->samplesRemaining == 0)
-            swapBuffers();
-        // NOTE bias of 0xC000 requires normalizing to 75% of full scale
-        speaker.write_u16(static_cast<uint16_t>(playing->getNextSample() + 0x8000) / 2);
-    }
-
-    bool startSong(char const *name) {
-        pc.printf("starting %s: ", name);
-        if (fp) fclose(fp);
-        fp = fopen(name, "rb");
-        pc.printf("opened, ");
-        if (!fp)  return false;
-        pc.printf("seekend, ");
-        if (fseek(fp, 0, SEEK_END)) return false;
-        long fileSize = ftell(fp);
-        pc.printf("size=%d, ", fileSize);
-        if (fileSize < 0) return false;
-        if (fseek(fp, 0, SEEK_SET)) return false;
-        pc.printf("rewound, ");
-        chunksRemaining = nChunks = fileSize / BUFFER_SIZE;
-        loading = &buffer[0];
-        playing = &buffer[1];
-        pc.printf("chunks=%d expected=%f seconds\r\n", nChunks, nChunks * SECONDS_PER_CHUNK);
-        if (! loadNextChunk())
-            return false;
-        timeInSong = 0.0;
-        sampleTicker.attach_us(this, &SongPlayer::playNextSample, SAMPLE_PERIOD_USEC);
-        return true;
-    }
-
-    // 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;
-    }
-
-    // look at loading buffer; load only if necessary.
-    bool loadIfNecessary() {
-        if (loading->isDone()) {
-            timeInSong += SECONDS_PER_CHUNK;
-            return loadNextChunk();
-        } else return true;
-    }
-
-    void playEntireSong(char const *name) {
-        if (!startSong(name)) return;
-        while (!isDone()) {
-            loadIfNecessary();
-        }
-        sampleTicker.detach();
-    }
-};
-
 std::list<Song> songs;
 
 int main()
 {
     SongPlayer player;
-    pc.baud(115200);
+    pc.baud(SERIAL_BAUD);
 
     // read the directory
     DIR *bassDir = opendir(BASS_DIRECTORY);
-    if (bassDir) {
-        while (dirent *dir = bassDir->readdir()) {
+    if (bassDir)
+    {
+        while (dirent *dir = bassDir->readdir())
+        {
             char fn[ 60 ];
             snprintf(fn, sizeof(fn), "%s/%s", BASS_DIRECTORY, dir->d_name);
             pc.printf(fn);
             // if this is a valid wave filename
-            if (Song::isValidWaveFileName(fn)) {
+            if (Song::isValidWaveFileName(fn))
+            {
                 pc.printf("\tvalid\r\n");
                 player.playEntireSong(fn);
                 pc.printf("Song time: %f\r\n", player.timeInSong);
-            } else {
+            }
+            else
+            {
                 pc.printf("\tnot valid\r\n");
             }
         }
-    } else {
+    }
+    else
+    {
         pc.printf("Error opening " BASS_DIRECTORY);
     }
 }