study 06 of 10
Functions
Every script so far was one function with a menu entry. A function can also be a word of your own, used by the others: write a Euclidean rhythm once, then call it on any track with any number of hits. This study builds a small library of such words.
Bricks
A function without menu does not appear in the menu. We call it a brick: the other
functions use it. Its arguments follow its name, after a |:
def two_euclids
menu "LEARN" >> "2 EUCLIDS"
euclid_on | 1 5 0
euclid_on | 2 3 4
def euclid_on | track hits rot
trk track:
tr.clr
l 1 16: if er.r hits 16 rot i: tr.add iOutput · what the simulator prints for $ otype.py run two_euclids -v
two_euclids seed 1 (--seed) pattern A01 len 16 (default) track 1
T1 1 5 9 13
X...X..X..X..X..
T2 1 5 9 13
....X.....X....X
X trig ! one-shot o trigless trig - trigless lock l locks only . none
part: tempo 120
cost: 354/450 random draws: rand 0 toss 0 prob 0 drunk 0 file 0A call is the function's name, a |, then its values. The bar is required whenever your function
takes arguments, and never written after one of the Octatrack's operations: at a glance, you see what is yours.
Your names use letters, digits and _, never a dot; dotted names belong to the machine.
Inside the brick, track, hits and rot are read like variables but cannot
be changed. A function takes up to four arguments. They are usually numbers, but can also be a text, a list or a
trig condition: OType works out which from the way the brick uses them, and checks every call.
Giving a value back: ret
ret value ends the function and gives the value back to the caller, so the call can stand inside
an expression like any operation:
def backbeat
menu "LEARN" >> "BACKBEAT"
tr.clr
tr.add beat_step | 2
tr.add beat_step | 4
def beat_step | beat
ret sub mul beat 4 3 # beat 1..4 gives step 1, 5, 9 or 13Output · what the simulator prints for $ otype.py run backbeat
backbeat seed 1 (--seed) pattern A01 len 16 (default) track 1
T1 1 5 9 13
....X.......X...
X trig ! one-shot o trigless trig - trigless lock l locks only . none
part: tempo 120
cost: 140/140 random draws: rand 0 toss 0 prob 0 drunk 0 file 0A function that ends without ret is worth 0. Called as a statement, its value is simply
dropped.
What a function sees
Each call has its own a b c d, all at 0. A function you call cannot touch yours:
def keep_a
menu "LEARN" >> "KEEP A"
a 5
set_a
msg "A IS STILL" a
def set_a
a 9Output · what the simulator prints for $ otype.py run keep_a
keep_a seed 1 (--seed) pattern A01 len 16 (default) track 1
(no track holds anything)
X trig ! one-shot o trigless trig - trigless lock l locks only . none
part: tempo 120
msg: "A IS STILL 5"
end box: "A IS STILL 5" (the last msg, instead of DONE)
cost: 4/4 random draws: rand 0 toss 0 prob 0 drunk 0 file 0The other things a function can see are shared: the global x y z t, every function, and every
const. As the previous study showed, the called function also starts with a copy of the caller's
tracks and steps. The appendix sums it all up in one table.
Constants
const name value, at the start of a line, names a value that never changes: a number, a text, a
list or a trig condition. Constants and functions are global: every file in /SCRIPTS/ sees them,
so a file can be a library that the others use.
const kick 1
const snare 2
def named_tracks
menu "LEARN" >> "NAMED"
euclid_on | kick 4 0
euclid_on | snare 2 4
def euclid_on | track hits rot
trk track:
tr.clr
l 1 16: if er.r hits 16 rot i: tr.add iOutput · what the simulator prints for $ otype.py run named_tracks -v
named_tracks seed 1 (--seed) pattern A01 len 16 (default) track 1
T1 1 5 9 13
X...X...X...X...
T2 1 5 9 13
....X.......X...
X trig ! one-shot o trigless trig - trigless lock l locks only . none
part: tempo 120
cost: 346/450 random draws: rand 0 toss 0 prob 0 drunk 0 file 0A name can only be used once across all the files: two functions, or a function and a constant, with the same name are both refused.
What functions cannot do
- Call themselves, directly or through others.
pingcallingpongcallingpingis refused withloop ping>pong>ping. - Nest deeper than eight calls.
Both rules, with the fixed loop bounds of decisions and loops, make sure a run always ends, and the checker can tell you in advance how much work it does at most.
What you learned
def name | a b- a function with arguments
- brick
- a function without
menu, used by the others name | 1 2- calling your own function
ret v- end and give a value back
const name value- a named value, global to all files
- no recursion, eight calls deep
- so every run ends
Try it
- Give
euclid_ona fourth argument: the number of steps of the rhythm. - Write a brick
clamp_vol | vthat returnsvkept between 40 and 127. - Move
euclid_onto its own file,LIB.OTY, and check both files together.