Big Mouth Billy Bass player that takes raw wavefiles and decision list text files from an SD card
Dependencies: SDFileSystem mbed BillyBass
Diff: main.cpp
- 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(;;) + ; +}