14. Computation

Gest is synthesized with gest_tick. It expects a conductor phasor signal conductor, and the current position in the current audio block blkpos. If blkpos is not applicable, this can be set to be 0.

Gest assumes that it is being used inside of an environment that internally uses fixed-size audio buffers to process audio (most computer music environments do this: PureData, SuperCollider, Csound, etc). The block position variable is used to produce events that have sample-accurate timing.

<<funcdefs>>=
SKFLT gest_tick(gest_d *g, SKFLT conductor, int blkpos);
<<funcs>>=
SKFLT gest_tick(gest_d *g, SKFLT conductor, int blkpos)
{
    SKFLT out;
    SKFLT inc;
    int reset;

    out = 0;

<<ignore_negative_values>>
<<analysis>>
<<compute_ramptree>>
<<apply_targets>>
<<update_previous_value>>

    g->prevout = out;
    return out;
}

Negative values in the conductor hold no meaning, so if one pops up, gesture computation is ignored and the previously cached output value is returned, effectively "pausing" gest. This feature was added more recently so that gest could work well with a sndkit algorithm called clkphs, which converts a clock signal into a gest-compatible phasor signal. clkphs requires some initial time in the beginning to compute the first period of the phasor, and during that time it returns -1.

<<ignore_negative_values>>=
if (conductor < 0) return g->prevout;

Analysis. The conductor signal is analyzed, and used to drive the timing in the Ramp Tree. The previous conductor signal is subtracted from the current one to produce the phasor increment amount inc.

A phase reset is detected if the current conductor value is less than the previous. A reset flag is set, and the sign of the increment value is flipped to be positive.

For reasons I don't understand at the moment, it seems that the first reset found at initialization needs to be discounted, so there is logic built in here to handle that.

<<analysis>>=
inc = conductor - g->last;
reset = 0;

if (conductor < g->last) {
    inc = g->last_inc;
    reset = 1;
}

g->last_inc = inc;

/* discount reset found at initial sample */
if (g->phs == -1) {
    g->phs = 0;
    reset = 0;
}

g->last = conductor;

Conductor analysis components, the ramp tree can then be computed with ramptree_step, which updates the state of the tree and returns the tramptree value.

<<compute_ramptree>>=
out = ramptree_step(g, inc, reset, blkpos);

With the computed ramp tree value in hand, the current target's tick function can be called. This function returns the output gesture.

<<apply_targets>>=
if (g->curtarget != NULL) {
    out = gest_behavior_tick(g->curtarget->curbehavior, g, out);
    if (g->curtarget->mix != NULL) {
        out = g->curtarget->mix(g, g->curval, g->nxtval, out);
    } else {
        out = g->mix(g, g->curval, g->nxtval, out);
    }
}

Right before the value is returned, the current output is cached in the prevout variable.

<<update_previous_value>>=
if (g->squawk > 1) {
    SKFLT diff;

    diff = out - g->prevout;
    if (fabs(diff) > 0.1) {
        fprintf(stderr, "large jump: %g\n", diff);

        if (reset) {
            fprintf(stderr, "this happened at a reset.\n");
        }
    }
}

g->prevout = out;



prev | home | next