phsclk
Overview
phsclk
is a utility that converts an incoming phasor
signal into a clock signal that will tick N times in one
cycle.
Using a phasor signal to generate a clock signal has the advantage of arbitrarily subdiving a beat. If the phasor is the master clock, parallel sequences can be subidvided in different ways while also remaining relatively in sync. There is no chance of accumulative drift.
This algorithm is minimally stateful, only requiring memory of the previous sample to work.
A tick is registered when the phasor crosses a certain threshold. Both the previous and current phasor signals are scaled by the subdivision amount, then floored. If they are different, a tick is registered.
Generated Files
phsclk.c
and phsclk.h
are the generated files.
#include <math.h>
#define SK_PHSCLK_PRIV
#include "phsclk.h"
<<funcs>>
#ifndef PHSCLK_H
#define PHSCLK_H
#ifndef SKFLT
#define SKFLT float
#endif
<<typedefs>>
<<funcdefs>>
#ifdef SK_PHSCLK_PRIV
<<structs>>
#endif
#endif
Structs
The state data is encapsulated in a struct called
sk_phsclk
.
typedef struct sk_phsclk sk_phsclk;
struct sk_phsclk {
<<sk_phsclk>>
};
SKFLT prev;
pc->prev = -1;
Init
phsclk is initialized with sk_phsclk_init
.
void sk_phsclk_init(sk_phsclk *pc);
void sk_phsclk_init(sk_phsclk *pc)
{
<<init>>
}
Setting Number of Ticks
The number of ticks is set with the function
sk_phsclk_nticks
.
void sk_phsclk_nticks(sk_phsclk *pc, SKFLT nticks);
void sk_phsclk_nticks(sk_phsclk *pc, SKFLT nticks)
{
pc->nticks = nticks;
}
SKFLT nticks;
4 is a sensible starting value. Western music loves multiples of 4 and 8.
sk_phsclk_nticks(pc, 4);
Computation
The function sk_phsclk_tick
computes a single sample of
audio from an input signal in
.
SKFLT sk_phsclk_tick(sk_phsclk *pc, SKFLT in);
The algorithm for phsclk is quite simple: scale and floor the previous and current input phasor signals, and if there is a difference, make a tick.
int i, pi;
SKFLT s, ps;
s = in * pc->nticks;
ps = pc->prev * pc->nticks;
i = floor(s);
pi = floor(ps);
if (i != pi) out = 1;
floor
will almost always truncate the decimal, and will
almost always ensure that the value is between 0 and
nticks - 1
. The exception to this is when in
is exactly
1. This can somes cause extra ticks to happen, so a
conditional is added to avoid this.
The one edge case for this is when nticks
is 1, which
will cause a tick only to happen at the start of the phase.
To handle this, phsclk will look for phase resets instead,
which happens when the previous value is greater than
the current value.
This also will assume the phasor starts off at 0 at the beginning, so an initial tick also gets generated.
if (pc->prev > in || pc->prev == -1) out = 1;
SKFLT sk_phsclk_tick(sk_phsclk *pc, SKFLT in)
{
SKFLT out;
out = 0;
if (in < 1) {
if (pc->nticks == 1) {
<<onetick_edgecase>>
} else {
<<the_usual_way>>
}
}
pc->prev = in;
return out;
}