Types
float, int, and vectors of either — plus the conversion rule that has no exceptions.
Anyone about to mix a sample rate with a sample index.
There are two scalar types, float and int, and vectors of either.
float<2> is a stereo sample as one value.
Read the type as part of the meaning#
| Type or value | Typical use | Example |
|---|---|---|
float | Audio samples, Hz, seconds, gains and interpolation positions | 0.5f |
int | Indices, counts and discrete choices | 7 |
float<2> | One stereo sample | float<2>(left, right) |
| A comparison result | A condition for if or ?: | gate > 0.5f |
The suffix on 1.0f is worth noticing: it says that the literal is a float.
The literal 1 is an int. A whole-number value can still be a float, just as
“one second” and “one sample” are different meanings despite both using one.
See conversions for call signatures
and worked unit conversions.
No implicit conversion, ever#
Mixing float and int is POLE0307. There is no promotion rule to remember
because there is no promotion.
let i = int(2.7f); // 2, truncated toward zero (not floor)
let f = float(i);int(-1.7f) is -1, while floor(-1.7f) is -2.0f. They differ, and
confusing them is the classic off-by-one in a table lookup. int is 32-bit; a
literal that does not fit is POLE0316.
Vectors#
Arithmetic on a vector is elementwise and compiles to one instruction, not a
loop — a float<2> is an LLVM vector, so a + b on a stereo pair is a
single fadd.
input stream float<2> in;
output stream float<2> out;
let mid = (in[0] + in[1]) * 0.5f; // an element
let wide = in * 0.5f; // a scalar broadcasts across the lanes
out <- float<2>(mid, mid);The rules are narrow on purpose:
- Widths must match, or one side must be a single element to broadcast
(POLE0345). Broadcasting widens; it does not convert — an
intagainst afloat<2>is still POLE0307. - A constant element outside the width is an error (POLE0347), not the silent
wrap an array index gets. A vector has no power-of-two masking to fall back
on, and
s[2]on a stereo signal is a mistake every time. - Comparisons are scalar (POLE0346): a per-element bool would have no
ifto feed. float<1>is rejected — it isfloatspelled longer.- The first output is the audio endpoint. Use
float<2>for stereo; subsequent outputs can carry side-channel data such as a meter or telemetry. See endpoints before choosing a layout.
Builtins are elementwise too#
A scalar broadcasts into them, so clamp(stereo, -1.0f, 1.0f) is one call with
ordinary numbers for bounds rather than one call per lane.
min, max and clamp also take ints, and are the only builtins that do.
Ordering and bounding are not float ideas, and clamping an integer index is
exactly what an array read needs. Their arguments must agree — all int or all
float (POLE0327). The rest stay float-only: sqrt of an int returning an int
would imply a rounding rule nobody asked for.
The full list is on the built-in maths page, which is produced by asking the compiler rather than by transcription.
Vectors in state and at the edges#
A state array holds vectors: float<2> buf[1024] is a stereo delay line as one
array rather than two that can drift apart. It occupies 2048 floats of state —
the size is in elements, the storage is per lane.
A vector endpoint occupies one host slot per lane, so float<2> in followed by
float gain puts gain at slot 2. --params, the export header and the graph
wiring all agree about that without being told separately, because they all ask
the same function.