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.
SKFLT gest_tick(gest_d *g, SKFLT conductor, int blkpos);
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.
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.
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.
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.
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.
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