Big Mouth Billy Bass automation library
Diff: player.hpp
- Revision:
- 0:84aaade0de8f
- Child:
- 2:eaba75af0f0d
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/player.hpp Mon Jun 17 22:17:59 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
Ned Konz