Arrays
Power-of-two sizes, an index that wraps instead of faulting, and why that is the whole safety story.
Anyone writing a delay line, a table or a buffer.
A state array holds float, int, or a vector of either:
float line[2048]; // a mono delay line
float<2> stereoLine[1024]; // a stereo one, as ONE arrayfloat<2> buf[1024] is a stereo delay line that cannot drift out of step with
itself, which two parallel arrays can. It occupies 2048 floats of state — the
size is in elements, the storage is per lane.
Sizes must be a power of two (POLE0320).
Capacity and duration are different#
A buffer's size is measured in elements. Its duration depends on the rate at
which you write those elements. A 2,048-element mono delay holds about 42.7 ms
at 48 kHz, but only 21.3 ms at 96 kHz. Stereo elements contain two lanes, so a
float<2>[2048] buffer holds the same number of frames and twice the data.
Choose a power-of-two capacity large enough for your longest delay at the highest supported sample rate. Then constrain the requested delay to that capacity. Wrapped indexing protects the memory access; it does not know that you intended a particular duration.
The index wraps; it does not fault#
An index into a sized array is masked (i & (size - 1)), not bounds-checked:
line[pos - 14000] // negative wraps to the far end
line[999999] // wraps; never reads outside the arrayOne instruction, no branch on the audio path, and no index a program can compute can reach memory outside its own array.
That is the whole reason arrays can exist in a language where a running patch must never be able to trap — and it is why a delay line is written by walking a write pointer forward and reading behind it, with no wrap arithmetic of your own:
processor Delay
{
input stream float in;
output stream float out;
param float time = 0.25f [0.01f, 0.9f];
param float feedback = 0.45f [0.0f, 0.95f];
float line[131072];
int pos = 0;
void main()
{
loop
{
let taps = clamp(int(time * processor.frequency), 1, 131071);
let old = line[pos - taps]; // negative: wraps to the far end
line[pos] = in + old * feedback;
out <- in * 0.6f + old * 0.6f;
pos = pos + 1;
advance();
}
}
}A sizeless external is the one exception: its length is not a power of two,
so its index is clamped to [0, length − 1] instead. Both guarantees say the
same thing — no index reaches memory outside the array.
Endpoint arrays index the same way#
input stream float voiceGate[8];
var held = 0.0f;
for (wrap<8> v) { held = held + voiceGate[v]; }
out <- held * 0.1f;Same masking, same rule. That is what makes polyphony a loop rather than eight copies of everything.