io7m | single-page | multi-page | epub | Aradine User Manual

Aradine User Manual

CREATOR Mark Raynsford
DATE 2023-02-26T20:18:22+00:00
DESCRIPTION Documentation for the Aradine system.
IDENTIFIER ded07ce8-21a9-4226-bfb8-261e7a1895f7
LANGUAGE en
RIGHTS Public Domain
TITLE Aradine User Manual
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:

2.2.2.1.3. Order 8

final var stage0 = new ARBQ1BiquadBPFO2();
final var stage1 = new ARBQ1BiquadBPFO2();
final var stage2 = new ARBQ1BiquadBPFO2();
final var stage3 = new ARBQ1BiquadBPFO2();

final var s0 = stage0.processOneFrame(input);
final var s1 = stage1.processOneFrame(s0);
final var s2 = stage2.processOneFrame(s1);
final var out = stage3.processOneFrame(s2);
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 filter implementation is loosely based upon the C++ implementation provided at https://www.earlevel.com/main/2012/11/26/biquad-c-source-code/.
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 filter implementations are based on the descriptions given in The Scientist and Engineer's Guide to Digital Signal Processing by Steven W. Smith (ISBN-13 978-0966017632).
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.

2.4.2.2.4. Oversampling

final var input = sampleBuffer.frameGetExact(index);
for (int oversample = 0; oversample < sampleCount; ++oversample) {
  f.processOneFrame(input);
}
final var output = f.highPassOutput();
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.
io7m | single-page | multi-page | epub | Aradine User Manual