8. The General Rephasor Algorithm

(NOTE: This section outlines the generalized rephasor. Not to be confused with the 10. Main Rephasor.)

8.1. Overview

The flexible timing in GestVM is made possible via the rephasor algorithm.

A rephasor creates a phasor signal that is rhythmically porportional to the input phasor signal. Auto-time correction is applied to prevent clock drift accumulation, at the cost of delay of 2 samples.

<<typedefs>>=
typedef struct gestvm_rephasor gestvm_rephasor;

Copied from the sndkit rephasor implementation.

The terseness of the symbols comes from a math notation from the original sndkit algorithm. They are outlined here below.

<<rephasor_struct>>=
struct gestvm_rephasor {
    SKFLT pr; /* rephasor */
    SKFLT pc[2]; /* comparison phasor */
    SKFLT pe[2]; /* external phasor */
    SKFLT c; /* correction */
    SKFLT s; /* scaling */
    SKFLT si; /* scaling, inverted */

    SKFLT ir; /* rephasor incrementor */
    SKFLT ic; /* comparison rephasor */
};

8.2. Setting the scale

The scaling parameter of the rephasor is set with gestvm_rephasor_scale.

<<funcdefs>>=
void gestvm_rephasor_scale(gestvm_rephasor *rp, SKFLT scale);

The scaling amount determines the rate of the new phasor relative to the old one. A value of 1.0 is no change, less than one is slower (eg: 0.5 is half speed), and greater than one is faster (2.0 is double speed).

<<funcs>>=
void gestvm_rephasor_scale(gestvm_rephasor *rp, SKFLT scale)
{
    if (scale != rp->s) {
        rp->s = scale;
        rp->si = 1.0 / scale;
    }
}

8.3. Initialization

Initialized with gestvm_rephasor_init.

<<funcdefs>>=
void gestvm_rephasor_init(gestvm_rephasor *rp);
<<funcs>>=
void gestvm_rephasor_init(gestvm_rephasor *rp)
{
    rp->pr = 0;
    rp->pc[0] = 0;
    rp->pc[1] = 0;
    rp->pe[0] = 0;
    rp->pe[1] = 0;
    rp->c = 1.0;
    rp->s = 1.0;
    rp->si = 1.0;

    rp->ir = 0.0;
    rp->ic = 0.0;
}

8.4. The Rephasor Algorithm

A single sample of audio can be computed with gestvm_rephasor_tick. It expects an input phasor ext.

<<funcdefs>>=
SKFLT gestvm_rephasor_tick(gestvm_rephasor *rp, SKFLT ext);

The core rephasor implemented here is based on the one described in sndkit that uses auto-correction.

The main principle of a rephasor is that a phasor can be reconstructed by measuring the rate of change of incoming phasor. Applying some scaling value to this rate of change will proportionally change the rate of the phasor.

Left unchecked, a rephasor created using this method will drift out of phase with the original signal. This can be prevented by putting the rephasor through another rephasor, set to be the inverse scaling value. This, in theory, should reproduce the original phasor signal. This new rephasor value is compared with the orignal, and the measured difference is applied as correction in the next iteration.

Due to the analysis step, a rephasor introduces one sample of delay. Both rephasors therefore introduce a total of 2 samples of delay. This is accounted for when computing the correction amount.

The correction value is clamped to be between 2.0 and 0.5. Anything out of these bounds is mostly likely an invalid value.

<<funcs>>=
static SKFLT phasor(SKFLT phs, SKFLT inc)
{
    phs += inc;

    if (phs > 1.0) return 0;

    return phs;
}

SKFLT gestvm_rephasor_tick(gestvm_rephasor *rp, SKFLT ext)
{
    SKFLT pr, pc;
    SKFLT out;


    /* delta function of \theta_e */
    if (ext > rp->pe[0]) {
        rp->ir = ext - rp->pe[0];
    }

    /* compute main rephasor \theta_r */
    pr = phasor(rp->pr, rp->s * rp->ir * rp->c);

    /* delta function of \theta_r */
    if (pr > rp->pr) {
        rp->ic = pr - rp->pr;
    }

    /* compute rephasor \theta_c */
    pc = phasor(rp->pc[0], rp->si * rp->ic);

    /* compute correction coefficient */
    if (rp->pc[1] != 0) {
        rp->c = rp->pe[1] / rp->pc[1];
    }

    /* clamping the correction */
    if (rp->c > 2.0) rp->c = 2.0;
    if (rp->c < 0.5) rp->c = 0.5;

    out = pr;

    /* update state */

    rp->pr = pr;

    rp->pc[1] = rp->pc[0];
    rp->pc[0] = pc;

    rp->pe[1] = rp->pe[0];
    rp->pe[0] = ext;

    return out;
}



prev | home | next