DSP fundamentals

Understand audio I/O, samples, state, controls and the building blocks you can compose into a Maru module.

Maru’s DSP toolkit gives you the parts to build instruments, effects, samplers and musical utilities: oscillators, filters, envelopes, dynamics, delay lines, reverbs, meters, sample readers and complete reusable voices. Start with a working module, choose the pieces for your sound, then audition and refine them. You can also write your own processing alongside these shared components.

A DSP leaf is a focused processing unit with explicit inputs, state and outputs. Some APIs call that unit a node. In a Processor, you connect leaves by passing their results to the next operation. A Graph instead describes connections between the Audio SDK’s registered nodes. The DSP library’s public imports and the Graph node registry are related authoring tools with different interfaces; a leaf import does not automatically create a Graph node.

Find a building block

What you want to buildReference
Input/output trim, a mixer, pan or dry/wet controlMaths, gains and mixing
Tone control, resonant filtering, EQ or IR filteringFilters and EQ
Compression, gating, limiting, de-essing or low-band repairDynamics and repair
Echo, delay alignment or a reverberant spaceDelays and reverbs
LFO motion, chorus, pitch modulation, drive or lo-fi colourModulation and distortion
Subtractive, FM, wavetable or physical-model instrumentsOscillators, envelopes and voices
Sample instruments, loops, pads, articulations or clip playbackSampling and playback
Pitch tracking, loudness, true peak, vocoding or spectral effectsMetering and spectral processing
Sequencers, note maps, expression, musical clocks or effect chainsMIDI, clocks and composition
Room responses or prepared neural amp inferenceRoom acoustics and neural models

Each reference lists the focused toolkit imports and explains the choices that matter when connecting them. The DSP library overview also includes a biquad example, a seeded record-texture example and effect control ranges.

Samples, frames and audio I/O

A sample is one channel’s amplitude at one instant. A frame contains the channels at that instant; stereo has a left and right sample per frame. The sample rate is frames per second: at 48,000 Hz, 480 frames last 10 ms. A processing block groups frames into one call, reducing scheduling overhead.

Floating-point audio conventionally uses −1 to +1 as the nominal full-scale range. Values outside it are possible inside a chain, so choose headroom and output gain deliberately. Do not silently clamp every intermediate signal: that introduces clipping and changes the sound.

The module declares its input/output widths and MIDI ports. The host connects devices and other modules; it passes buffers and events to the Processor. DSP does not open a microphone, select a MIDI device or connect browser audio nodes. An instrument can have no audio input, an effect reads input, and an event-only utility can have no audio bus.

ProcessorBlock uses planar audio: each channel occupies a contiguous region of a shared array. Use frameOffset, frameCount and channelStride when addressing it. These describe the current slice, which may start partway through the buffer. Do not assume interleaved L/R samples or that every call begins at zero. Address a channel’s sample as channel * channelStride + frameOffset + frame. The Processor guide connects this buffer contract to a complete module.

Prepare state, then process

A filter remembers earlier samples; a delay retains a ring buffer; a voice retains its phase and envelope. Keep that state per module instance and, where required, per channel or note.

StageWhat happens
CreateAllocate persistent state, voice pools, tables and capacity.
PrepareApply the host’s sample rate/layout and derive rate-dependent coefficients or active lengths.
ConfigureMap parameters to the leaf’s setters; refresh coefficients at the documented point.
ProcessAdvance the prepared state and write audio/events for the current frames.
ResetRestore the documented transient state, such as phase, histories, held notes or transport.

A leaf may combine some of these stages or use different names. Stateless math has no allocation/reset lifecycle. Some stereo leaves return a scalar gain; others return audio or write outLeft/outRight on state. Read the selected signature before connecting its output.

Keep allocations, file access, network requests, decoding, locks and UI calls outside realtime processing. Prepare assets and candidate configurations before installing them. For large rings or IR changes, use bounded work and staged transitions where provided. A reset that clears a large buffer is a lifecycle operation, not automatically a safe live control operation.

When sample rate changes, re-prepare the rate-dependent state using the leaf’s supported lifecycle. An initializer called at an arbitrary hard-coded rate does not make its pitch, timing or filter coefficients correct at another rate.

Units and controls

Use the leaf’s declared units: Hz for a cutoff, dB for a threshold or trim, seconds/milliseconds/frames for time, and ratios or normalized values only where specified. For example, a normalized knob position of 0.5 is not 0.5 Hz and is not a −6 dB gain.

The parameter schema owns the range, default, unit and taper. The module maps that physical value into the DSP setter; the face and host automation use the same contract. Smooth a parameter where a discontinuous jump would click or produce an unintended pitch change. Use the toolkit’s smoothing or the leaf’s own transition law rather than adding an unrelated UI-only curve.

A leaf may output wet audio only, include its own dry/wet control, or generate sound with no input. Choose the product’s mix at the right point. Delay, lookahead and partitioned processing may also require dry-path alignment and a declared latency. Those choices determine both the sound and host behavior.

Imports and API discovery

ts
import { initBiquadNode, biquadNodeTick } from "maruaudio/dsp/filters/biquad"

Public projects use maruaudio/dsp/.... Repository examples may spell the same focused API as @maruaudio/dsp/.... Prefer the narrow path your Processor uses; category barrels such as filters, mixing and voices, and the root DSP barrel, are discovery conveniences. processor and processor-events expose the processing and event contracts rather than audible effects.

Use your editor’s completion and Go to Definition on an import to inspect its exported types, exact setter arguments, constants and reset behavior in the installed package. The category references distinguish algorithms, prepared-data helpers and reference processors so you can select the right layer.

Try a complete example

bash
maru init my-effect --kind processor-effect
cd my-effect
npm install
maru dev --open

The effect starter already connects a declaration, drive/tone Processor, parameters, presets, face and a test. The processor-instrument starter adds note-driven voices. Follow Define a Processor module for the file-by-file walkthrough and the checklist for maru publish. Its example discovery commands help you inspect other available modules.

The small gain, oscillator and true-peak examples in this section isolate one operation at a time; integrate them through the starter’s state and lifecycle. In a repository checkout, packages/dsp/examples/leaf-playground/ also contains focused teaching examples such as gain.ts, biquad.ts, oscillator.ts and adsr.ts. These demonstrate the leaf APIs; their preview helpers are not the Processor module contract.

Audition the musical behavior and test state, silence, extreme settings, control changes, tails and the intended sample rates. Then run the module’s checks for the selected build targets. Check, build and export explains the next step; parity and determinism explains repeatable comparisons.