README

dowdiness/moondsp/dsp does not have a README file

#
ArithSym

pub(open) trait ArithSym {
fn constant(Double) -> Self
fn mul(Self, Self) -> Self
fn mix(Self, Self) -> Self
fn clip(Self, Double) -> Self
}

Core algebra for the Finally Tagless DSP representation. Downstream packages implement this trait to provide different interpretations (evaluation, code generation, pretty printing, etc.).

#
ChannelSpec

pub trait ChannelSpec {
fn channel_count(Self) -> Int
}

#
DelaySym

pub(open) trait DelaySym : DspSym {
fn delay(Self, Int, Int, Double) -> Self
}

#
DspSym

pub(open) trait DspSym : ArithSym {
fn oscillator(Self, Waveform) -> Self
fn noise(UInt) -> Self
fn adsr(Double, Double, Double, Double) -> Self
fn gain(Self, Double) -> Self
fn output(Self) -> Self
}

#
FilterSym

pub(open) trait FilterSym : DspSym {
fn biquad(Self, BiquadMode, Double, Double) -> Self
}

#
StereoDelaySym

pub(open) trait StereoDelaySym : StereoSym + DelaySym {
fn stereo_delay(Self, Int, Int, Double) -> Self
}

#
StereoFilterSym

pub(open) trait StereoFilterSym : StereoSym + FilterSym {
fn stereo_biquad(Self, BiquadMode, Double, Double) -> Self
}

#
StereoSym

pub(open) trait StereoSym : DspSym {
fn pan(Self, Double) -> Self
fn stereo_gain(Self, Double) -> Self
fn stereo_clip(Self, Double) -> Self
fn stereo_mixdown(Self) -> Self
fn stereo_output(Self) -> Self
}

#
Adsr

pub struct Adsr {
// private fields
}
#alias(new)
fn Adsr::Adsr(attack_ms~ : Double, decay_ms~ : Double, sustain~ : Double, release_ms~ : Double) -> Adsr

Stateful ADSR envelope generator.

#
Adsr::gate_off

fn Adsr::gate_off(self : Adsr) -> Unit

Release the current note from the envelope's present level.

#
Adsr::gate_on

fn Adsr::gate_on(self : Adsr) -> Unit

Start a new note and enter the attack stage from silence.

#
Adsr::level

fn Adsr::level(self : Adsr) -> Double

Return the current envelope level.

#
Adsr::process

fn Adsr::process(self : Adsr, context : DspContext, output : AudioBuffer) -> Unit

Fill a buffer with successive envelope values.

#
Adsr::reset

fn Adsr::reset(self : Adsr) -> Unit

Reset the envelope to idle silence.

#
Adsr::stage

fn Adsr::stage(self : Adsr) -> EnvStage

Return the current envelope stage.

#
Adsr::tick

fn Adsr::tick(self : Adsr, context : DspContext) -> Double

Advance the envelope by one sample using the given DSP context.

#
AudioBuffer

pub struct AudioBuffer {
// private fields
}
#alias(new)
fn AudioBuffer::AudioBuffer(data : FixedArray[Double]) -> AudioBuffer

Thin wrapper around FixedArray[Double] for DSP block processing.

#
AudioBuffer::adopt

fn AudioBuffer::adopt(data : FixedArray[Double]) -> AudioBuffer

Wrap a FixedArray[Double] without copying.

The buffer and the source array share storage in both directions: writes through the buffer (set / fill) mutate the source, and mutations through the source handle appear through the buffer. Two specific bypasses follow from this: (a) the buffer's initial contents are whatever the source array holds at adoption time, not run through the normal non-finite-to-0.0 normalization path; and (b) any later mutation through the retained source handle skips AudioBuffer validation entirely. Writes through buf.set(...) or buf.fill(...) on an adopted buffer still go through those methods and normalize non-finite values to 0.0. Use this only for FFI-bridged buffers (e.g., TypedArray or SharedArrayBuffer wrappers) where the copy cost is genuinely prohibitive and the caller can reason about the source lifetime. Otherwise use AudioBuffer::new.

#
AudioBuffer::all

#alias(every)
fn AudioBuffer::all(self : AudioBuffer, predicate : (Double) -> Bool raise?) -> Bool raise?

Test whether predicate holds for every sample in the buffer.

#
AudioBuffer::any

#alias(exists)
fn AudioBuffer::any(self : AudioBuffer, predicate : (Double) -> Bool raise?) -> Bool raise?

Test whether predicate holds for at least one sample in the buffer.

#
AudioBuffer::fill

fn AudioBuffer::fill(self : AudioBuffer, value : Double) -> Unit

Fill the buffer with a single sample value.

Non-finite values (NaN, +Inf, -Inf) are normalized to 0.0.

#
AudioBuffer::filled

fn AudioBuffer::filled(size : Int, init? : Double) -> AudioBuffer

Construct an audio buffer pre-filled with init (default 0.0).

The buffer's storage is freshly allocated and not shared with any caller-visible array. Routed through adopt to avoid the unnecessary copy AudioBuffer::new would now perform on the freshly-allocated FixedArray. The initializer is normalized once before allocation: non-finite values become 0.0, while finite values pass through unchanged.

#
AudioBuffer::get

fn AudioBuffer::get(self : AudioBuffer, index : Int) -> Double

Read one sample from the buffer.

#
AudioBuffer::length

fn AudioBuffer::length(self : AudioBuffer) -> Int

Get the number of samples stored in the buffer.

#
AudioBuffer::set

fn AudioBuffer::set(self : AudioBuffer, index : Int, value : Double) -> Unit

Write one sample into the buffer.

Non-finite values (NaN, +Inf, -Inf) are normalized to 0.0.

#
Biquad

pub struct Biquad {
// private fields
}
#alias(new)
fn Biquad::Biquad() -> Biquad

Stateful biquad filter using Direct Form II Transposed processing.

#
Biquad::process

fn Biquad::process(self : Biquad, context : DspContext, buffer : AudioBuffer) -> Unit

Filter one block of samples in place.

#
Biquad::reset

fn Biquad::reset(self : Biquad) -> Unit

Clear the filter delay state without changing the current coefficients.

#
Biquad::tick

fn Biquad::tick(self : Biquad, input : Double) -> Double

Filter one input sample and advance the internal delay state.

#
Biquad::update

fn Biquad::update(self : Biquad, context : DspContext, mode : BiquadMode, cutoff : Double, q : Double) -> Bool

Recalculate filter coefficients for the given mode, cutoff, and resonance.

Returns false and disables processing when the parameters are invalid.

#
BiquadMode

pub(all) enum BiquadMode {
LowPass
HighPass
BandPass
} derive(Eq)

Supported biquad response shapes for the Phase 1 filter primitive.

#
Clip

pub struct Clip {
}
#alias(new)
fn Clip::Clip() -> Clip

Stateless hard-clipping processor for explicit range limiting.

#
Clip::process

fn Clip::process(self : Clip, context~ : DspContext, buffer~ : AudioBuffer, threshold~ : Double) -> Unit

Apply hard clipping in place using a symmetric threshold.

#
DelayLine

pub struct DelayLine {
// private fields
}
#alias(new)
fn DelayLine::DelayLine(max_delay_samples : Int, delay_samples? : Int, feedback? : Double) -> DelayLine

Stateful integer-sample delay line backed by a circular buffer.

#
DelayLine::delay_samples

fn DelayLine::delay_samples(self : DelayLine) -> Int

Return the active delay length in samples.

#
DelayLine::feedback

fn DelayLine::feedback(self : DelayLine) -> Double

Return the active feedback amount.

#
DelayLine::max_delay_samples

fn DelayLine::max_delay_samples(self : DelayLine) -> Int

Return the maximum representable delay length in samples.

#
DelayLine::process

fn DelayLine::process(self : DelayLine, context : DspContext, buffer : AudioBuffer) -> Unit

Process one block of samples in place.

#
DelayLine::reset

fn DelayLine::reset(self : DelayLine) -> Unit

Clear the circular buffer state without changing the configured delay.

#
DelayLine::set_delay_samples

fn DelayLine::set_delay_samples(self : DelayLine, delay_samples : Int) -> Unit

Update the active delay length, clamping to the allocated storage range.

#
DelayLine::set_feedback

fn DelayLine::set_feedback(self : DelayLine, feedback : Double) -> Unit

Update the feedback amount, clamping to the stable supported range.

#
DelayLine::tick

fn DelayLine::tick(self : DelayLine, input : Double) -> Double

Process one input sample and return the delayed output sample.

#
DemoSource

pub struct DemoSource {
// private fields
}
#alias(new)
fn DemoSource::DemoSource() -> DemoSource

Encapsulates the mutable oscillator + noise state used by the browser demo tick functions. Each WASM entry point (root, browser/, browser_test/) creates its own instance so module-level mutable globals stay per-package.

#
DemoSource::reset

fn DemoSource::reset(self : DemoSource) -> Unit

Reset oscillator and noise state to the start of the waveform.

#
DemoSource::tick_source

fn DemoSource::tick_source(self : DemoSource, source_id~ : Int, freq_hz~ : Double, sample_rate~ : Double) -> Double

Generate one sample from the selected source.

Source ids:
  • 0: sine (default)
  • 1: saw
  • 2: square
  • 3: triangle
  • 4: noise

#
DspContext

pub struct DspContext {
// private fields
}
#alias(new)
fn DspContext::DspContext(sample_rate~ : Double, block_size~ : Int) -> DspContext

Shared execution context for block-based DSP processing.

#
DspContext::block_size

fn DspContext::block_size(self : DspContext) -> Int

Return the configured block size for this processing context.

#
DspContext::make_buffer

fn DspContext::make_buffer(self : DspContext, init? : Double) -> AudioBuffer

Create an owned audio buffer sized to this context's block size.

#
DspContext::sample_rate

fn DspContext::sample_rate(self : DspContext) -> Double

Return the configured sample rate for this processing context.

#
EnvStage

pub enum EnvStage {
Idle
Attack
Decay
Sustain
Release
} derive(Eq)

Public ADSR stage names for diagnostics and tests.

#
Gain

pub struct Gain {
}
#alias(new)
fn Gain::Gain() -> Gain

Stateless in-place gain processor for Phase 1 block processing.

#
Gain::process

fn Gain::process(self : Gain, context~ : DspContext, buffer~ : AudioBuffer, amount~ : Double) -> Unit

Apply a scalar gain to a block of audio samples in place.

#
Mix

pub struct Mix {
}
#alias(new)
fn Mix::Mix() -> Mix

Stateless in-place mixer for combining audio buffers.

#
Mix::process

fn Mix::process(self : Mix, context : DspContext, output : AudioBuffer, input : AudioBuffer) -> Unit

Add one input buffer into the output buffer without implicit clipping.

#
Mono

pub struct Mono {
}
#alias(new)
fn Mono::Mono() -> Mono

impl ChannelSpec for Mono

#
Noise

pub struct Noise {
// private fields
}
#alias(new)
fn Noise::Noise(seed : UInt) -> Noise

Deterministic white-noise source with explicit RNG state.

#
Noise::process

fn Noise::process(self : Noise, context : DspContext, output : AudioBuffer) -> Unit

Fill an output buffer with white-noise samples.

#
Noise::reset

fn Noise::reset(self : Noise, seed : UInt) -> Unit

Reset the generator to a new explicit seed.

#
Noise::seed

fn Noise::seed(self : Noise) -> UInt

Expose the current RNG state for testing and diagnostics.

#
Noise::tick

fn Noise::tick(self : Noise) -> Double

Generate one white-noise sample in the range [-1.0, 1.0].

#
Oscillator

pub struct Oscillator {
// private fields
}
#alias(new)
fn Oscillator::Oscillator() -> Oscillator

Stateful oscillator for the first reusable Phase 1 DSP primitive.

#
Oscillator::phase

fn Oscillator::phase(self : Oscillator) -> Double

Expose the current phase for testing and diagnostics.

#
Oscillator::process

fn Oscillator::process(self : Oscillator, context~ : DspContext, output~ : AudioBuffer, freq_hz~ : Double) -> Unit

Fill an output buffer with oscillator samples.

#
Oscillator::process_waveform

fn Oscillator::process_waveform(self : Oscillator, context~ : DspContext, output~ : AudioBuffer, waveform~ : Waveform, freq_hz~ : Double) -> Unit

Fill an output buffer with samples for the selected waveform.

#
Oscillator::reset

fn Oscillator::reset(self : Oscillator) -> Unit

Reset the oscillator to the start of the waveform.

#
Oscillator::tick

fn Oscillator::tick(self : Oscillator, freq_hz~ : Double, sample_rate~ : Double) -> Double

Generate one sine sample and advance the oscillator state.

#
Oscillator::tick_waveform

fn Oscillator::tick_waveform(self : Oscillator, waveform~ : Waveform, freq_hz~ : Double, sample_rate~ : Double) -> Double

Generate one sample for the selected waveform and advance the oscillator state.

#
Pan

pub struct Pan {
}
#alias(new)
fn Pan::Pan() -> Pan

Stateless equal-power pan processor for mono-to-stereo routing.

#
Pan::process

fn Pan::process(self : Pan, context~ : DspContext, input~ : AudioBuffer, left_output~ : AudioBuffer, right_output~ : AudioBuffer, position~ : Double) -> Unit

Pan a mono input buffer into explicit left and right output buffers.

Finite positions are clamped to [-1.0, 1.0], where -1.0 is hard left, 0.0 is center, and 1.0 is hard right. Invalid positions or contexts write silence to both outputs.

#
ParamSmoother

pub struct ParamSmoother {
// private fields
}
#alias(new)
fn ParamSmoother::ParamSmoother(initial : Double, smoothing_ms : Double, sample_rate : Double) -> ParamSmoother

Stateful one-pole smoother for click-free control changes.

#
ParamSmoother::current

fn ParamSmoother::current(self : ParamSmoother) -> Double

Return the current smoothed value.

#
ParamSmoother::from_context

fn ParamSmoother::from_context(initial : Double, smoothing_ms : Double, context : DspContext) -> ParamSmoother

Create a parameter smoother using the sample rate stored in a DSP context.

#
ParamSmoother::reset

fn ParamSmoother::reset(self : ParamSmoother, value : Double) -> Unit

Immediately snap both the current and target value to a new point.

#
ParamSmoother::set_smoothing_time

fn ParamSmoother::set_smoothing_time(self : ParamSmoother, smoothing_ms : Double, sample_rate : Double) -> Unit

Update the smoothing time using an explicit sample rate.

#
ParamSmoother::set_smoothing_time_from_context

fn ParamSmoother::set_smoothing_time_from_context(self : ParamSmoother, smoothing_ms : Double, context : DspContext) -> Unit

Update the smoothing time using the sample rate stored in a DSP context.

#
ParamSmoother::set_target

fn ParamSmoother::set_target(self : ParamSmoother, value : Double) -> Unit

Set a new target value. Invalid inputs are ignored to keep the state deterministic inside the audio path.

#
ParamSmoother::target

fn ParamSmoother::target(self : ParamSmoother) -> Double

Return the current target value.

#
ParamSmoother::tick

fn ParamSmoother::tick(self : ParamSmoother) -> Double

Advance the smoother by one sample.

#
Stereo

pub struct Stereo {
}
#alias(new)
fn Stereo::Stereo() -> Stereo

#
Waveform

pub(all) enum Waveform {
Sine
Saw
Square
Triangle
} derive(Eq)

Supported oscillator waveforms for the Phase 1 source primitive.

#
effective_sample_count

fn effective_sample_count(context : DspContext, buffer : AudioBuffer) -> Int

Compute the effective number of samples to process, bounded by both the context block size and the buffer length.

#
exit_deliverable

fn[T : FilterSym + DspSym + ArithSym] exit_deliverable() -> T

The Phase 2 exit deliverable: sine(2).range(200,400).sine().lpf(800,1).out() An LFO at 2 Hz modulates the frequency of a carrier oscillator between 200-400 Hz, then filtered through a low-pass biquad at 800 Hz.

#
is_finite

fn is_finite(value : Double) -> Bool

Check whether a floating-point value is finite (not NaN, not Inf).

#
is_finite_positive

fn is_finite_positive(value : Double) -> Bool

Check whether a floating-point value is finite and strictly positive.

#
lin_map

fn[T : ArithSym] lin_map(input : T, in_lo : Double, in_hi : Double, out_lo : Double, out_hi : Double) -> T

Maps [in_lo, in_hi] -> [out_lo, out_hi]. Only requires ArithSym. Precondition: in_lo != in_hi (division by zero otherwise).

#
max_feedback_amount

fn max_feedback_amount() -> Double

#
mono_shape

fn mono_shape() -> Int

Bridge to existing MONO_SIGNAL_SHAPE constant (value 0).

#
pan_left_gain

fn pan_left_gain(position : Double) -> Double

Equal-power left-channel gain for a pan position in [-1.0, 1.0]. -1.0 = hard left (gain 1.0), 0.0 = center (~0.707), 1.0 = hard right (gain 0.0). Non-finite positions return 0.0.

#
pan_right_gain

fn pan_right_gain(position : Double) -> Double

Equal-power right-channel gain for a pan position in [-1.0, 1.0]. -1.0 = hard left (gain 0.0), 0.0 = center (~0.707), 1.0 = hard right (gain 1.0). Non-finite positions return 0.0.

#
range

fn[T : ArithSym] range(input : T, lo : Double, hi : Double) -> T

Maps [-1, 1] -> [lo, hi]. Only requires ArithSym.

#
sanitize_buffer

fn sanitize_buffer(buffer : AudioBuffer, sample_count : Int) -> Int

Replace non-finite samples (NaN, Inf) with 0.0 in-place. Returns the number of samples replaced. This is the output firewall — the last line of defense before samples leave the DSP engine.

#
stereo_shape

fn stereo_shape() -> Int

Bridge to existing STEREO_SIGNAL_SHAPE constant (value 1).