Big Mouth Billy Bass automation library

Dependents:   BillyBass_with_SD

song.cpp

Committer:
bikeNomad
Date:
2013-06-20
Revision:
8:ad0c038ebfc1
Parent:
7:dba9221acf48

File content as of revision 8:ad0c038ebfc1:

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <algorithm>
#include <math.h>

#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;
// one song, re-used
Song Song::theSong;

// _name is relative to BASS_DIRECTORY
// class static
Song *Song::newSong(char const *_name)
{
    Song *s = &theSong;
    s->reset();
    if (! s->parseFilename(_name)) {
        fprintf(stderr, "parseFilename(%s) failed\r\n", _name);
        goto on_error;
    }
    fprintf(stderr, "parsed filename OK\r\n");
    if (! s->readActions()) {
        fprintf(stderr, "readActions(%s) failed\r\n", _name);
        goto on_error;
    }

    return s;

on_error:
    return 0;
}

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::addAction(float _time, int _state, DigitalOut* _out, char _code)
{
    if (numActions >= MAX_ACTIONS_PER_SONG) return false;
    actions[numActions++].set(_time, _state, _out, _code);
    return true;
}

bool Song::readActions()
{
    fprintf(stderr, "reading actions of %s\r\n", getTextFileName());

    FILE *txtfile = fopen(getTextFileName(), "r");
    if (!txtfile)  {
        fprintf(stderr, "can't open %s\r\n", getTextFileName());
        return false;
    } else
        fprintf(stderr, "opened %s OK\r\n", getTextFileName());
    bool retval = false;

    BillyBass *bass = BillyBass::bassNumber(whichFish);
    if (!bass) {
        fprintf(stderr, "No bass!\r\n");
        goto done;
    }

    // read actions from file
    static char textFileBuffer[ MAX_TEXT_FILE_LENGTH ];
    memset(textFileBuffer, 0, sizeof(textFileBuffer));
    int nread = fread(textFileBuffer, 1, sizeof(textFileBuffer), txtfile);
    // fprintf(stderr, "Read %d bytes\r\n", nread);
    if (nread <= 0 || nread == sizeof(textFileBuffer)) {
        goto done;
    }
    unsigned line = 1;
    for (char *actionLine = strtok(textFileBuffer, "\r\n");
            actionLine;
            actionLine = strtok(0, "\r\n"), line++) {
        char *p;
        float startTime = strtof(actionLine, &p);
        if (p == actionLine)
            goto done;

        char *q;
        float endTime = strtof(p, &q);
        if (q == p)
            goto done;

        char const *outName;
        float onDelay, offDelay, minOnTime;
        DigitalOut *out = bass->outputNamed(++q, &outName, &onDelay, &offDelay, &minOnTime);
        if (!out) {
            fprintf(stderr, "%s line %d: bad outname \"%s\"\r\n", getTextFileName(), line, q);
            goto done;
        }

#if FIX_TIMES
        startTime -= onDelay;
        if (startTime < 0.0) startTime = 0.0;

        if (endTime < startTime + minOnTime)
            endTime = startTime + minOnTime;
#endif

        startTime -= remainder(startTime, SECONDS_PER_CHUNK);
        endTime += remainder(endTime, SECONDS_PER_CHUNK);

        fprintf(stderr, "%d %f %f %s\r\n", line, startTime, endTime, outName);

        addAction(startTime, bass->onState(), out, toupper(outName[0]));
        addAction(endTime, bass->offState(), out, outName[0]);
    }
    fprintf(stderr, "Added %d actions\r\n", numActions);
    qsort(actions, numActions, sizeof(Action), &Action::compare);
#if 0
    for (int i = 0; i < numActions; i++ ) {
        fprintf(stderr, "%f %c\r\n", actions[i].actionTime, actions[i].code);
    }
#endif
    retval = true;

done:

    fclose(txtfile);
    return retval;
}