Control flow
if, for, while, break, every — all of them bounded, on purpose.
Anyone whose patch has to decide something.
if (x > 0.0f) { ... } else if (x > -1.0f) { ... } else { ... }
for (wrap<64> i) { ... } // constant trip count, i is int
while (cond) { ... } // capped at 1024 iterations
loop { ...; advance(); } // one frame
every (64) { ... } // once every 64 framesChoose a loop for the job#
The outer loop says “produce the next audio frame.” A for inside it says
“do a known number of operations while producing that frame.” They describe
different scales of time. An eight-voice sum needs eight calculations before
one output sample is ready; it does not need eight calls to advance().
| Form | Use it for | Important detail |
|---|---|---|
if / else | Choose behaviour from the current values | The condition is a boolean expression, such as gate > 0.5f. |
for (wrap<N> i) | Visit a fixed number of entries | i runs from 0 through N−1 and is an int. |
while (condition) | Search until a condition changes | The safety cap can end it; design for a bounded result. |
every (N) | Update occasional work inside the frame loop | Put values that must hold between updates in state. |
break | Stop a bounded search early | Exits the innermost bounded loop, not the audio frame. |
Try it: in the search below, change target from 0.5 to 0.9. The loop
examines more entries before it finds a match. At 1.0 there is no match because
the table ends at 63/64, so the initialized fallback value remains. This is a
control-value example, not a tone generator.
Every loop is bounded#
for takes a constant trip count: for (wrap<64> i) runs 64 times and i
is an int. A non-constant bound is POLE0209.
while is allowed and is capped by the safety pass at 1024 iterations. It
cannot run forever, whatever its condition does.
This is not a restriction that gets in the way in practice — a DSP loop is over a table, a partial count or a voice count, all of which are known — and it buys something large: a patch cannot hang the thing running it. That is what lets the playground compile and run code from strangers.
break#
break; exits the nearest enclosing for or capped while, and execution
continues after that loop. It works inside conditionals and inside pure helper
functions; nested loops each have their own exit.
It does not exit a frame's loop, cross a function boundary, or jump out
of an every body (POLE0362).
processor Search
{
output stream float out;
param float target = 0.5f [0.0f, 1.0f];
float table[64];
init
{
for (wrap<64> i) { table[i] = float (i) * (1.0f / 64.0f); }
}
void main()
{
loop
{
var found = 0.0f;
for (wrap<64> i)
{
if (table[i] >= target) { found = float (i) * (1.0f / 64.0f); break; }
}
out <- found * 0.2f;
advance();
}
}
}The safety pass still charges the full trip count even when a break looks
inevitable. An early exit saves work at run time without weakening the
worst-case bound, which is the number the guarantee is made of.
every#
every (N) { … } runs its body once every N frames. It is how you do something
at a slower rate than the audio without leaving the frame loop — refill a slice
of a table, recompute a coefficient set, advance a sequencer step.
A bounded loop inside an every body can use break normally.