Big Mouth Billy Bass player that takes raw wavefiles and decision list text files from an SD card
Dependencies: SDFileSystem mbed BillyBass
Diff: player.hpp
- Revision:
- 5:5b846ef42702
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/player.hpp Sat Jun 15 03:32:20 2013 +0000 @@ -0,0 +1,154 @@ +#ifndef __included_player_hpp +#define __included_player_hpp + +#include "billybass.hpp" + +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; + } + + Sample_t getNextSample() + { + --samplesRemaining; + return *++nextSample; + } + + 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; + float timeInSong; + Ticker sampleTicker; + + SongPlayer() : playing(0) + , loading(0) + , fp(0) + , nChunks(0) + , chunksRemaining(0) + { + } + + // interrupt handler + void playNextSample(void) + { + if (playing->samplesRemaining == 0) + swapBuffers(); + // NOTE bias of 0xC000 requires normalizing to 75% of full scale + speaker.write_u16(static_cast<uint16_t>(playing->getNextSample() + 0x8000) / 2); + } + + bool startSong(char const *name) + { + pc.printf("starting %s: ", name); + if (fp) fclose(fp); + fp = fopen(name, "rb"); + pc.printf("opened, "); + if (!fp) return false; + + pc.printf("seekend, "); + if (fseek(fp, 0, SEEK_END)) return false; + + long fileSize = ftell(fp); + pc.printf("size=%d, ", fileSize); + if (fileSize < 0) return false; + + if (fseek(fp, 0, SEEK_SET)) return false; + + pc.printf("rewound, "); + chunksRemaining = nChunks = fileSize / BUFFER_SIZE; + loading = &buffer[0]; + playing = &buffer[1]; + pc.printf("chunks=%d expected=%f seconds\r\n", nChunks, nChunks * SECONDS_PER_CHUNK); + if (!loadNextChunk()) + return false; + + timeInSong = 0.0; + sampleTicker.attach_us(this, &SongPlayer::playNextSample, SAMPLE_PERIOD_USEC); + return true; + } + + // 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; + } + + // look at loading buffer; load only if necessary. + bool loadIfNecessary() + { + if (loading->isDone()) + { + timeInSong += SECONDS_PER_CHUNK; + return loadNextChunk(); + } + else { return true; } + } + + void playEntireSong(char const *name) + { + if (!startSong(name)) return; + + while (!isDone()) + { + loadIfNecessary(); + } + sampleTicker.detach(); + } +}; + +#endif