18. Scalars

A scalar is a mostly constant value that is able to be changed every time a new target happens. In most situations, scalars can also change values with sample accurate timing alongside the target they are attached to (more on this later).

One good application for scalars are for use as gate signals when using gest to sequence melody. A target can change an arbitrary number of scalars, so it is a good way to add some elementary multi-dimensional control that is correlated with a gesture.

The data for a scalar is contained in a struct called gest_scalar.

Because it is mostly intended to be used externally from Gest, the struct is exposed publically in the header.

<<typedefs>>=
typedef struct {
    SKFLT cur;
    SKFLT nxt;
    int pos;
} gest_scalar;

The scalar holds two values, a current value cur, and a next value nxt, as well as an integer marking block position pos. When non-negative, the position will mark at what point in an active audio buffer a value has changed. Anything before it is the current value, anything on or after it is the next value.

This struct can be initialized with gest_scalar_init.

<<funcdefs>>=
void gest_scalar_init(gest_scalar *s);
<<funcs>>=
void gest_scalar_init(gest_scalar *s)
{
    s->cur = 0;
    s->nxt = 0;
    s->pos = -1;
}

The current/next value approach with a position flag allows for up to one change to happen inside of a audio block. In graforge, this is typically a block size of 64 samples. This means targets cannot be spaced less thans than 1ms apart. For most situations, this is an acceptable limitation. As a result, what is achieved is a very efficient means for sample accurate timing.

Scalars are external to an instance of gest, and are connected to it via an action.

The function action_scalar creates a new action for a target that sets the input scalar to a particular value. The action will set the next value of the scalar, and set the position.

<<static_funcdefs>>=
static void action_scalar(gest_d *g,
                          gest_target *t,
                          gest_scalar *s,
                          SKFLT val);
<<funcs>>=
struct action_scalar_d {
   gest_scalar *s;
   SKFLT val;
};

static void scalar_cb(gest_d *g, void *ud, int pos)
{
    struct action_scalar_d *ad;
    ad = (struct action_scalar_d *)ud;

    ad->s->nxt = ad->val;
    ad->s->pos = pos;
}

static void action_scalar(gest_d *g,
                          gest_target *t,
                          gest_scalar *s,
                          SKFLT val)
{
    struct action_scalar_d *ad;

    ad = gest_alloc(g, sizeof(struct action_scalar_d));

    ad->s = s;
    ad->val = val;

    append_action(g, t, scalar_cb, ad);
}

In order for scalar logic to work properly, the instance of Gest must be computed before the scalar value is computed. That way, the scalar value gets updated properly.

The scalar node itself is beyond the scope of this document, but an implementation of a scalar node using sndkit is available in this project.



prev | home | next