Big Mouth Billy Bass automation library

Dependents:   BillyBass_with_SD

song.cpp

Committer:
bikeNomad
Date:
2013-06-17
Revision:
0:84aaade0de8f
Child:
1:9b1f3eb204ac

File content as of revision 0:84aaade0de8f:

#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;
}