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 14 05:08:54 2013 +0000
Revision:
4:babc37764bd3
Parent:
3:00b4c1aadd30
Child:
5:5b846ef42702
Made audio playing work.

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 4:babc37764bd3 103 int nItems = sscanf(_name, BASS_DIRECTORY "/%u_%u_%s", &num1, &num2, basename);
bikeNomad 4:babc37764bd3 104 if (nItems != 3) {
bikeNomad 4:babc37764bd3 105 pc.printf(" nItems=%d\r\n", nItems);
bikeNomad 4:babc37764bd3 106 return false;
bikeNomad 4:babc37764bd3 107 }
bikeNomad 3:00b4c1aadd30 108 char *p = strrchr(basename, '.');
bikeNomad 4:babc37764bd3 109 if (!p) {
bikeNomad 4:babc37764bd3 110 pc.printf(" no extension\r\n");
bikeNomad 4:babc37764bd3 111 return false;
bikeNomad 4:babc37764bd3 112 }
bikeNomad 3:00b4c1aadd30 113 strcpy(extension, p+1);
bikeNomad 3:00b4c1aadd30 114 *p = 0;
bikeNomad 4:babc37764bd3 115 if (num2 > 2) {
bikeNomad 4:babc37764bd3 116 pc.printf(" num2=%d\r\n", num2);
bikeNomad 4:babc37764bd3 117 return false;
bikeNomad 4:babc37764bd3 118 }
bikeNomad 4:babc37764bd3 119 if (strcasecmp("raw", extension)) {
bikeNomad 4:babc37764bd3 120 pc.printf(" ext=%s\r\n", extension);
bikeNomad 4:babc37764bd3 121 return false;
bikeNomad 4:babc37764bd3 122 }
bikeNomad 0:0944c3654ded 123 _num1 = num1;
bikeNomad 0:0944c3654ded 124 _num2 = num2;
bikeNomad 0:0944c3654ded 125 strcpy(_basename, basename);
bikeNomad 0:0944c3654ded 126 return true;
bikeNomad 0:0944c3654ded 127 }
bikeNomad 0:0944c3654ded 128
bikeNomad 0:0944c3654ded 129 // return true if filename is of proper format
bikeNomad 0:0944c3654ded 130 // <num>_<num>_<name>.raw
bikeNomad 0:0944c3654ded 131 // and there is a corresponding .txt file
bikeNomad 0:0944c3654ded 132 static bool isValidWaveFileName(char const *_name) {
bikeNomad 0:0944c3654ded 133 unsigned int num1, num2;
bikeNomad 0:0944c3654ded 134 char basename[ 31 ];
bikeNomad 0:0944c3654ded 135 return parseFilename(_name, num1, num2, basename);
bikeNomad 0:0944c3654ded 136 }
bikeNomad 0:0944c3654ded 137 };
bikeNomad 0:0944c3654ded 138
bikeNomad 0:0944c3654ded 139 class SongPlayer;
bikeNomad 0:0944c3654ded 140
bikeNomad 0:0944c3654ded 141 struct SampleBuffer {
bikeNomad 0:0944c3654ded 142 Sample_t volatile buf[ SAMPLES_PER_BUFFER ];
bikeNomad 0:0944c3654ded 143 size_t volatile samplesRemaining;
bikeNomad 0:0944c3654ded 144 Sample_t volatile * volatile nextSample;
bikeNomad 0:0944c3654ded 145
bikeNomad 0:0944c3654ded 146 bool isDone() {
bikeNomad 0:0944c3654ded 147 return !samplesRemaining;
bikeNomad 0:0944c3654ded 148 }
bikeNomad 0:0944c3654ded 149
bikeNomad 0:0944c3654ded 150 float remainingDuration() {
bikeNomad 0:0944c3654ded 151 return samplesRemaining / SAMPLE_RATE_HZ;
bikeNomad 0:0944c3654ded 152 }
bikeNomad 0:0944c3654ded 153
bikeNomad 0:0944c3654ded 154 // return true if we read any samples
bikeNomad 0:0944c3654ded 155 bool loadFrom(FILE *fp) {
bikeNomad 0:0944c3654ded 156 samplesRemaining = fread((void *)buf, sizeof(Sample_t), SAMPLES_PER_BUFFER, fp);
bikeNomad 0:0944c3654ded 157 nextSample = buf;
bikeNomad 0:0944c3654ded 158 return samplesRemaining > 0;
bikeNomad 0:0944c3654ded 159 }
bikeNomad 0:0944c3654ded 160
bikeNomad 1:f3f439154ab4 161 Sample_t getNextSample() {
bikeNomad 1:f3f439154ab4 162 --samplesRemaining;
bikeNomad 1:f3f439154ab4 163 return *++nextSample;
bikeNomad 1:f3f439154ab4 164 }
bikeNomad 1:f3f439154ab4 165
bikeNomad 0:0944c3654ded 166 SampleBuffer() : samplesRemaining(0), nextSample(buf) { }
bikeNomad 0:0944c3654ded 167 };
bikeNomad 0:0944c3654ded 168
bikeNomad 0:0944c3654ded 169 struct SongPlayer {
bikeNomad 0:0944c3654ded 170 SampleBuffer * volatile playing;
bikeNomad 0:0944c3654ded 171 SampleBuffer * volatile loading;
bikeNomad 0:0944c3654ded 172 SampleBuffer buffer[2];
bikeNomad 0:0944c3654ded 173 FILE *fp;
bikeNomad 0:0944c3654ded 174 size_t nChunks;
bikeNomad 0:0944c3654ded 175 size_t volatile chunksRemaining;
bikeNomad 3:00b4c1aadd30 176 float timeInSong;
bikeNomad 1:f3f439154ab4 177 Ticker sampleTicker;
bikeNomad 1:f3f439154ab4 178
bikeNomad 4:babc37764bd3 179 SongPlayer() : playing(0), loading(0), fp(0), nChunks(0), chunksRemaining(0)
bikeNomad 4:babc37764bd3 180 {}
bikeNomad 4:babc37764bd3 181
bikeNomad 1:f3f439154ab4 182 // interrupt handler
bikeNomad 1:f3f439154ab4 183 void playNextSample(void) {
bikeNomad 4:babc37764bd3 184 if (playing->samplesRemaining == 0)
bikeNomad 1:f3f439154ab4 185 swapBuffers();
bikeNomad 4:babc37764bd3 186 // NOTE bias of 0xC000 requires normalizing to 75% of full scale
bikeNomad 4:babc37764bd3 187 speaker.write_u16(static_cast<uint16_t>(playing->getNextSample() + 0x8000) / 2);
bikeNomad 1:f3f439154ab4 188 }
bikeNomad 0:0944c3654ded 189
bikeNomad 0:0944c3654ded 190 bool startSong(char const *name) {
bikeNomad 4:babc37764bd3 191 pc.printf("starting %s: ", name);
bikeNomad 0:0944c3654ded 192 if (fp) fclose(fp);
bikeNomad 0:0944c3654ded 193 fp = fopen(name, "rb");
bikeNomad 4:babc37764bd3 194 pc.printf("opened, ");
bikeNomad 4:babc37764bd3 195 if (!fp) return false;
bikeNomad 4:babc37764bd3 196 pc.printf("seekend, ");
bikeNomad 0:0944c3654ded 197 if (fseek(fp, 0, SEEK_END)) return false;
bikeNomad 0:0944c3654ded 198 long fileSize = ftell(fp);
bikeNomad 4:babc37764bd3 199 pc.printf("size=%d, ", fileSize);
bikeNomad 0:0944c3654ded 200 if (fileSize < 0) return false;
bikeNomad 0:0944c3654ded 201 if (fseek(fp, 0, SEEK_SET)) return false;
bikeNomad 4:babc37764bd3 202 pc.printf("rewound, ");
bikeNomad 0:0944c3654ded 203 chunksRemaining = nChunks = fileSize / BUFFER_SIZE;
bikeNomad 0:0944c3654ded 204 loading = &buffer[0];
bikeNomad 0:0944c3654ded 205 playing = &buffer[1];
bikeNomad 4:babc37764bd3 206 pc.printf("chunks=%d expected=%f seconds\r\n", nChunks, nChunks * SECONDS_PER_CHUNK);
bikeNomad 1:f3f439154ab4 207 if (! loadNextChunk())
bikeNomad 1:f3f439154ab4 208 return false;
bikeNomad 3:00b4c1aadd30 209 timeInSong = 0.0;
bikeNomad 1:f3f439154ab4 210 sampleTicker.attach_us(this, &SongPlayer::playNextSample, SAMPLE_PERIOD_USEC);
bikeNomad 1:f3f439154ab4 211 return true;
bikeNomad 0:0944c3654ded 212 }
bikeNomad 0:0944c3654ded 213
bikeNomad 0:0944c3654ded 214 // swap loading/playing buffers;
bikeNomad 0:0944c3654ded 215 // decrement chunksRemaining
bikeNomad 0:0944c3654ded 216 void swapBuffers() {
bikeNomad 0:0944c3654ded 217 SampleBuffer * volatile tmp = playing;
bikeNomad 0:0944c3654ded 218 if (tmp == buffer + 0)
bikeNomad 0:0944c3654ded 219 playing = buffer + 1;
bikeNomad 0:0944c3654ded 220 else
bikeNomad 0:0944c3654ded 221 playing = buffer + 0;
bikeNomad 0:0944c3654ded 222 loading = tmp;
bikeNomad 0:0944c3654ded 223 if (chunksRemaining)
bikeNomad 0:0944c3654ded 224 chunksRemaining--;
bikeNomad 0:0944c3654ded 225 }
bikeNomad 0:0944c3654ded 226
bikeNomad 0:0944c3654ded 227 // get next chunk of file into *loading
bikeNomad 0:0944c3654ded 228 // to prepare for eventual swap.
bikeNomad 0:0944c3654ded 229 // returns true if more samples remain
bikeNomad 0:0944c3654ded 230 bool loadNextChunk() {
bikeNomad 0:0944c3654ded 231 if (! chunksRemaining) return false;
bikeNomad 0:0944c3654ded 232 bool notDone = loading->loadFrom(fp);
bikeNomad 0:0944c3654ded 233 return notDone;
bikeNomad 0:0944c3654ded 234 }
bikeNomad 0:0944c3654ded 235
bikeNomad 0:0944c3654ded 236 bool isDone() {
bikeNomad 0:0944c3654ded 237 return !chunksRemaining;
bikeNomad 0:0944c3654ded 238 }
bikeNomad 0:0944c3654ded 239
bikeNomad 1:f3f439154ab4 240 // look at loading buffer; load only if necessary.
bikeNomad 1:f3f439154ab4 241 bool loadIfNecessary() {
bikeNomad 1:f3f439154ab4 242 if (loading->isDone()) {
bikeNomad 3:00b4c1aadd30 243 timeInSong += SECONDS_PER_CHUNK;
bikeNomad 1:f3f439154ab4 244 return loadNextChunk();
bikeNomad 1:f3f439154ab4 245 } else return true;
bikeNomad 1:f3f439154ab4 246 }
bikeNomad 4:babc37764bd3 247
bikeNomad 3:00b4c1aadd30 248 void playEntireSong(char const *name) {
bikeNomad 3:00b4c1aadd30 249 if (!startSong(name)) return;
bikeNomad 3:00b4c1aadd30 250 while (!isDone()) {
bikeNomad 3:00b4c1aadd30 251 loadIfNecessary();
bikeNomad 3:00b4c1aadd30 252 }
bikeNomad 3:00b4c1aadd30 253 sampleTicker.detach();
bikeNomad 3:00b4c1aadd30 254 }
bikeNomad 0:0944c3654ded 255 };
bikeNomad 0:0944c3654ded 256
bikeNomad 0:0944c3654ded 257 std::list<Song> songs;
bikeNomad 0:0944c3654ded 258
bikeNomad 0:0944c3654ded 259 int main()
bikeNomad 0:0944c3654ded 260 {
bikeNomad 3:00b4c1aadd30 261 SongPlayer player;
bikeNomad 4:babc37764bd3 262 pc.baud(115200);
bikeNomad 3:00b4c1aadd30 263
bikeNomad 0:0944c3654ded 264 // read the directory
bikeNomad 0:0944c3654ded 265 DIR *bassDir = opendir(BASS_DIRECTORY);
bikeNomad 0:0944c3654ded 266 if (bassDir) {
bikeNomad 0:0944c3654ded 267 while (dirent *dir = bassDir->readdir()) {
bikeNomad 4:babc37764bd3 268 char fn[ 60 ];
bikeNomad 4:babc37764bd3 269 snprintf(fn, sizeof(fn), "%s/%s", BASS_DIRECTORY, dir->d_name);
bikeNomad 4:babc37764bd3 270 pc.printf(fn);
bikeNomad 0:0944c3654ded 271 // if this is a valid wave filename
bikeNomad 4:babc37764bd3 272 if (Song::isValidWaveFileName(fn)) {
bikeNomad 0:0944c3654ded 273 pc.printf("\tvalid\r\n");
bikeNomad 4:babc37764bd3 274 player.playEntireSong(fn);
bikeNomad 3:00b4c1aadd30 275 pc.printf("Song time: %f\r\n", player.timeInSong);
bikeNomad 0:0944c3654ded 276 } else {
bikeNomad 0:0944c3654ded 277 pc.printf("\tnot valid\r\n");
bikeNomad 0:0944c3654ded 278 }
bikeNomad 0:0944c3654ded 279 }
bikeNomad 0:0944c3654ded 280 } else {
bikeNomad 0:0944c3654ded 281 pc.printf("Error opening " BASS_DIRECTORY);
bikeNomad 0:0944c3654ded 282 }
bikeNomad 0:0944c3654ded 283 }