Two kinds of conditional exist, and choosing between them matters:
select2 / select compute all their inputs every
sample and pick one value per channel. Cheap, branch-free, and the right choice for
per-sample decisions (they are used throughout common_ugens).if_ / switch generate actual branches in the
compiled code: only the taken branch's subgraph runs. Use them when the branches are
expensive (whole alternative signal chains) and the condition changes rarely (e.g. a
choice control).| Function | Description |
|---|---|
select2(test, ifOne, ifZero) | Per-channel: ifOne where test is nonzero, else ifZero. All three evaluate every sample. |
select(test, exprs) | Per-channel N-way select: exprs[test]. All inputs evaluate every sample. |
if_(test, thenFn, elseFn?) | Branching conditional. The branches are zero-argument functions (fn() S { ... }) built as subgraphs; only the taken branch executes. Missing else yields 0. An overload on a plain Bool test resolves at graph-build time. |
switch(test, funs) | N-way branching conditional over an array of subgraph functions, indexed by test. |
for_(varname, count, bodyFn) | A loop in the generated code: the body subgraph (a fn(S) S receiving the loop variable) runs count times per sample. Prefer ordinary Tzopilotl loops (which unroll into the graph) unless you specifically need a runtime loop. |
-- select2: per-sample, both sides always computed
fn divz(numer, denom, otherwise) = select2(denom == 0.0, otherwise, numer / denom);
-- switch: only the chosen oscillator chain runs
fn multiOsc() S {
let f = control("freq", ControlSpec { lo: 20.0, hi: 5000.0, init: 220.0, warp: ControlWarp.exponential });
let w = choice("wave", 3);
switch(w, [
fn() S { f sinosc },
fn() S { f smoothSaw(4) },
fn() S { f lfbpulse(0.5) },
]) * 0.2 |> outlet
}
↑ Back to top