VOLTAreference

Reference / Design a sound

The AS3340 oscillator

Design a sound

Open in the workbench →
On this page · 5 sections

Use a triangle-core oscillator with adjustable circuit character, sync, linear FM and optional high-quality rendering.

Start here#

source = as3340 "saw" (freq pitch) { quality = "high" }

as3340 "tri"|"saw"|"pulse" freq [pw] [character] keeps its existing positional arguments. Add a named options record for the extra controls. freq accepts Hz and the usual fm expression; pw defaults to 0.5 and character to 0.4. The core frequency clamps to 0.0001 Hz–0.22 × output sample rate in both modes. The three outputs are normalized to approximately ±1, with small transient overshoot. Each call owns a chip; selecting different outputs with separate calls does not share one oscillator state.

The physically informed model uses a direction-switched triangle integrator, a derived saw with a mid-ramp step and switching transient, and a pulse comparator with hysteresis. It is a circuit-inspired model, not a measured replica of a particular hardware instrument.

Character (0–1) and all seven detail amounts (0–2) accept live expressions, read per sample. Changes preserve chip identity, phase, noise/filter history, sync and the quality-filter delay. Zero noise depth continues the seeded trajectory, so bringing it back does not redraw a chip. Mode and quality remain structural choices.

Rendering quality#

qualityUseBehavior
"standard" (default)Inexpensive playing and existing patchesOriginal causal event correction and switching-spike response
"high"Detailed rendering, especially upper notes, PWM and syncFour complete event-correction taps, 4× internal processing and a strict output low-pass

Quality is a compile-time choice saved in the source and compiled IR. It works in live playback and exports on every engine target. It costs more CPU; it does not change the character macro or draw a different chip/noise stream. Use it on the oscillators that need it:

chipBass = patch {
  pwm = lfoSine @ 1c |> scale [0.2, 0.8],
  source = as3340 "pulse" (freq pitch) {
    pw = pwm, quality = "high", character = 0.5, jitter = 0, tracking = 0
  },
  ampEnv = adsr 2ms 110ms 0.3 120ms,
  gain = vel * 0.3
}

High quality adds 129 output samples (2.69 ms at 48 kHz): 128 for the output filter/alignment and one to reconstruct the interval between modulation samples. The engine aligns parallel voice inputs and envelopes, extends voice tails, and includes the source delay in track compensation. Feedback loops align their external inputs without inserting a delay into their internal state paths. Existing mixer limits for aux-send compensation still apply; see Mixing tracks and buses. This is a per-oscillator choice, not a global export quality switch.

The output filter preserves frequencies through 0.45 × sample rate (21.6 kHz at 48 kHz), then rolls off before Nyquist. High quality also replaces the standard path's full-sample switching spike with a fractionally placed, band-limited event of area amplitude × 3 µs, followed by a matched exponential pole. That corrects the spike's sample-rate-dependent strength, so enabling high quality can change this small character contribution as well as aliasing.

PWM, FM and sync#

pw is a signal clamped to 0..1. An audio oscillator can drive it after mapping its bipolar range, for example sine 137Hz |> scale [0.1, 0.9]. The comparator solves crossings against the moving threshold, including when PWM motion opposes the core's movement. Hysteresis retains the chip's endpoint latch.

linearFm accepts an audio signal as a fraction of charging current:

source = as3340 "saw" (freq pitch) {
  quality = "high", linearFm = sine 137Hz * 0.4
}

This is rectifying linear FM: zero leaves the current unchanged; −1 or lower clamps it to its positive minimum. It is not through-zero FM. High quality reconstructs the endpoint rates and pulse widths inside each sample; modulators elsewhere in the graph still run at the engine's output rate.

For musical detuning, use freq pitch + 8Cents or freq pitch - 8Cents. Both apply a frequency ratio, including when a live signal supplies the interval. freq pitch - 8Hz instead subtracts 8 Hz. A pre-existing engine bug confused these two subtraction forms; the September 8 Circuit Tour repair corrects the negative detune in circuits.modelD in both quality modes. Evidence: Circuit Tour Tuning 2026 09 08.

source = as3340 "saw" (freq (pitch + 7Semitones)) {
  quality = "high"
} |> sync (freq pitch) "soft"

|> sync masterHz discharges the core at each fractional master wrap. |> sync masterHz "soft" reverses the core's direction; this differs from the windowed reset used by the plain saw/triangle family. The master starts at phase zero per note and supports changing frequency. Negative master frequency uses its magnitude; extreme master rates clamp to 0.45 × output sample rate.

Measured (engine/tests/analog_wins.rs, character = 0): a 587 Hz slave under a 100 Hz master repeats every 480 samples exactly (max |x[n] − x[n+480]| = 0, against 0.18 free-running), and a steady linearFm of +0.5 / −0.25 plays a 200 Hz core at 299.98 / 150.02 Hz — the 1.5× / 0.75× the charging- current law predicts.

Character controls#

character is a live control from 0 (clean, perfectly trimmed) to 1 (worn). Independent amounts multiply its individual contributions. All default to 1, range 0..2, and accept live values; zero disables just that contribution. character = 0 remains clean regardless of these amounts.

OptionContribution
driftSlow thermal wander, about 0.02 Hz
wobbleSub-audio supply/bias movement, about 1.5 Hz
jitterFiltered CV noise, about 200 Hz
spreadStatic per-chip tuning offset
glitchDerived saw's mid-ramp step
spikeSwitching transient
trackingScale/curve error and high-frequency flattening

For stable tuning with the saw's circuit shape intact, use drift = 0, wobble = 0, jitter = 0, spread = 0, tracking = 0 and retain glitch/spike.

Determinism and measurement#

Chip identity, free-start phase and noise seeds come from the performance coordinate (synth, track, cycle, event, node_id). Equal performances render identically; separate notes and unison instances decorrelate. |> phase p overrides the note-on core phase in cycles while retaining chip identity. Noise advances once per output sample in both quality modes. This is still per-event identity, not a persistent analog voice-card allocator.

The reproducible tests and probe are engine/tests/as3340_quality.rs, engine/examples/analyze-as3340.rs and engine/scripts/analyze-as3340.py. They separate coherent unwanted spectral lines, spike area, modulation convergence, source timing and actual compiled rendering. Numerical agreement with a higher-rate instance is not hardware or listening approval. To measure the switching spike, isolate it by subtracting two otherwise identical models, and keep character measurements away from the character = 0 coherent-alias probes.