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

Dependencies:   SDFileSystem mbed BillyBass

Committer:
bikeNomad
Date:
Fri Jun 07 03:36:00 2013 +0000
Revision:
2:f96989f2b8a1
Parent:
1:f3f439154ab4
Child:
3:00b4c1aadd30
changed root of SD to /SD_Files

Who changed what in which revision?

UserRevisionLine numberNew contents of line
bikeNomad 0:0944c3654ded 1 #include "mbed.h"
bikeNomad 0:0944c3654ded 2 #include "SDFileSystem.h"
bikeNomad 0:0944c3654ded 3 #include <list>
bikeNomad 0:0944c3654ded 4 #include <cmath>
bikeNomad 0:0944c3654ded 5
bikeNomad 0:0944c3654ded 6 #define SD_NAME "sd"
bikeNomad 0:0944c3654ded 7 #define SD_ROOT "/" SD_NAME
bikeNomad 2:f96989f2b8a1 8 #define BASS_DIRECTORY SD_ROOT "/SD_Files"
bikeNomad 0:0944c3654ded 9
bikeNomad 0:0944c3654ded 10 // Power:
bikeNomad 0:0944c3654ded 11 // Power GND J9/14
bikeNomad 0:0944c3654ded 12 // Vin (6V) J9/16
bikeNomad 0:0944c3654ded 13
bikeNomad 0:0944c3654ded 14 // Digital:
bikeNomad 0:0944c3654ded 15 DigitalOut tail(PTA13); // J3/2
bikeNomad 0:0944c3654ded 16 DigitalOut mouth(PTC12); // J3/1
bikeNomad 0:0944c3654ded 17 DigitalOut head(PTC13); // J3/3
bikeNomad 0:0944c3654ded 18
bikeNomad 0:0944c3654ded 19 DigitalIn pushbutton(PTD5); // J3/4
bikeNomad 0:0944c3654ded 20
bikeNomad 0:0944c3654ded 21 PwmOut redLED(LED_RED);
bikeNomad 0:0944c3654ded 22 PwmOut greenLED(LED_GREEN);
bikeNomad 0:0944c3654ded 23 PwmOut blueLED(LED_BLUE);
bikeNomad 0:0944c3654ded 24
bikeNomad 0:0944c3654ded 25 // Analog:
bikeNomad 0:0944c3654ded 26 // GND J3/14
bikeNomad 0:0944c3654ded 27 // VrefH J3/16
bikeNomad 0:0944c3654ded 28 AnalogOut speaker(PTE30); // J10/11
bikeNomad 0:0944c3654ded 29
bikeNomad 0:0944c3654ded 30 // PTD0 D10 – Used for CS of SPI
bikeNomad 0:0944c3654ded 31 // PTD2 D11 – Used for MOSI of SPI
bikeNomad 0:0944c3654ded 32 // PTD3 D12 – Used for MISO of SPI
bikeNomad 0:0944c3654ded 33 // PTC5 J1/9 Used for SCLK of SPI
bikeNomad 0:0944c3654ded 34
bikeNomad 0:0944c3654ded 35 // MOSI, MISO, SCLK, CS, name
bikeNomad 0:0944c3654ded 36 SDFileSystem sd(PTD2, PTD3, PTC5, PTD0, SD_NAME);
bikeNomad 0:0944c3654ded 37 Serial pc(USBTX, USBRX);
bikeNomad 0:0944c3654ded 38
bikeNomad 0:0944c3654ded 39 typedef int16_t Sample_t; // 16-bit raw, LE samples
bikeNomad 0:0944c3654ded 40
bikeNomad 0:0944c3654ded 41 const size_t BUFFER_SIZE = 512;
bikeNomad 0:0944c3654ded 42 const float SAMPLE_RATE_HZ = 8000.0;
bikeNomad 1:f3f439154ab4 43 const unsigned SAMPLE_PERIOD_USEC = (unsigned)(1.0e6/SAMPLE_RATE_HZ);
bikeNomad 0:0944c3654ded 44 const size_t SAMPLES_PER_BUFFER = BUFFER_SIZE / sizeof(Sample_t);
bikeNomad 0:0944c3654ded 45 const float SECONDS_PER_CHUNK = SAMPLES_PER_BUFFER / SAMPLE_RATE_HZ;
bikeNomad 0:0944c3654ded 46
bikeNomad 0:0944c3654ded 47 struct Action {
bikeNomad 0:0944c3654ded 48 float actionTime;
bikeNomad 0:0944c3654ded 49 bool desiredState;
bikeNomad 0:0944c3654ded 50 DigitalOut *output;
bikeNomad 0:0944c3654ded 51
bikeNomad 0:0944c3654ded 52 bool operator < (Action const &other) const {
bikeNomad 0:0944c3654ded 53 return actionTime < other.actionTime;
bikeNomad 0:0944c3654ded 54 }
bikeNomad 0:0944c3654ded 55
bikeNomad 0:0944c3654ded 56 bool isValid() {
bikeNomad 0:0944c3654ded 57 return actionTime >= 0.0 && output != 0;
bikeNomad 0:0944c3654ded 58 }
bikeNomad 0:0944c3654ded 59
bikeNomad 0:0944c3654ded 60 Action() : actionTime(-1.0), desiredState(false), output(0) {
bikeNomad 0:0944c3654ded 61 }
bikeNomad 0:0944c3654ded 62
bikeNomad 0:0944c3654ded 63 bool parseLine(char const *line) {
bikeNomad 0:0944c3654ded 64 // TODO
bikeNomad 0:0944c3654ded 65 return true;
bikeNomad 0:0944c3654ded 66 }
bikeNomad 0:0944c3654ded 67 };
bikeNomad 0:0944c3654ded 68
bikeNomad 0:0944c3654ded 69 #define MAX_BASENAME_LENGTH 30
bikeNomad 0:0944c3654ded 70 #define MAX_ACTION_LINE_LENGTH 100
bikeNomad 0:0944c3654ded 71
bikeNomad 0:0944c3654ded 72 struct Song {
bikeNomad 0:0944c3654ded 73 char basename[ MAX_BASENAME_LENGTH + 1 ];
bikeNomad 0:0944c3654ded 74 unsigned sequenceNumber;
bikeNomad 0:0944c3654ded 75 unsigned whichFish;
bikeNomad 0:0944c3654ded 76 std::list<Action> actions;
bikeNomad 0:0944c3654ded 77
bikeNomad 0:0944c3654ded 78 Song() : sequenceNumber(0), whichFish(3) {
bikeNomad 0:0944c3654ded 79 basename[0] = 0;
bikeNomad 0:0944c3654ded 80 }
bikeNomad 0:0944c3654ded 81
bikeNomad 0:0944c3654ded 82 bool readWaveFile(char const *_waveFileName) {
bikeNomad 0:0944c3654ded 83 if (!parseFilename(_waveFileName, sequenceNumber, whichFish, basename)) return false;
bikeNomad 0:0944c3654ded 84 char txtFileName[ FILENAME_MAX ];
bikeNomad 0:0944c3654ded 85 sprintf(txtFileName, "%u_%u_%s.txt", sequenceNumber, whichFish, basename);
bikeNomad 0:0944c3654ded 86 FILE *txtfile = fopen(txtFileName, "rt");
bikeNomad 0:0944c3654ded 87 if (!txtfile) return false; // TODO
bikeNomad 0:0944c3654ded 88 // read actions from file
bikeNomad 0:0944c3654ded 89 char actionLine[ MAX_ACTION_LINE_LENGTH + 1 ];
bikeNomad 0:0944c3654ded 90 while (fgets(actionLine, sizeof(actionLine), txtfile)) {
bikeNomad 0:0944c3654ded 91 Action action;
bikeNomad 0:0944c3654ded 92 if (action.parseLine(actionLine)) {
bikeNomad 0:0944c3654ded 93 actions.push_back(action);
bikeNomad 0:0944c3654ded 94 }
bikeNomad 0:0944c3654ded 95 }
bikeNomad 0:0944c3654ded 96 return true;
bikeNomad 0:0944c3654ded 97 }
bikeNomad 0:0944c3654ded 98
bikeNomad 0:0944c3654ded 99 static bool parseFilename(char const *_name, unsigned &_num1, unsigned &_num2, char *_basename) {
bikeNomad 0:0944c3654ded 100 char basename[ MAX_BASENAME_LENGTH + 1 ];
bikeNomad 0:0944c3654ded 101 unsigned num1, num2;
bikeNomad 0:0944c3654ded 102 char extension[ 4 ];
bikeNomad 0:0944c3654ded 103 int nItems = sscanf(_name, "%u_%u_%30[^_.].%3[a-zA-Z]", &num1, &num2, basename, extension);
bikeNomad 0:0944c3654ded 104 if (nItems != 4) return false;
bikeNomad 0:0944c3654ded 105 if (num2 > 2) return false;
bikeNomad 0:0944c3654ded 106 if (strcasecmp("raw", extension)) return false;
bikeNomad 0:0944c3654ded 107 _num1 = num1;
bikeNomad 0:0944c3654ded 108 _num2 = num2;
bikeNomad 0:0944c3654ded 109 strcpy(_basename, basename);
bikeNomad 0:0944c3654ded 110 return true;
bikeNomad 0:0944c3654ded 111 }
bikeNomad 0:0944c3654ded 112
bikeNomad 0:0944c3654ded 113 // return true if filename is of proper format
bikeNomad 0:0944c3654ded 114 // <num>_<num>_<name>.raw
bikeNomad 0:0944c3654ded 115 // and there is a corresponding .txt file
bikeNomad 0:0944c3654ded 116 static bool isValidWaveFileName(char const *_name) {
bikeNomad 0:0944c3654ded 117 unsigned int num1, num2;
bikeNomad 0:0944c3654ded 118 char basename[ 31 ];
bikeNomad 0:0944c3654ded 119 return parseFilename(_name, num1, num2, basename);
bikeNomad 0:0944c3654ded 120 }
bikeNomad 0:0944c3654ded 121 };
bikeNomad 0:0944c3654ded 122
bikeNomad 0:0944c3654ded 123 class SongPlayer;
bikeNomad 0:0944c3654ded 124
bikeNomad 0:0944c3654ded 125 struct SampleBuffer {
bikeNomad 0:0944c3654ded 126 Sample_t volatile buf[ SAMPLES_PER_BUFFER ];
bikeNomad 0:0944c3654ded 127 size_t volatile samplesRemaining;
bikeNomad 0:0944c3654ded 128 Sample_t volatile * volatile nextSample;
bikeNomad 0:0944c3654ded 129
bikeNomad 0:0944c3654ded 130 bool isDone() {
bikeNomad 0:0944c3654ded 131 return !samplesRemaining;
bikeNomad 0:0944c3654ded 132 }
bikeNomad 0:0944c3654ded 133
bikeNomad 0:0944c3654ded 134 float remainingDuration() {
bikeNomad 0:0944c3654ded 135 return samplesRemaining / SAMPLE_RATE_HZ;
bikeNomad 0:0944c3654ded 136 }
bikeNomad 0:0944c3654ded 137
bikeNomad 0:0944c3654ded 138 // return true if we read any samples
bikeNomad 0:0944c3654ded 139 bool loadFrom(FILE *fp) {
bikeNomad 0:0944c3654ded 140 samplesRemaining = fread((void *)buf, sizeof(Sample_t), SAMPLES_PER_BUFFER, fp);
bikeNomad 0:0944c3654ded 141 nextSample = buf;
bikeNomad 0:0944c3654ded 142 return samplesRemaining > 0;
bikeNomad 0:0944c3654ded 143 }
bikeNomad 0:0944c3654ded 144
bikeNomad 1:f3f439154ab4 145 Sample_t getNextSample() {
bikeNomad 1:f3f439154ab4 146 --samplesRemaining;
bikeNomad 1:f3f439154ab4 147 return *++nextSample;
bikeNomad 1:f3f439154ab4 148 }
bikeNomad 1:f3f439154ab4 149
bikeNomad 0:0944c3654ded 150 SampleBuffer() : samplesRemaining(0), nextSample(buf) { }
bikeNomad 0:0944c3654ded 151 };
bikeNomad 0:0944c3654ded 152
bikeNomad 0:0944c3654ded 153 struct SongPlayer {
bikeNomad 0:0944c3654ded 154 SampleBuffer * volatile playing;
bikeNomad 0:0944c3654ded 155 SampleBuffer * volatile loading;
bikeNomad 0:0944c3654ded 156 SampleBuffer buffer[2];
bikeNomad 0:0944c3654ded 157 FILE *fp;
bikeNomad 0:0944c3654ded 158 size_t nChunks;
bikeNomad 0:0944c3654ded 159 size_t volatile chunksRemaining;
bikeNomad 1:f3f439154ab4 160 Ticker sampleTicker;
bikeNomad 1:f3f439154ab4 161
bikeNomad 1:f3f439154ab4 162 // interrupt handler
bikeNomad 1:f3f439154ab4 163 void playNextSample(void) {
bikeNomad 1:f3f439154ab4 164 if (!playing->samplesRemaining)
bikeNomad 1:f3f439154ab4 165 swapBuffers();
bikeNomad 1:f3f439154ab4 166 // TODO change bias to 0xC000 (requires normalizing to 75% of full scale)
bikeNomad 1:f3f439154ab4 167 speaker.write_u16(static_cast<uint16_t>(playing->getNextSample() + 0x8000));
bikeNomad 1:f3f439154ab4 168 }
bikeNomad 0:0944c3654ded 169
bikeNomad 0:0944c3654ded 170 bool startSong(char const *name) {
bikeNomad 0:0944c3654ded 171 if (fp) fclose(fp);
bikeNomad 0:0944c3654ded 172 fp = fopen(name, "rb");
bikeNomad 0:0944c3654ded 173 if (!fp) return false;
bikeNomad 0:0944c3654ded 174 if (fseek(fp, 0, SEEK_END)) return false;
bikeNomad 0:0944c3654ded 175 long fileSize = ftell(fp);
bikeNomad 0:0944c3654ded 176 if (fileSize < 0) return false;
bikeNomad 0:0944c3654ded 177 if (fseek(fp, 0, SEEK_SET)) return false;
bikeNomad 0:0944c3654ded 178 chunksRemaining = nChunks = fileSize / BUFFER_SIZE;
bikeNomad 0:0944c3654ded 179 loading = &buffer[0];
bikeNomad 0:0944c3654ded 180 playing = &buffer[1];
bikeNomad 1:f3f439154ab4 181 if (! loadNextChunk())
bikeNomad 1:f3f439154ab4 182 return false;
bikeNomad 1:f3f439154ab4 183 sampleTicker.attach_us(this, &SongPlayer::playNextSample, SAMPLE_PERIOD_USEC);
bikeNomad 1:f3f439154ab4 184 return true;
bikeNomad 0:0944c3654ded 185 }
bikeNomad 0:0944c3654ded 186
bikeNomad 0:0944c3654ded 187 // swap loading/playing buffers;
bikeNomad 0:0944c3654ded 188 // decrement chunksRemaining
bikeNomad 0:0944c3654ded 189 void swapBuffers() {
bikeNomad 0:0944c3654ded 190 SampleBuffer * volatile tmp = playing;
bikeNomad 0:0944c3654ded 191 if (tmp == buffer + 0)
bikeNomad 0:0944c3654ded 192 playing = buffer + 1;
bikeNomad 0:0944c3654ded 193 else
bikeNomad 0:0944c3654ded 194 playing = buffer + 0;
bikeNomad 0:0944c3654ded 195 loading = tmp;
bikeNomad 0:0944c3654ded 196 if (chunksRemaining)
bikeNomad 0:0944c3654ded 197 chunksRemaining--;
bikeNomad 0:0944c3654ded 198 }
bikeNomad 0:0944c3654ded 199
bikeNomad 0:0944c3654ded 200 // get next chunk of file into *loading
bikeNomad 0:0944c3654ded 201 // to prepare for eventual swap.
bikeNomad 0:0944c3654ded 202 // returns true if more samples remain
bikeNomad 0:0944c3654ded 203 bool loadNextChunk() {
bikeNomad 0:0944c3654ded 204 if (! chunksRemaining) return false;
bikeNomad 0:0944c3654ded 205 bool notDone = loading->loadFrom(fp);
bikeNomad 0:0944c3654ded 206 return notDone;
bikeNomad 0:0944c3654ded 207 }
bikeNomad 0:0944c3654ded 208
bikeNomad 0:0944c3654ded 209 bool isDone() {
bikeNomad 0:0944c3654ded 210 return !chunksRemaining;
bikeNomad 0:0944c3654ded 211 }
bikeNomad 0:0944c3654ded 212
bikeNomad 1:f3f439154ab4 213 // look at loading buffer; load only if necessary.
bikeNomad 1:f3f439154ab4 214 bool loadIfNecessary() {
bikeNomad 1:f3f439154ab4 215 if (loading->isDone()) {
bikeNomad 1:f3f439154ab4 216 return loadNextChunk();
bikeNomad 1:f3f439154ab4 217 } else return true;
bikeNomad 1:f3f439154ab4 218 }
bikeNomad 0:0944c3654ded 219 };
bikeNomad 0:0944c3654ded 220
bikeNomad 0:0944c3654ded 221 std::list<Song> songs;
bikeNomad 0:0944c3654ded 222
bikeNomad 0:0944c3654ded 223 int main()
bikeNomad 0:0944c3654ded 224 {
bikeNomad 0:0944c3654ded 225 // read the directory
bikeNomad 0:0944c3654ded 226 DIR *bassDir = opendir(BASS_DIRECTORY);
bikeNomad 0:0944c3654ded 227 if (bassDir) {
bikeNomad 0:0944c3654ded 228 while (dirent *dir = bassDir->readdir()) {
bikeNomad 0:0944c3654ded 229 pc.printf("%s", dir->d_name);
bikeNomad 0:0944c3654ded 230 // if this is a valid wave filename
bikeNomad 0:0944c3654ded 231 if (Song::isValidWaveFileName(dir->d_name)) {
bikeNomad 0:0944c3654ded 232 pc.printf("\tvalid\r\n");
bikeNomad 0:0944c3654ded 233 } else {
bikeNomad 0:0944c3654ded 234 pc.printf("\tnot valid\r\n");
bikeNomad 0:0944c3654ded 235 }
bikeNomad 0:0944c3654ded 236 }
bikeNomad 0:0944c3654ded 237 } else {
bikeNomad 0:0944c3654ded 238 pc.printf("Error opening " BASS_DIRECTORY);
bikeNomad 0:0944c3654ded 239 }
bikeNomad 0:0944c3654ded 240
bikeNomad 0:0944c3654ded 241 for(;;)
bikeNomad 0:0944c3654ded 242 ;
bikeNomad 0:0944c3654ded 243 }