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

Dependencies:   SDFileSystem mbed BillyBass

main.cpp

Committer:
bikeNomad
Date:
2013-06-05
Revision:
0:0944c3654ded
Child:
1:f3f439154ab4

File content as of revision 0:0944c3654ded:

#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(;;)
        ;
}