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(;;)
+ ;
+}
Ned Konz