7. Behavior Commands

Behaviors are the means by which one target gets to thep next target.

7.1. Linear

Linear: converts the last target to use linear behavior.

<<funcdefs>>=
void gest_behavior_linear(gest_d *g);
<<funcs>>=
static SKFLT linear(gest_d *g, SKFLT a, void *ud)
{
    return a;
}

void gest_behavior_linear(gest_d *g)
{
    set_behavior(g, linear, NULL);
}

7.2. Step

Step: converts the last target to be a step (no line, just the value).

<<funcdefs>>=
void gest_behavior_step(gest_d *g);
<<funcs>>=
static SKFLT step(gest_d *g, SKFLT a, void *ud)
{
    return 0;
}

void gest_behavior_step(gest_d *g)
{
    set_behavior(g, step, NULL);
}

7.3. Gliss

Gliss is a behavior intended gestures that are pitches mimicking glissando. It works by cutting the incoming ramp in half. The first half is 0, and the second half gets normalized to be 0-1 with a cubic slope. This then gets interpolated between the x and y target values.

7.3.1. Regular Gliss

<<funcdefs>>=
void gest_behavior_gliss(gest_d *g);
<<funcs>>=
static SKFLT gliss(gest_d *g, SKFLT a, void *ud)
{
    if (a < 0.5) {
        a = 0;
    } else {
        a -= 0.5;
        if (a < 0) a = 0;
        a *= 2;
        a = a * a * a;
    }

    return a;
}

void gest_behavior_gliss(gest_d *g)
{
    set_behavior(g, gliss, NULL);
}

7.3.2. Small Gliss

smallgliss does this, but smaller scale. Best for instruments that want the minimal amount of smoothing done to preserve a step-sequenced sound.

<<funcdefs>>=
void gest_behavior_smallgliss(gest_d *g);
<<funcs>>=
static SKFLT smallgliss(gest_d *g, SKFLT a, void *ud)
{
    if (a < 0.97) {
        a = 0;
    } else {
        a -= 0.97;
        if (a < 0) a = 0;
        a /= 0.03;
        a = a * a * a;
    }
    return a;
}

void gest_behavior_smallgliss(gest_d *g)
{
    set_behavior(g, smallgliss, NULL);
}

7.3.3. Medium Gliss

Ideally tuned for voice.

<<funcdefs>>=
void gest_behavior_mediumgliss(gest_d *g);
<<funcs>>=
static SKFLT mediumgliss(gest_d *g, SKFLT a, void *ud)
{
    if (a < 0.75) {
        a = 0;
    } else {
        a -= 0.75;
        if (a < 0) a = 0;
        a /= 0.25;
        a = a * a * a;
    }
    return a;
}

void gest_behavior_mediumgliss(gest_d *g)
{
    set_behavior(g, mediumgliss, NULL);
}

7.4. Exponential

Exponential: converts the last target to use exponential behavior with slope slope.

<<funcdefs>>=
void gest_behavior_exponential(gest_d *g, SKFLT slope);
<<funcdefs>>=
void gest_behavior_step(gest_d *g);
<<funcs>>=
static SKFLT exponential(gest_d *g, SKFLT a, void *ud)
{
    SKFLT *s;

    s = ud;

    return (1.0 - exp(a * (*s))) / (1 - exp(*s));
}

void gest_behavior_exponential(gest_d *g, SKFLT slope)
{
    SKFLT *s;
    s = gest_alloc(g, sizeof(SKFLT));
    *s = slope;
    set_behavior(g, exponential, s);
}

7.5. Bezier

Applies a quadratic Bezier line segment between to two targets. Bezier takes in two control values that are control targets.

<<funcdefs>>=
void gest_behavior_bezier(gest_d *g, SKFLT cx, SKFLT cy);

Explanation for how this works is already done in the sndkit Bezier algorithm where this is code is based, so it's worth checking that out for the mathematical derivation.

<<funcs>>=
struct bezier_data {
    SKFLT cx;
    SKFLT cy;
};

/* https://pbat.ch/sndkit/bezier/ */

static SKFLT quadratic_equation(SKFLT a, SKFLT b, SKFLT c)
{
    SKFLT det; /* determinant */

    det = b*b - 4*a*c;

    if (det >= 0) {
        return ((-b + sqrt(det))/(2.0*a));
    } else {
        return 0;
    }
}

static SKFLT find_t(SKFLT x0, SKFLT x1, SKFLT x2, SKFLT x)
{
    SKFLT a, b, c;

    a = (x0 - 2.0 * x1 + x2);
    b = 2.0 * (-x0 + x1);
    c = x0 - x;

    if (a) {
        return quadratic_equation(a, b, c);
    } else {
        return (x - x0) / b;
    }
}

static SKFLT bezier_curve(SKFLT xpos, SKFLT cx, SKFLT cy)
{
    SKFLT x[3];
    SKFLT y[3];
    SKFLT t;
    SKFLT val;

    x[0] = 0;
    x[1] = cx;
    x[2] = 1;

    y[0] = 0;
    y[1] = cy;
    y[2] = 1;

    t = find_t(x[0], x[1], x[2], xpos);

    val = (1.0-t)*(1.0-t)*y[0] + 2.0*(1 - t)*t*y[1] + t*t*y[2];
    return val;
}

static SKFLT bezier(gest_d *g, SKFLT a, void *ud)
{

    struct bezier_data *bd;
    bd = ud;
    return bezier_curve(a, bd->cx, bd->cy);
}

void gest_behavior_bezier(gest_d *g, SKFLT cx, SKFLT cy)
{
    struct bezier_data *bd;
    bd = gest_alloc(g, sizeof(struct bezier_data));
    bd->cx = cx;
    bd->cy = cy;
    set_behavior(g, bezier, bd);
}

7.6. Smoothstep

Applies a smooth step function to the behavior.

<<funcdefs>>=
void gest_behavior_smoothstep(gest_d *g);
<<funcs>>=
static SKFLT smoothstep(gest_d *g, SKFLT a, void *ud)
{
    SKFLT out;

    if (a <= 0) out = 0;
    else if (a >= 1) out = 1;
    else out = a * a * (3 - 2 * a);
    return out;
}

void gest_behavior_smoothstep(gest_d *g)
{
    set_behavior(g, smoothstep, NULL);
}

7.7. Biramp

Takes the linear ramp, and splits it in half at a particular position with an upwards and a downwards ramp. Intended to be used for LFO purposes.

<<funcdefs>>=
void gest_behavior_biramp(gest_d *g, SKFLT pos);
<<funcdefs>>=
void gest_behavior_biramp(gest_d *g, SKFLT pos);
<<funcs>>=
static SKFLT biramp(gest_d *g, SKFLT a, void *ud)
{
    SKFLT *pos;
    SKFLT out;

    pos = ud;

    if (*pos == 0 || *pos == 1) return *pos;

    if (a < *pos) out = a / *pos;
    else out = (1 - a) / (1 - *pos);

    return out;
}

void gest_behavior_biramp(gest_d *g, SKFLT pos)
{
    SKFLT *ppos;
    ppos = gest_alloc(g, sizeof(SKFLT));
    *ppos = pos;
    set_behavior(g, biramp, ppos);
}

7.8. Gate

Takes the input ramp, and produces a gate signal that is on (value of 1) for a relative percentage of the target before turning off (0).

<<funcdefs>>=
void gest_behavior_gate(gest_d *g, SKFLT pos);
<<funcs>>=
static SKFLT gatefun(gest_d *g, SKFLT a, void *ud)
{
    SKFLT *pos;
    SKFLT out;

    pos = ud;

    if (a <= *pos) out = 1.0;
    else out = 0.0;

    return out;
}

void gest_behavior_gate(gest_d *g, SKFLT pos)
{
    SKFLT *ppos;
    ppos = gest_alloc(g, sizeof(SKFLT));
    *ppos = pos;
    set_behavior(g, gatefun, ppos);
}



prev | home | next