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 04:00:57 2013 +0000
Revision:
3:00b4c1aadd30
Parent:
2:f96989f2b8a1
Child:
4:babc37764bd3
added song playing

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