Script Patterns
A class that implements the Pattern interface is a pattern. Send an instance to a track with <<
and resonon calls query() once per cycle. For worked examples, see
Write a Pattern as a Class.
The interface
Section titled “The interface”class Arp implements Pattern { fn query(cycle) -> Note | Number | Rest | Array | Pattern? { … }}- The class defines
querywith one parameter namedcycle, and declares its return type as the interface does. A class that does not conform is refused where it is defined. - A class with a
querymethod that does not sayimplements Patternis not a pattern:<<refuses it and names the fix. - The class has every combinator of
Pattern(.fast(),.transpose(),.stack(), …), and every parameter typedPatterntakes its instances.
Where the instance is captured
Section titled “Where the instance is captured”A combinator, << and a stdlib parameter typed Pattern each capture the instance as it is at
that moment, fields and methods. A later write to the instance changes nothing already captured:
let walk = Arp(#[C4, E4]);let fast = walk.fast(2); // captures walk nowwalk.notes = #[D4]; // fast still plays C4 and E4A function of your own that takes p: Pattern gets the instance itself, not a capture.
The call
Section titled “The call”For each cycle, the track:
- Rebuilds the instance’s fields by replaying
query()for every earlier cycle, starting from the field values the instance had when it was sent to the track. - Calls
query(cycle)with the cycle number (0, 1, 2, …). - Converts the return value into the events of that cycle.
| Rule | Consequence |
|---|---|
Fields assigned in query() carry to the next cycle. | this.idx = this.idx + 1; advances once per cycle. |
| State is replayed from cycle 0. | The same cycle always returns the same result, in any order and after any seek. |
| Fields are captured when the instance is sent to a track, or passed to a combinator. | Changing the original instance afterwards does not change the playing pattern. |
cps is in scope inside query(). | The current tempo, in cycles per second. |
Return values
Section titled “Return values”| Return value | Result |
|---|---|
Number (60) | A MIDI note filling the cycle |
Note (C4) | A note filling the cycle |
Array (#[C4, E4, G4]) | A chord — all notes sound together for the cycle |
Pattern ([C4 E4 G4], or an instance of a class implementing Pattern) | A sub-pattern, laid out across the cycle |
[_] | A rest — silence for the cycle |
What query() may do
Section titled “What query() may do”query() runs in a restricted evaluator, so that it is deterministic and safe to run away from the
main thread.
| Supported | Not supported |
|---|---|
let, if/else, match, return, for, loop | print(), put() |
| Arithmetic, field and array access | Audio and MIDI operations |
| Calls to your own functions, methods and classes | Native functions |
Seeded rand() and choose() from std/random |