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:
Sat Jun 15 03:32:20 2013 +0000
Revision:
5:5b846ef42702
separated into multiple files

Who changed what in which revision?

UserRevisionLine numberNew contents of line
bikeNomad 5:5b846ef42702 1 #ifndef __included_player_hpp
bikeNomad 5:5b846ef42702 2 #define __included_player_hpp
bikeNomad 5:5b846ef42702 3
bikeNomad 5:5b846ef42702 4 #include "billybass.hpp"
bikeNomad 5:5b846ef42702 5
bikeNomad 5:5b846ef42702 6 class SongPlayer;
bikeNomad 5:5b846ef42702 7
bikeNomad 5:5b846ef42702 8 struct SampleBuffer
bikeNomad 5:5b846ef42702 9 {
bikeNomad 5:5b846ef42702 10 Sample_t volatile buf[ SAMPLES_PER_BUFFER ];
bikeNomad 5:5b846ef42702 11 size_t volatile samplesRemaining;
bikeNomad 5:5b846ef42702 12 Sample_t volatile * volatile nextSample;
bikeNomad 5:5b846ef42702 13
bikeNomad 5:5b846ef42702 14 bool isDone()
bikeNomad 5:5b846ef42702 15 {
bikeNomad 5:5b846ef42702 16 return !samplesRemaining;
bikeNomad 5:5b846ef42702 17 }
bikeNomad 5:5b846ef42702 18
bikeNomad 5:5b846ef42702 19 float remainingDuration()
bikeNomad 5:5b846ef42702 20 {
bikeNomad 5:5b846ef42702 21 return samplesRemaining / SAMPLE_RATE_HZ;
bikeNomad 5:5b846ef42702 22 }
bikeNomad 5:5b846ef42702 23
bikeNomad 5:5b846ef42702 24 // return true if we read any samples
bikeNomad 5:5b846ef42702 25 bool loadFrom(FILE *fp)
bikeNomad 5:5b846ef42702 26 {
bikeNomad 5:5b846ef42702 27 samplesRemaining = fread((void*)buf, sizeof(Sample_t), SAMPLES_PER_BUFFER, fp);
bikeNomad 5:5b846ef42702 28 nextSample = buf;
bikeNomad 5:5b846ef42702 29 return samplesRemaining > 0;
bikeNomad 5:5b846ef42702 30 }
bikeNomad 5:5b846ef42702 31
bikeNomad 5:5b846ef42702 32 Sample_t getNextSample()
bikeNomad 5:5b846ef42702 33 {
bikeNomad 5:5b846ef42702 34 --samplesRemaining;
bikeNomad 5:5b846ef42702 35 return *++nextSample;
bikeNomad 5:5b846ef42702 36 }
bikeNomad 5:5b846ef42702 37
bikeNomad 5:5b846ef42702 38 SampleBuffer() : samplesRemaining(0)
bikeNomad 5:5b846ef42702 39 , nextSample(buf) {
bikeNomad 5:5b846ef42702 40 }
bikeNomad 5:5b846ef42702 41 };
bikeNomad 5:5b846ef42702 42
bikeNomad 5:5b846ef42702 43 struct SongPlayer
bikeNomad 5:5b846ef42702 44 {
bikeNomad 5:5b846ef42702 45 SampleBuffer * volatile playing;
bikeNomad 5:5b846ef42702 46 SampleBuffer * volatile loading;
bikeNomad 5:5b846ef42702 47 SampleBuffer buffer[2];
bikeNomad 5:5b846ef42702 48 FILE *fp;
bikeNomad 5:5b846ef42702 49 size_t nChunks;
bikeNomad 5:5b846ef42702 50 size_t volatile chunksRemaining;
bikeNomad 5:5b846ef42702 51 float timeInSong;
bikeNomad 5:5b846ef42702 52 Ticker sampleTicker;
bikeNomad 5:5b846ef42702 53
bikeNomad 5:5b846ef42702 54 SongPlayer() : playing(0)
bikeNomad 5:5b846ef42702 55 , loading(0)
bikeNomad 5:5b846ef42702 56 , fp(0)
bikeNomad 5:5b846ef42702 57 , nChunks(0)
bikeNomad 5:5b846ef42702 58 , chunksRemaining(0)
bikeNomad 5:5b846ef42702 59 {
bikeNomad 5:5b846ef42702 60 }
bikeNomad 5:5b846ef42702 61
bikeNomad 5:5b846ef42702 62 // interrupt handler
bikeNomad 5:5b846ef42702 63 void playNextSample(void)
bikeNomad 5:5b846ef42702 64 {
bikeNomad 5:5b846ef42702 65 if (playing->samplesRemaining == 0)
bikeNomad 5:5b846ef42702 66 swapBuffers();
bikeNomad 5:5b846ef42702 67 // NOTE bias of 0xC000 requires normalizing to 75% of full scale
bikeNomad 5:5b846ef42702 68 speaker.write_u16(static_cast<uint16_t>(playing->getNextSample() + 0x8000) / 2);
bikeNomad 5:5b846ef42702 69 }
bikeNomad 5:5b846ef42702 70
bikeNomad 5:5b846ef42702 71 bool startSong(char const *name)
bikeNomad 5:5b846ef42702 72 {
bikeNomad 5:5b846ef42702 73 pc.printf("starting %s: ", name);
bikeNomad 5:5b846ef42702 74 if (fp) fclose(fp);
bikeNomad 5:5b846ef42702 75 fp = fopen(name, "rb");
bikeNomad 5:5b846ef42702 76 pc.printf("opened, ");
bikeNomad 5:5b846ef42702 77 if (!fp) return false;
bikeNomad 5:5b846ef42702 78
bikeNomad 5:5b846ef42702 79 pc.printf("seekend, ");
bikeNomad 5:5b846ef42702 80 if (fseek(fp, 0, SEEK_END)) return false;
bikeNomad 5:5b846ef42702 81
bikeNomad 5:5b846ef42702 82 long fileSize = ftell(fp);
bikeNomad 5:5b846ef42702 83 pc.printf("size=%d, ", fileSize);
bikeNomad 5:5b846ef42702 84 if (fileSize < 0) return false;
bikeNomad 5:5b846ef42702 85
bikeNomad 5:5b846ef42702 86 if (fseek(fp, 0, SEEK_SET)) return false;
bikeNomad 5:5b846ef42702 87
bikeNomad 5:5b846ef42702 88 pc.printf("rewound, ");
bikeNomad 5:5b846ef42702 89 chunksRemaining = nChunks = fileSize / BUFFER_SIZE;
bikeNomad 5:5b846ef42702 90 loading = &buffer[0];
bikeNomad 5:5b846ef42702 91 playing = &buffer[1];
bikeNomad 5:5b846ef42702 92 pc.printf("chunks=%d expected=%f seconds\r\n", nChunks, nChunks * SECONDS_PER_CHUNK);
bikeNomad 5:5b846ef42702 93 if (!loadNextChunk())
bikeNomad 5:5b846ef42702 94 return false;
bikeNomad 5:5b846ef42702 95
bikeNomad 5:5b846ef42702 96 timeInSong = 0.0;
bikeNomad 5:5b846ef42702 97 sampleTicker.attach_us(this, &SongPlayer::playNextSample, SAMPLE_PERIOD_USEC);
bikeNomad 5:5b846ef42702 98 return true;
bikeNomad 5:5b846ef42702 99 }
bikeNomad 5:5b846ef42702 100
bikeNomad 5:5b846ef42702 101 // swap loading/playing buffers;
bikeNomad 5:5b846ef42702 102 // decrement chunksRemaining
bikeNomad 5:5b846ef42702 103 void swapBuffers()
bikeNomad 5:5b846ef42702 104 {
bikeNomad 5:5b846ef42702 105 SampleBuffer * volatile tmp = playing;
bikeNomad 5:5b846ef42702 106 if (tmp == buffer + 0)
bikeNomad 5:5b846ef42702 107 playing = buffer + 1;
bikeNomad 5:5b846ef42702 108 else
bikeNomad 5:5b846ef42702 109 playing = buffer + 0;
bikeNomad 5:5b846ef42702 110 loading = tmp;
bikeNomad 5:5b846ef42702 111 if (chunksRemaining)
bikeNomad 5:5b846ef42702 112 chunksRemaining--;
bikeNomad 5:5b846ef42702 113 }
bikeNomad 5:5b846ef42702 114
bikeNomad 5:5b846ef42702 115 // get next chunk of file into *loading
bikeNomad 5:5b846ef42702 116 // to prepare for eventual swap.
bikeNomad 5:5b846ef42702 117 // returns true if more samples remain
bikeNomad 5:5b846ef42702 118 bool loadNextChunk()
bikeNomad 5:5b846ef42702 119 {
bikeNomad 5:5b846ef42702 120 if (!chunksRemaining) return false;
bikeNomad 5:5b846ef42702 121
bikeNomad 5:5b846ef42702 122 bool notDone = loading->loadFrom(fp);
bikeNomad 5:5b846ef42702 123 return notDone;
bikeNomad 5:5b846ef42702 124 }
bikeNomad 5:5b846ef42702 125
bikeNomad 5:5b846ef42702 126 bool isDone()
bikeNomad 5:5b846ef42702 127 {
bikeNomad 5:5b846ef42702 128 return !chunksRemaining;
bikeNomad 5:5b846ef42702 129 }
bikeNomad 5:5b846ef42702 130
bikeNomad 5:5b846ef42702 131 // look at loading buffer; load only if necessary.
bikeNomad 5:5b846ef42702 132 bool loadIfNecessary()
bikeNomad 5:5b846ef42702 133 {
bikeNomad 5:5b846ef42702 134 if (loading->isDone())
bikeNomad 5:5b846ef42702 135 {
bikeNomad 5:5b846ef42702 136 timeInSong += SECONDS_PER_CHUNK;
bikeNomad 5:5b846ef42702 137 return loadNextChunk();
bikeNomad 5:5b846ef42702 138 }
bikeNomad 5:5b846ef42702 139 else { return true; }
bikeNomad 5:5b846ef42702 140 }
bikeNomad 5:5b846ef42702 141
bikeNomad 5:5b846ef42702 142 void playEntireSong(char const *name)
bikeNomad 5:5b846ef42702 143 {
bikeNomad 5:5b846ef42702 144 if (!startSong(name)) return;
bikeNomad 5:5b846ef42702 145
bikeNomad 5:5b846ef42702 146 while (!isDone())
bikeNomad 5:5b846ef42702 147 {
bikeNomad 5:5b846ef42702 148 loadIfNecessary();
bikeNomad 5:5b846ef42702 149 }
bikeNomad 5:5b846ef42702 150 sampleTicker.detach();
bikeNomad 5:5b846ef42702 151 }
bikeNomad 5:5b846ef42702 152 };
bikeNomad 5:5b846ef42702 153
bikeNomad 5:5b846ef42702 154 #endif