You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/lyte-dsp-quickstart.md
+54-13Lines changed: 54 additions & 13 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,7 @@ When you create a new Lyte DSP node in Audulus, it starts with this template:
11
11
```lyte
12
12
// Inputs, outputs, `frames`, `sampleRate`, and `storage` are globals.
13
13
// Inputs, outputs, and `storage` are slices bound to the engine buffers.
14
+
// `storage` is persistent host storage, not the normal place for large DSP buffers.
14
15
// MAX_FRAMES is the max processing frames (used to declare temporary arrays).
15
16
16
17
init {}
@@ -31,19 +32,19 @@ These are the main words you will see right away:
31
32
-`input`, `output`: the audio coming in and going out for the current block. `input[i]` means “sample number `i`”
32
33
-`frames`: how many samples are in this block
33
34
-`sampleRate`: how many samples happen each second, usually `44100.0` or `48000.0`
34
-
-`storage`: extra memory for things like delay lines
35
+
-`storage`: persistent host storage provided by Audulus. Use it for node-owned values that should live outside the audio block, not as the default location for large delay lines or scratch buffers
35
36
-`globals`: variables declared outside `process`. They keep their value from one block to the next
36
37
-`slice`: a buffer you read with `[...]`
37
38
-`MAX_FRAMES`: the biggest block size Audulus may send to the node. Use it when you need a temporary array
38
39
39
-
You do not create `frames`, `sampleRate`, or `storage` yourself. Audulus gives them to the node automatically.
40
+
You do not create `frames`, `sampleRate`, or `storage` yourself. Audulus gives them to the node automatically. For large DSP buffers, declare a dedicated global array with the size you need.
40
41
41
42
| Global | Type | Description |
42
43
|---|---|---|
43
44
|`input`, `output`|`[f32]`| Default port slices. Add ports as needed above the code viewer in the Inspector. |
44
45
|`frames`|`i32`| Number of samples in this processing block. |
45
46
|`sampleRate`|`f32`| Current sample rate, e.g. `44100.0`. |
46
-
|`storage`|`[f32]`|Pre-allocated buffer for delay lines etc., set the size in the Inspector. |
47
+
|`storage`|`[f32]`|Persistent host storage supplied by Audulus. Use dedicated global arrays for large DSP buffers such as delay lines. |
47
48
|`MAX_FRAMES`|`i32`| Maximum possible block size — use to declare stack arrays. |
48
49
49
50
**`init {}`** runs once when the node loads. Use it for setup values.
@@ -85,21 +86,48 @@ These are good default habits for writing Lyte in Audulus.
85
86
- Start from the fresh-node block template and change one thing at a time when debugging.
86
87
- Use `sin`, `cos`, `tan`, and other unsuffixed math builtins.
87
88
- Read a port sample into a scratch `f32` first if the compiler gets ambiguous about expressions like `input[i] * inv_sr`.
88
-
- Keep slice index proofs inline near `storage[...]` accesses when the safety checker complains.
89
+
- Keep slice index proofs inline near buffer accesses when the safety checker complains.
90
+
- Use `require` on slice helpers when the caller can prove the index is valid.
89
91
- Use `freq[i]`, `cutoff[i]`, and similar forms for normal per-sample processing. Use `freq[0]` only when you intentionally want one control value for the whole block.
90
92
- Move expensive math out of the sample loop when true audio-rate updates are not needed.
91
93
- Try block-rate control first when it sounds good enough. It is usually simpler and cheaper.
92
94
93
95
**Don't:**
94
96
- Don't assume every Expr-node feature maps over exactly. Lyte does have built-in trig/math functions and stdlib helpers like `clamp(x, lo, hi)` and `mix(a, b, t)`, but constants like `pi` are still not built in.
95
97
- Don't use `sinf`, `cosf`, or other suffixed math names in Audulus Lyte. Use unsuffixed names like `sin`, `cos`, `tan`, `atan2`, `sqrt`, `clamp`, and `mix`.
96
-
- Don't rely on helper functions to prove slice indices are safe; the checker usually wants the bounds checks inline.
98
+
- Don't rely on helper functions to prove slice indices by context alone. Use inline guards, or add explicit `require` clauses to the helper.
97
99
- Don't use `assume` in node code — it is only allowed in the standard library or prelude.
98
100
- Don't assume examples from the standalone Lyte repo will drop into Audulus unchanged.
99
101
- Don't rely on block-form inline `if` expressions in assignments such as `let x = if ...` or `output[i] = if ...`. In Lyte those can trigger confusing parser errors. Use a normal assignment first, then a plain `if` block.
100
102
101
103
---
102
104
105
+
## Slice Helper Preconditions
106
+
107
+
Audulus beta 983 and newer Lyte builds support `require` clauses on helper functions. Use them when a helper indexes into a slice and every caller must prove the index is valid.
The `require` clauses do two jobs. Inside the helper, they let the safety checker treat `idx` as a valid index for `arr`. At each call site, the checker makes sure the caller has already proved those conditions.
If the caller cannot prove a clause, Lyte reports that it `couldn't prove require clause` for that call. Add a nearby guard such as `if idx >= 0 && idx < arr.len { ... }`, or restructure the loop so the range proves the bound.
126
+
127
+
The release-note form places `require` immediately after the parameter list. Until a return-valued helper form is confirmed for the Audulus build you are targeting, keep `require` examples to setter-style helpers like `set` and `write_delay`, and keep reads guarded inline.
128
+
129
+
---
130
+
103
131
## Debug Printing
104
132
105
133
`println` is useful for simple debugging. For numbers, write into a
@@ -626,27 +654,36 @@ process {
626
654
---
627
655
## 7. Delay Line
628
656
629
-
A delay line stores past samples in `storage` and reads them back later. This is the core
630
-
of echo, comb filters, chorus, flanging, and many reverb designs.
657
+
A delay line stores past samples in a dedicated memory block and reads them back later.
658
+
This is the core of echo, comb filters, chorus, flanging, and many reverb designs.
659
+
660
+
`storage` is persistent Audulus node storage, but it is not the preferred place for a
661
+
large delay buffer. For delay lines, declare a global array with the length you need.
662
+
This example dedicates 65536 samples of memory to the delay line.
631
663
632
664
This is different from unit delay. Unit delay is always one sample. A delay line is a
633
665
larger buffer measured in samples, milliseconds, or seconds.
Copy file name to clipboardExpand all lines: docs/tutorial.md
+36Lines changed: 36 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -635,6 +635,34 @@ One of Lyte's safety features is that it checks array access *before* your code
635
635
636
636
For example, accessing `array[1]` on a `[i32; 1]` array (which only has index `0`) is caught before your program ever runs. No mysterious crashes at runtime.
637
637
638
+
### Function preconditions with `require`
639
+
640
+
When a helper function indexes into a slice, Lyte may need an explicit promise about the index. Audulus beta 983 and newer Lyte builds support `require` clauses for this.
641
+
642
+
Write `require` after the parameter list and before the body, matching the Audulus release-note form:
This means "`set` may only be called when `idx` is inside `arr`." The safety checker uses those clauses inside the function, so `arr[idx]` is accepted. It also checks every call to `set`; if the caller cannot prove the clauses, Lyte reports an error such as:
651
+
652
+
```
653
+
❌ couldn't prove require clause `idx < arr.len` for call to `set`
654
+
```
655
+
656
+
You can write multiple `require` clauses, or combine them with `&&`:
For Audulus DSP code, this is useful for small setter-style buffer helpers. If the checker complains at the call site, put the call inside a guard like `if idx >= 0 && idx < buffer.len { ... }`, or use a loop range that proves the index is in bounds. Do not document a return-valued helper form until that exact syntax is confirmed in the target Audulus build.
665
+
638
666
### Arrays are copied by value
639
667
640
668
When you assign an array to a new variable, you get an independent copy. Changing one doesn't affect the other:
@@ -764,6 +792,14 @@ The key difference:
764
792
-`[i32; 5]` — a fixed array of exactly 5 integers. The size is known at *compile time* — meaning Lyte knows it before your program ever runs.
765
793
-`[i32]` — a slice, a view into any array of integers. The size is only known at *runtime* — meaning when your program is actually running and the data exists.
766
794
795
+
When a function takes a slice and writes through an index, add `require` clauses if the helper expects the caller to provide a valid index:
0 commit comments