The aradine package provides a modular system for digital signal processing
with an emphasis on musical applications.
The com.io7m.aradine.envelope.table1 module contains a table-based envelope
implementation.
The envelope supports a number of different interpolation functions to produce amplitude
values between
breakpoints. For a hypothetical breakpoint B at frame
frame(B)
with amplitude amplitude(B), and a hypothetical breakpoint
C
at frame frame(C) with amplitude
amplitude(C), where
frame(B) < frame(C), the interpolation function specified for
B
is used to produce interpolated amplitude values for all frames greater than
frame(B)
and less than frame(C).
The definitions below are explained in terms of a pair of breakpoints B
and C where frame(B) = 0,
amplitude(B) = 0, frame(C) = 1000,
and amplitude(C) = 1.
If a breakpoint B uses the interpolation function
CONSTANT_CURRENT, then all frames between
B
and C will have an amplitude equal to
amplitude(B).
If a breakpoint B uses the interpolation function
CONSTANT_NEXT, then all frames between
B
and C will have an amplitude equal to
amplitude(C).
If a breakpoint B uses the interpolation function
COSINE, then all frames between
B
and C will have amplitudes produced by a cosine interpolation of
amplitude(B)
and amplitude(C).
If a breakpoint B uses the interpolation function
EXPONENTIAL, then all frames between
B
and C will have amplitudes produced by an exponential interpolation of
amplitude(B)
and
amplitude(C).
This is equivalent to
linear interpolation
where the interpolation factor is the square of the normal linear factor.
If a breakpoint B uses the interpolation function
LINEAR, then all frames between
B
and C will have amplitudes produced by a linear interpolation of
amplitude(B)
and
amplitude(C).
If a breakpoint B uses the interpolation function
LOGARITHMIC, then all frames between
B
and C will have amplitudes produced by a logarithmic interpolation of
amplitude(B)
and
amplitude(C).
This is equivalent to
linear interpolation
where the interpolation factor is the square root of the normal linear factor.
The com.io7m.aradine.filter.biquad1 module contains biquad filter implementations.
Biquad filters can be composed to produce stronger filters. The output signal of one
filter is fed into the
input of another filter in a process known as cascading. The main filter implementation
included in the com.io7m.aradine.filter.biquad1 module is a filter of order
2
(specifically, it has two poles, and two zeroes). It would be theoretically possible
to use the same algorithm
with more filter coefficients to produce a single filter of order N, but in
practice it is better to
cascade
N / 2
filters of order 2; the resulting filter will be much more numerically stable and
less subject to issues such as those involving floating point precision.
For example, the following code uses four filters of order 2 to produce an
effective filter of order 8:
Biquad filters are relatively suitable for use in EQ applications, or when the filter
cutoff value is not
expected to be modulated over time. For rapidly modulating filter cutoff values, a
state variable filter
may be more suitable.
The com.io7m.aradine.filter.recursive1 module contains simple recursive filter
implementations.
The filter implementations provided in the
com.io7m.aradine.filter.recursive1
module are extremely simple 1-pole filters. They represent the bare minimum in terms
of filter implementations and
may be useful in terms of eliminating low frequency grounds hum from audio, or reducing
high frequency noise.
The com.io7m.aradine.filter.statevar1 module contains state variable filter
implementations.
The filter implementations provided in the
com.io7m.aradine.filter.statevar1
module are fairly direct translations of the hardware filters used on many analogue
synthesizers. They offer
good resolution at all frequencies, but can become unstable at certain frequencies
unless
oversampling
is used.
State variable filters accept separate Q and
C
parameters, where Q is a positive real value, and C is
a real value in the range [0, 1]. Filters may become unstable in the sense that
particular combinations of
Q
and C values may cause the filter to start oscillating and massively increase the
gain of the signal passing through it. This can be directly observed by running the
filter on a sample of white
noise, picking various Q and C values, and seeing if
the resulting output contains one or more frequency bands where the amplitude is greater
than or equal to
0.6. In an unfiltered white noise sample, every frequency band should have an
amplitude of roughly 0.5, so any band with a value much greater than this can
indicate that the filter has begun to self oscillate. The following diagram shows
an increasing
Q
downwards along the Y axis, and an increasing
C
rightwards along the X axis:
In the diagram, a green square indicates that no frequency band in the output had
an amplitude of greater than
or equal to 0.6, whilst a red square indicates that one or more frequency bands had
an amplitude of greater than or equal to
0.6. As should be fairly obvious, much of the parameter space is essentially
unusable. Fortunately, this problem can be mitigated through
oversampling; simply evaluate the filter multiple times per input sample, and discard
all but the output sample from the last evaluation.
Applying the exact same
Q and
C values as in
the
original diagram, but evaluating the filter four
times instead of one, yields the following output instead:
The resulting audio output is not drastically different than for the single sample
case, but it is clear from
the diagram that a much wider range of Q and
C
values have become usable.
The filter implementation is derived from the description given in
Musical Applications Of Microprocessors
by Hal Chamberlin (ISBN 0-8104-5768-7).
The entire
aradine API is documented the included
JavaDoc.