Big Mouth Billy Bass automation library
Diff: song.cpp
- Revision:
- 0:84aaade0de8f
- Child:
- 1:9b1f3eb204ac
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/song.cpp Mon Jun 17 22:17:59 2013 +0000 @@ -0,0 +1,178 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <algorithm> + +#include "billybass.hpp" +#include "song.hpp" + +// class static +char const Song::directoryName[ BASS_DIRECTORY_LENGTH + 1 ] = BASS_DIRECTORY; +// class static +char const *Song::textExtension = "txt"; +// class static +char const *Song::sampleExtension = "raw"; +// class static +unsigned const Song::NO_FISH = MAX_FISH + 1; +// class static +std::list<Song*> Song::songs; + +// _name is relative to BASS_DIRECTORY +// class static +Song *Song::newSong(char const *_name) +{ + Song *s = new Song; + if (!s) { + pc.printf("new Song == 0\r\n"); + return 0; + } + if (! s->parseFilename(_name)) { + pc.printf("parseFilename(%s) failed\r\n", _name); + goto on_error; + } + if (! s->readActions()) { + pc.printf("readActions(%s) failed\r\n", _name); + goto on_error; + } + songs.push_back(s); + + return s; + +on_error: + delete s; + return 0; +} + +// class static +void Song::clearAllSongs() +{ + for (std::list<Song *>::const_iterator song = songs.begin(); song != songs.end(); song++) + delete *song; + songs.clear(); +} + +// class static +unsigned Song::readAllSongs(DIR *bassDir) +{ + if (!bassDir) return 0; + clearAllSongs(); + + while (dirent *dir = bassDir->readdir()) { + pc.printf("Reading %s\r\n", dir->d_name); + unsigned namelen = strlen(dir->d_name); + if (namelen > 9 && !strcasecmp(dir->d_name + namelen - 3, sampleExtension)) { + Song *song = Song::newSong(dir->d_name); + if (!song) { + pc.printf("ERROR reading %s\r\n", dir->d_name); + } else { + song->print(pc); + } + } + } + + return songs.size(); +} + +bool Song::parseFilename(char const *_name) +{ + if (strlen(_name) > MAX_BASENAME_LENGTH) + goto on_error; + + strcpy(fullname, directoryName); + basename = fullname + BASS_DIRECTORY_LENGTH; + *basename++ = '/'; + strcpy(basename, _name); + + char *p; + long n = strtol(_name, &p, 10); + if (*p != '_' || n <= 0) + goto on_error; + + sequenceNumber = (unsigned)(n - 1); + + p++; // skip underscore + n = strtol(p, &p, 10); + if (*p != '_' || n <= 0 || n > MAX_FISH) + goto on_error; + + whichFish = (unsigned)(n - 1); + + p = strrchr(basename, '.'); + if (!p) + goto on_error; + + extension = p+1; + + if (strcasecmp(extension, sampleExtension)) + goto on_error; + + return true; + +on_error: + whichFish = NO_FISH; + basename = 0; + return false; +} + +bool Song::readActions() +{ + FILE *txtfile = fopen(getTextFileName(), "r"); + if (!txtfile) { + pc.printf("can't open %s\r\n", getTextFileName()); + return false; + } + bool retval = false; + + BillyBass *bass = BillyBass::bassNumber(whichFish); + if (!bass) { + pc.printf("No bass!\r\n"); + goto done; + } + + // read actions from file + unsigned line = 1; + char actionLine[ MAX_ACTION_LINE_LENGTH + 1 ]; + while (true) { + if (!fgets(actionLine, sizeof(actionLine), txtfile)) + break; + + // delete trailing whitespace + char *p = actionLine + strlen(actionLine) - 1; + while (isspace(*p)) *p-- = 0; + + float startTime = strtof(actionLine, &p); + if (p == actionLine) + goto done; + + char *q; + float endTime = strtof(p, &q); + if (q == p) + goto done; + + while (isspace(*q)) q++; + + char const *outName; + DigitalOut *out = bass->outputNamed(q, &outName); + if (!out) { + pc.printf("%s line %d: bad outname \"%s\"\r\n", getTextFileName(), line, q); + goto done; + } + pc.printf("%d add %f %f %s\r\n", line, startTime, endTime, outName); + + actions.push_back(Action(startTime, true, out, outName)); + actions.push_back(Action(endTime, false, out, outName)); + + line++; + } + + std::sort(actions.begin(), actions.end()); // sort actions by time + retval = true; + +done: + if (!retval) { + pc.printf("Error reading action from \"%s\"\r\n", actionLine); + } + fclose(txtfile); + return retval; +}