Sparse
Overview
Sparse
implements a sparse noise generator. This algorithm
will randomly fire impulses with amplitudes within the
range [-1, 1] at an average rate determined by a parametric
frequency value. The resulting sound is something that
can crudely approximate the kind of crackling noise from
a vinyl record (some filtering on the sparse noise helps
a bit).
Sparse noise is closely related to Velvet Noise
, a special
kind of signal that's been often used in the context of
reverberation. The difference between the two is that velvet
noise only has impulses with amplitudes of -1 and 1, and
nothing in between. Putting sparse noise through a
Sign Function will
convert it to Velvet Noise.
Tangled Files
Sparse tangles to two self contained files sparse.c
and
sparse.h
. Define SK_SPARSE_PRIV
will expose the contents
of the sk_sparse
struct.
#ifndef SK_SPARSE_H
#define SK_SPARSE_H
#ifndef SKFLT
#define SKFLT float
#endif
<<typedefs>>
#ifdef SK_SPARSE_PRIV
<<structs>>
#endif
<<funcdefs>>
#endif
#define SK_SPARSE_PRIV
#include "sparse.h"
<<static_funcdefs>>
<<funcs>>
Struct
typedef struct sk_sparse sk_sparse;
struct sk_sparse {
int sr;
<<sk_sparse>>
};
Initialization
The function sk_sparse_init
will initialize an instance
of sk_sparse
. It requires the sampling rate sr
, and
an initial seed for the internal random number generator.
void sk_sparse_init(sk_sparse *sp, int sr, unsigned long seed);
void sk_sparse_init(sk_sparse *sp, int sr, unsigned long seed)
{
sp->sr = sr;
<<init>>
}
Setting The Frequency Parameter
The rate of the sparse noise generator is set with
the frequency parameter via sk_sparse_freq
.
The frequency variable uses parameter caching in order to update values only when needed.
SKFLT freq;
SKFLT pfreq;
sk_sparse_freq(sp, 30); /* arbitrary positive value */
sp->pfreq = -1;
void sk_sparse_freq(sk_sparse *sp, SKFLT freq);
void sk_sparse_freq(sk_sparse *sp, SKFLT freq)
{
sp->freq = freq;
}
Threshold Component
The thresh
variable will maintain a threshold generator,
that gets updated every time the frequency changes. This
is a value between 0 and 1. The higher the value, the more
likely an impulse will be generated.
SKFLT thresh;
sp->thresh = 0;
Random Number Generator
For randomness, a simple 32-bit LCG is good enough for
musical purposes. This is used over system rand
for
portability reasons.
unsigned long rng;
sp->rng = seed;
The 32-bit random value that is generated from the LCG gets normalized to be in range 0 and 1.
A little utility called randval
will be used to compute
and normalize the LCG.
static SKFLT randval(sk_sparse *sp);
static SKFLT randval(sk_sparse *sp)
{
sp->rng = (1103515245L * sp->rng + 12345L);
sp->rng %= 2147483648L;
return (SKFLT)sp->rng / 2147483648L;
}
Computation
A single sample of audio is computed with sk_sparse_tick
.
SKFLT sk_sparse_tick(sk_sparse *sp);
SKFLT sk_sparse_tick(sk_sparse *sp)
{
SKFLT out;
SKFLT r;
out = 0;
<<check_and_update_frequency>>
<<compute_random_value>>
<<check_and_produce_impulse>>
return out;
}
Before any computation can be begin, the frequency parameter is checked and updated if needed. It's at this point that the threshold value is updated by dividing the frequency by the sampling rate. A frequency set at the sampling rate will generate a random value at every sample, effectively making white noise. If the frequency is 0 or negative, no values will ever be generated, and the output will only be silence.
if (sp->freq != sp->pfreq) {
sp->pfreq = sp->freq;
sp->thresh = sp->freq / (SKFLT)sp->sr;
}
For every sample, a random number is generated and checked against the threshold. If it is less than the threshold, an impulse will be fired with a random amplitude.
r = randval(sp);
if (r < sp->thresh) out = (2 * randval(sp)) - 1;