Big Mouth Billy Bass player that takes raw wavefiles and decision list text files from an SD card
Dependencies: SDFileSystem mbed BillyBass
Revision 7:ce27f959813b, committed 2013-06-17
- Comitter:
- bikeNomad
- Date:
- Mon Jun 17 22:18:12 2013 +0000
- Parent:
- 6:e90a12ca056f
- Child:
- 8:1dd2bb31dec6
- Commit message:
- mostly working; hangs when reading actions
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BillyBass.lib Mon Jun 17 22:18:12 2013 +0000 @@ -0,0 +1,1 @@ +BillyBass#84aaade0de8f
--- a/action.hpp Sat Jun 15 04:08:25 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -#ifndef __included_action_hpp -#define __included_action_hpp - -#include "billybass.hpp" - -struct Action -{ - float actionTime; - bool desiredState; - DigitalOut *output; - - bool operator < (Action const &other) const { - return actionTime < other.actionTime; - } - - bool isValid() - { - return actionTime >= 0.0 && output != 0; - } - - Action() : actionTime(-1.0) - , desiredState(false) - , output(0) { - } - - bool parseLine(char const *line) - { - // TODO - return true; - } -}; - -#endif
--- a/billybass.hpp Sat Jun 15 04:08:25 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -#ifndef __included_billybass_hpp -#define __included_billybass_hpp - -#include "mbed.h" -#include "SDFileSystem.h" -#include <list> -#include <cmath> - -struct Song; - -#define SD_NAME "sd" -#define SD_ROOT "/" SD_NAME -#define BASS_DIRECTORY SD_ROOT "/SD_Files" - -typedef int16_t Sample_t; // 16-bit raw, LE samples - -const size_t BUFFER_SIZE = 512; -const float SAMPLE_RATE_HZ = 8000.0; -#define MAX_BASENAME_LENGTH 30 -#define MAX_FILENAME_LENGTH 60 -#define MAX_ACTION_LINE_LENGTH 100 -#define SERIAL_BAUD 115200 - -const unsigned SAMPLE_PERIOD_USEC = (unsigned)(1.0e6/SAMPLE_RATE_HZ); -const size_t SAMPLES_PER_BUFFER = BUFFER_SIZE / sizeof(Sample_t); -const float SECONDS_PER_CHUNK = SAMPLES_PER_BUFFER / SAMPLE_RATE_HZ; - -extern DigitalOut tail; // J3/2 -extern DigitalOut mouth; // J3/1 -extern DigitalOut head; // J3/3 -extern DigitalIn pushbutton; // J3/4 -extern PwmOut redLED; -extern PwmOut greenLED; -extern PwmOut blueLED; -extern AnalogOut speaker; // J10/11 -extern SDFileSystem sd; -extern Serial pc; -extern std::list<Song> songs; - -#endif
--- a/main.cpp Sat Jun 15 04:08:25 2013 +0000 +++ b/main.cpp Mon Jun 17 22:18:12 2013 +0000 @@ -1,4 +1,5 @@ #include "billybass.hpp" +#include "SDFileSystem.h" #include "song.hpp" #include "player.hpp" #include "action.hpp" @@ -7,17 +8,15 @@ // Power GND J9/14 // Vin (6V) J9/16 -// Digital: -DigitalOut tail(PTA13); // J3/2 -DigitalOut mouth(PTC12); // J3/1 -DigitalOut head(PTC13); // J3/3 +// tailPin, mouthPin, bodyPin +BillyBass testBass(LED_RED, LED_GREEN, LED_BLUE); + +// tailPin, mouthPin, bodyPin +// J3/2, J3/1, J3/3 +BillyBass realBass(PTA13, PTC12, PTC13); DigitalIn pushbutton(PTD5); // J3/4 -PwmOut redLED(LED_RED); -PwmOut greenLED(LED_GREEN); -PwmOut blueLED(LED_BLUE); - // Analog: // GND J3/14 // VrefH J3/16 @@ -28,44 +27,33 @@ // PTD3 D12 - Used for MISO of SPI // PTC5 J1/9 Used for SCLK of SPI -// MOSI, MISO, SCLK, CS, name +// MOSI, MISO, SCLK, CS, name SDFileSystem sd(PTD2, PTD3, PTC5, PTD0, SD_NAME); + Serial pc(USBTX, USBRX); -std::list<Song> songs; - int main() { SongPlayer player; pc.baud(SERIAL_BAUD); - redLED = 1.0; - greenLED = 1.0; - blueLED = 1.0; + + pc.printf("*** REBOOT ***\r\n"); // read the directory - DIR *bassDir = opendir(BASS_DIRECTORY); - if (bassDir) - { - while (dirent *dir = bassDir->readdir()) - { - char fn[ MAX_FILENAME_LENGTH ]; - snprintf(fn, sizeof(fn), "%s/%s", BASS_DIRECTORY, dir->d_name); - pc.printf(fn); - // if this is a valid wave filename - if (Song::isValidWaveFileName(fn)) - { - pc.printf("\tvalid\r\n"); - player.playEntireSong(fn); - pc.printf("Song time: %f\r\n", player.timeInSong); - } - else - { - pc.printf("\tnot valid\r\n"); - } + DIR *bassDir = 0; + while (!bassDir) { + if ((bassDir = opendir(BASS_DIRECTORY)) != 0) + break; + pc.printf("Error opening " BASS_DIRECTORY "\r\n"); + wait(1.0); + } + + if (!Song::readAllSongs(bassDir)) { + pc.printf("Found no songs!\r\n"); + } else { + for (std::list<Song *>::const_iterator song_it = Song::songs.begin(); song_it != Song::songs.end(); song_it++) { + Song *song = *song_it; + song->print(pc); } } - else - { - pc.printf("Error opening " BASS_DIRECTORY); - } }
--- a/player.hpp Sat Jun 15 04:08:25 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +0,0 @@ -#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
--- a/song.hpp Sat Jun 15 04:08:25 2013 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -#ifndef __included_song_hpp -#define __included_song_hpp - -#include "billybass.hpp" -#include "action.hpp" - -struct Song -{ - char basename[ MAX_BASENAME_LENGTH + 1 ]; - unsigned sequenceNumber; - unsigned whichFish; - std::list<Action> actions; - - Song() : sequenceNumber(0) - , whichFish(3) { - basename[0] = 0; - } - - bool readWaveFile(char const *_waveFileName) - { - if (!parseFilename(_waveFileName, sequenceNumber, whichFish, basename)) return false; - - char txtFileName[ FILENAME_MAX ]; - sprintf(txtFileName, "%u_%u_%s.txt", sequenceNumber, whichFish, basename); - FILE *txtfile = fopen(txtFileName, "rt"); - if (!txtfile) return false; // TODO - - // read actions from file - char actionLine[ MAX_ACTION_LINE_LENGTH + 1 ]; - while (fgets(actionLine, sizeof(actionLine), txtfile)) - { - Action action; - if (action.parseLine(actionLine)) - { - actions.push_back(action); - } - } - return true; - } - - static bool parseFilename(char const *_name, unsigned &_num1, unsigned &_num2, char *_basename) - { - char basename[ MAX_BASENAME_LENGTH + 1 ]; - unsigned num1, num2; - char extension[ 4 ]; - int nItems = sscanf(_name, BASS_DIRECTORY "/%u_%u_%s", &num1, &num2, basename); - if (nItems != 3) - { - return false; - } - char *p = strrchr(basename, '.'); - if (!p) - { - return false; - } - strcpy(extension, p+1); - *p = 0; - if (num2 > 2) - { - return false; - } - if (strcasecmp("raw", extension)) - { - return false; - } - _num1 = num1; - _num2 = num2; - strcpy(_basename, basename); - return true; - } - - // return true if filename is of proper format - // <num>_<num>_<name>.raw - // and there is a corresponding .txt file - static bool isValidWaveFileName(char const *_name) - { - unsigned int num1, num2; - char basename[ 31 ]; - return parseFilename(_name, num1, num2, basename); - } -}; - -#endif // __included_song_hpp