Features Studio Pricing Docs — Studio Docs — Music Player Docs Tunings Reference Compare Blog About Support
↓ Download iOS / iPadOS macOS visionOS Companion App

The component library

Two libraries.

Nodes are the operations you write inside a graph. They compose into an expression and fuse into a single unit of work. See the ASL guide.

AudioMaterials are complete, named, parameterised units built out of those nodes. They drop onto a track.

import { lowpassMaterial, filter } from 'audiocrate';

track.materials.add(lowpassMaterial);               // the AudioMaterial
graph: ({ input }) => filter.lowpass(input, { cutoff: 800 });   // the node

Almost every AudioMaterial in this document is a few lines of ASL. User-defined AudioMaterials sit alongside them.

The editor palette (every kind, jack, range, default, and file slot) is in the materials catalog. That file is generated from the catalog. This page is the library overview and the ASL node list.

AudioMaterials

Every AudioMaterial below is exported by name. An AudioMaterial carries a kind, a parameter schema, and a graph. Pure-ASL AudioMaterials need no registration: adding one to a track is enough. Only AudioMaterials with files, presets, reported latency, or bound DSP need to be registered, and only one core AudioMaterial has any of those.

Parameters listed empty means the AudioMaterial has no controls. Each parameter also declares what kind of control it wants, which a host reads through describeAudioMaterial; the tables below mark the ones that are not plain faders.

Sources

Note-driven or free-running. These read no input.

AudioMaterialParameters
oscillatorMaterialtype, width, octave, detune, gain, ADSRPolyphonic note-driven oscillator. The envelope is live, like SynthVoice’s and Wavetable’s
toneMaterialfreq, gainFree-running sine. Sounds on Play without a gate
noiseMaterialcolor, gain, cutoffSix spectral colors through a lowpass
impulseMaterialA single-sample impulse from a gate
wavetableMaterialposition, octave, detune, gain, ADSRScans a bank of single-cycle frames. createWavetableMaterial, setWavetableAsset
samplePlayerMaterialrate, start, loop, pitch, gainOne-shot or loop from a loaded file. Pitch is semitones. createSamplePlayerMaterial, setSampleAsset
synthVoiceMaterialwave, unison, filter, env amt, ADSRA complete subtractive voice

Filters

AudioMaterialParameters
lowpassMaterialcutoff, q
highpassMaterialcutoff, q
bandpassMaterialcutoff, q
notchMaterialcutoff, q
allpassMaterialcutoff, qPhase without amplitude
lowShelfMaterialfreq, gainDb, q
highShelfMaterialfreq, gainDb, q
parametricEqMaterialHP/LP plus low, two mids, highChannel EQ. Low/high are shelf or peak. Dry at 0 dB with filters off.
onePoleLowpassMaterialcutoffCheap, stable when modulated
onePoleHighpassMaterialcutoff
svfLowpassMaterialcutoff, qA topology that stays stable under fast modulation
svfHighpassMaterialcutoff, q
svfBandpassMaterialcutoff, q
ladderMaterialcutoff, resonance, driveResonant ladder. drive saturates into the filter, and 1 is exactly clean
combMaterialfreq, feedback, mixTuned comb
slopeLowpass12MaterialcutoffFixed slope, no resonance control
slopeLowpass24Materialcutoff
slopeHighpass12Materialcutoff
dcBlockerMaterialRemoves offset

Several responses appear more than once in different topologies. A swept lowpass and a static lowpass use different implementations.

Nonlinear

AudioMaterialParameters
softClipMaterialdriveSmooth saturation
hardClipMaterialdriveAbrupt ceiling
bitcrushMaterialbits (stepper)Amplitude resolution reduction
downsampleMaterialfactor (stepper)Time resolution reduction
fullRectifyMaterial
halfRectifyMaterial
waveshapeMaterialcurve, drive, mixTransfer curve. Mix at 0 is dry
ringModMaterialfreq, mix
syncedTremoloMaterialdivision (menu), depthAmplitude modulation locked to the beat

Dynamics

AudioMaterialParameters
compressorMaterialthreshold, ratio, attack, release, makeup, mixMix at 0 is dry
limiterMaterialthreshold, attack, release
gateMaterialthreshold, attack, release, holdhold is the shortest time it may stay open, so decaying material does not chatter it
expanderMaterialthreshold, ratio, attack, release
transientMaterialattack, sustainShapes attack and sustain independently of level
envelopeFollowerMaterialattack, releaseOutput is a control signal, not audio

Time

AudioMaterialParameters
delayMaterialtimeSec, feedback, mixMix at 0 is dry
reverbMaterialsize, decay, damp, mixComb tank. Mix at 0 is dry
reverseMaterialtimeSecPlays fixed-length windows backwards
looperMaterialrecord, play, overdub, undo, length (bars, 0 = free), threshold, quantize (Free/Beat/Bar), clear, mixRecord captures. Play starts, restarts, or closes a take. Bars 0 is free up to the ring. N bars auto-closes on that many measures. Threshold 0 is off. Quantize snaps a free take. Outlets: audio, start (first play sample and each wrap), end (last sample of the loop). Mix at 0 is dry
pitchShiftMaterialpitch (st), mixMix at 0 is dry
grainMaterialduration, position, rateGranular playback of a supplied buffer. createGrainMaterial, setGrainAsset
syncedDelayMaterialdivision (menu), feedback, mixDelay time is a note length, so it survives a tempo change
irMaterialmix, gainConvolution. createIrMaterial, setIrAsset, irLatencySamples

irMaterial is the one core AudioMaterial with a plugin (irPlugin), because it loads a file and reports latency. registerCoreMaterials() registers it.

Control

None of these produce audio. They produce numbers that drive things that do.

AudioMaterialParameters
controlMaterialvalueA constant, so an automation lane has something to write to
offsetMaterialamountAdds a constant
slewMaterialrise, fallRate limiter, with asymmetric rise and fall
sampleHoldMaterialfreq, clock (jack)freq at 0 means somebody else’s clock
transportMaterialbpm, beatsPerBar, beatUnit (steppers)Host tempo and time signature. Params publish the session clock. The graph outlet is a quarter-note pulse. The editor also exposes bpm, beats, bars, playing, beatsPerBar, beatUnit, and pulse jacks
clockMaterialfreq, reset (jack)Free-running pulse train
clockDivideMaterialfactor (stepper), reset (jack)
clockMultiplyMaterialfactor (stepper), reset (jack)
triggerMaterialthresholdAn edge when the input crosses
pulseMaterialwidthSecFixed-width pulse from an edge
flipFlopMaterialToggles on each edge
compareGreaterMaterialthreshold
compareLessMaterialthreshold
logicAndMaterialother
logicOrMaterialother
logicXorMaterialother
logicNotMaterial
quantizeMaterialroot, scale (menus)Snaps a pitch signal to a scale. Note names and scale names, not indices
euclideanMaterialsteps, hits, rotation (steppers), clock and reset (jacks)Evenly spread hits across a step count
sequencerMaterialstep0 to step7, clock and reset (jacks)Advances through eight values on a clock
randomSteppedMaterialfreqNew value per tick
randomSmoothMaterialfreqInterpolated between values
adsrMaterialattack, decay, sustain, release, amountNote-driven. Times are live. gate is a live audio inlet; noteOn still opens it when that inlet is silent
dahdsrMaterialdelay, attack, hold, decay, sustain, release
syncedRampMaterialdivision (menu), depthA 0..1 ramp locked to the grid
syncedClockMaterialdivision (menu)A pulse on each division boundary, for the sequencer nodes. Reads the host snapshot. Does not set bpm
lfoMaterialtype, width, rate, amount, phase, reset (jack)Same eight waves as the oscillator, at control rate. phase offsets where the shape is read without moving where the cycle has got to
breakpointEnvelopeMaterialtime0..time3, level0..level3Four live points from a gate

This is the largest category.

Jacks a cable cannot drive. Nine inlets the palette draws accept a cable, save it in the document, and have no effect on the sound. They are two different things wearing one face.

gate on SynthVoice, Oscillator, and Wavetable belongs to the voice allocator rather than to the graph: it is a flag on the render state, not a value a node can read. It stays on the canvas because a keyboard cable into it is not a signal, it is the statement that this module is what the keys play, and flatten reads it exactly that way while contributing no node. To gate a voice from inside a patch, drive its gain or velocity; both are live. adsrMaterial is the exception: its gate is a live audio inlet.

The five position parameters on the two spatial modules (spatialsource.x/y/z, spatialmaster.yaw/pitch) are real controls whose effect lives outside the graph: an automation lane writes them, they are saved with the patch, and the host’s spatial renderer acts on them. What cannot reach them is a cable. That is a gap in those two modules rather than in the palette, and removing the jacks would hide a control that works to conceal one route into it that does not.

To gate an envelope from inside a patch, cable into adsrMaterial’s gate or use dahdsrMaterial, whose trigger is input. breakpointEnvelopeMaterial and samplePlayerMaterial take theirs the same way. The remaining dead-jack list is pinned by DeadJackTests in the Swift package, which measures it rather than trusting it: a new dead jack fails, and so does fixing one.

Reset. The five modules that carry a position in a pattern (Clock, Clock Divide, Clock Multiply, Euclidean, Sequencer) take a reset jack. A rising edge puts the module back to its first step; it is edge-triggered, so a signal held high resets once rather than pinning the pattern at step zero.

Patch the Transport’s playing outlet into it and the pattern starts with the song. That cable is the point of the jack: a free-running clock’s phase has nothing to do with the song, and a step counter under even a Synced Clock is wherever it was left, so without a reset a pattern comes back rotated by however far it had got when somebody last pressed Stop. Synced Clock and Synced Ramp need no reset, being read off the song position with no state of their own, but anything counting steps downstream of one does.

transportMaterial publishes the session clock. Clock is free-running Hz. Synced Clock reads the host snapshot and does not set bpm. Synced Delay and Looper need some host to publish tempo. Flatten treats Transport as host I/O: cables from its named outlets become transport.* reads, and the knobs are not exported as plugin params.

MIDI In (midiin) and MIDI Out (midiout) are the same kind of host I/O as Line and Master. Device ids are strings the host stores. They name a port on this machine and must not be sent over a multiplayer session: stripHostLocalPatch / restoreHostLocalPatch. MIDI channel, message, and width are patch settings and do sync. Flatten skips the nodes. MIDI In is last-note analog, like Keyboard, and a note/gate cable into an instrument allocates voices. MIDI Out is a sink: gate holds the voice, trig is the event (a pulse plus a note). If gate is also patched, trig retriggers only while gate is high. If gate is not patched, the note lasts until trig falls or widthSec, whichever is later.

Routing

AudioMaterialParameters
gainMaterialgain
invertMaterialFlips sign
bypassMaterialPasses through unchanged. Useful as a placeholder in a chain
mixMateriala, b, cWeighted sum of three inputs
selectMaterialother, which (switch)Chooses between two signals
panLeftMaterialpan
panRightMaterialpan

Stereo

Each of these is one expression over the two channel values.

AudioMaterialParameters
monoSumMaterialFold to mono, at the level a correlated pair started at
monoLeftMaterialLeft to both sides
monoRightMaterialRight to both sides
channelSwapMaterial
stereoWidthMaterialwidth0 collapses, 1 is unity, above widens. The centre never moves
midSideEncodeMaterial
midSideDecodeMaterial
balanceMaterialleft, rightIndependent level per side
stereoPanMaterialpanTrue stereo pan, not two mono gains
autoPanMaterialrate, depth
haasMaterialdelayMsWidth from a short one-sided delay
stereoMergeMaterialTwo live inputs into one stereo pair. createStereoMergeMaterial

Sidechain

These read a second live input. Declare where it comes from with material.setAudioSource('sidechain', ...); see the ASL guide.

AudioMaterialParameters
sidechainCompressorMaterialthreshold, ratio, attack, releaseCompresses one signal by another’s level
duckerMaterialamount, attack, releaseOne control
sidechainGateMaterialthreshold, attack, release, holdOpens when the detector does
audioMultiplyMaterialmixRing modulation by a live signal
audioMixMateriallevelSums a second input
crossfadeMaterialposition
inputSelectMaterialwhich (switch)Hard switch between two live inputs

Each has a factory (createDuckerMaterial and so on) because a sidechain AudioMaterial holds a routing declaration, and two duckers on two tracks need two instances rather than one shared object.

Measurement

These pass audio through unchanged and report what went by.

AudioMaterialParameters
meterMaterialPeak and level, also as CV (peak, rms). createMeterMaterial, METER_TAP
scopeMaterialA window of samples, plus peak / rms CV. createScopeMaterial, SCOPE_TAP
analyzerMaterialMeter plus spectrum. CV: note, cv (1V/oct), hz, cents, gate, peak, rms, lufs. createAnalyzerMaterial
tunerMaterialSame capture, pitch-first. CV: note, cv, hz, cents, gate. createTunerMaterial
rmsMaterialwindowSecLevel as a control signal inside the graph
peakMaterialrelease
onsetMaterialthresholdFires on transients

The first four send data out to the main thread and cost nothing until something reads them. The last three stay inside the graph and drive other nodes.

Nodes

What you write inside graph. Numbers are accepted anywhere a value is.

Every value is chainable:

.mul(x) .add(x)Arithmetic
.range(min, max)Map from -1..1 into a range
.toFrequency()Note number to hertz
.trigger(velocity)Gate an envelope

Sources

osc({ freq, type?, width? })        // sine, saw, square, triangle
lfo({ rate, shape?, width?, phase?, reset? })
noise({ color? })                  // white, pink, brown
impulse({ gate? })
wavetable({ freq, table?, position?, frameSize? })
samplePlay({ rate?, gate?, position?, loop?, pitch?, table? })
uniform(x)                         // a constant or an external value

Filters

filter.lowpass(x, { cutoff, q? })
filter.highpass(x, { cutoff, q? })
filter.bandpass(x, { cutoff, q? })
filter.notch(x, { cutoff, q? })
filter.allpass(x, { cutoff, q? })
filter.peaking(x, { freq, gainDb, q? })
filter.lowshelf(x, { freq, gainDb, q? })
filter.highshelf(x, { freq, gainDb, q? })
filter.onePoleLowpass(x, { cutoff })
filter.onePoleHighpass(x, { cutoff })
filter.svf(x, { cutoff, q?, mode? })
filter.ladder(x, { cutoff, resonance?, drive? })
filter.comb(x, { freq, feedback?, mix? })
filter.slope(x, { cutoff, poles?, mode? })
dcBlock(x)

Envelopes

env.adsr({ a, d, s, r })
env.dahdsr({ delay?, attack?, hold?, decay?, sustain?, release?, gate? })
env.breakpoints({ times, levels, gate? })

Nonlinear

clip(x, { drive?, mode? })         // soft or hard
bitcrush(x, { bits? })
downsample(x, { factor? })
rectify(x, { mode? })
waveshape(x, { curve? })

Time

delay(x, { timeSec, feedback?, mix?, maxTimeSec? })
reverse(x, { timeSec?, maxTimeSec? })
looper(x, { record?, play?, overdub?, undo?, duration?, bars?, clear?, threshold?, quantize?, fadeSec?, latencySec?, maxTimeSec?, box?, field? })
createLooperBox()
// field: 'audio' (default), 'start', or 'end'. Share `box` so those reads tick once.
pitchShift(x, { pitch?, unit? })
grain(triggerIn, { duration?, position?, rate?, table? })

Dynamics

compressor(x, { threshold?, ratio?, attack?, release?, sidechain? })
expander(x, { threshold?, ratio?, attack?, release? })
transient(x, { attack?, sustain? })
envFollow(x, { attack?, release? })

Control

clock({ freq?, reset? })
clockDivide(x, { factor?, reset? })
clockMultiply(x, { factor?, reset? })
trigger(x, { threshold? })
pulse(x, { widthSec? })
flipFlop(x)
compare(x, { threshold?, mode? })
logic.and(a, b)   logic.or(a, b)   logic.xor(a, b)   logic.not(a)
quantize(x, { root?, scale? })
euclidean(clockIn, { steps?, hits?, rotation?, reset? })
sequencer(clockIn, [ ... ], { reset? })
random({ freq?, mode? })
slew(x, { rise?, fall? })
sampleHold(x, { freq?, clock? })

quantizeToScale, euclideanPattern, and QUANTIZE_SCALES are exported too, so an interface can show what a graph is about to do before it does it.

Analysis

rms(x, { windowSec? })
peak(x, { release? })
onset(x, { threshold? })
pitch(x, { field? })   // hz | midi | cents | gate. Hop-held zero-crossing follow.

Routing and stereo

mix(a, b, c, ...)
select(a, b, { which })
panLaw(x, { pan, channel })
audio.input(name?)        audio.left(name?)   audio.right(name?)
audio.sidechain()         audio.lane()

The transport

transport.beats()           transport.bars()
transport.bpm()             transport.playing()
transport.beatsPerBar()     transport.beatUnit()
transport.phase(lengthBeats)
transport.pulse(lengthBeats)
transport.seconds(lengthBeats)
transport.division(index, { divisions })

beats and bpm are quarter-note. bars uses beatsPerBar and beatUnit. barBeats / normalizeBeatUnit are that conversion. SYNC_DIVISIONS names note lengths in quarter-note beats; COMMON_DIVISION_NAMES and COMMON_DIVISION_BEATS are the menu and lookup table a synced AudioMaterial pairs. See the ASL guide.

Measurement and kernels

tap.meter(x, { id })
tap.capture(x, { id, windowSize? })
kernel.seam(slot, x)
kernel.source(slot, x, { fallback })

See the ASL guide for taps and the kernels guide for slots.

What is missing

  • A tempo-synced reverb, chorus or phaser. The transport nodes exist and the four synced AudioMaterials are examples rather than a complete set. There is a free-running reverbMaterial and a Space Reverb kernel.
  • A published inspector. Every AudioMaterial here describes its own controls through describeAudioMaterial. Audiocrate does not yet ship the component that draws them.
  • Parameter kinds are declared but not everywhere they could be. The core library uses menus, switches, and steppers where those match the control. A plugin package’s forty-parameter tree is still mostly ranges.
  • Multi-output. An AudioMaterial has one output, so a splitter is a graph shape rather than a node. stereoMergeMaterial shows the merge direction works.