Program structure
Processors, main, advance, and the one rule that decides what a frame is.
Everyone. This page is the spine the rest hangs off.
A Pole file contains processors, and optionally graphs that wire them together. Nothing else lives at the top level — not a function, not a constant. A stray declaration is POLE0207.
processor Gain
{
input stream float in;
output stream float out;
void main()
{
loop
{
out <- in * 0.5f;
advance();
}
}
}main runs once per sample#
Not once per block. Once per sample.
That single decision is why there is no modulation system in this language and
no "control rate" to be coarser than the audio: anything you compute in main
is per-sample by construction, so modulating a cutoff is multiplication rather
than a feature you route through.
advance() ends the frame#
advance() is the frame boundary. Code after it in the same pass is
unreachable and is reported (POLE0601).
A loop with no advance() in it is rejected (POLE0405). It would
otherwise be an infinite loop, and quietly compiling it as "the body runs
once" would make it mean something the person who wrote it did not write.
processor NeverAdvances
{
input stream float in;
output stream float out;
void main()
{
loop { out <- in; }
}
}Every output starts each frame at zero#
An output the frame does not write reads 0 that frame — not the previous frame's value.
input stream float gate;
// On a frame where gate is 0, `out` is 0. It does not hold its last value.
if (gate > 0.5f) { out <- in; }A value that should persist between writes belongs in processor state. This is the same on every path a program can run on — one node at a time, a whole block at a time, a fused graph, an exported object — because the generated frame function does the zeroing itself rather than leaving it to a caller.
What a processor may contain#
input / output stream | its endpoints |
param | an endpoint with a name, default and range |
external | a buffer the host owns — a wavetable, a sample, an impulse response |
| state declarations | float phase = 0.0f;, float line[2048]; |
| functions | pure helpers, inlined at the call site |
init | state built once, before the first frame |
on <endpoint> | an event handler, run between samples |
void main() | the frame loop |
What is deliberately absent#
No dynamic allocation. No recursion (POLE0603). No pointers, no structs, no strings, no generics. Every loop has a bound the compiler can see.
That last one is not austerity for its own sake — it is what makes a patch written by a stranger safe to compile and run, which is what lets the playground exist at all.