Big Mouth Billy Bass automation library

Dependents:   BillyBass_with_SD

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