-
Notifications
You must be signed in to change notification settings - Fork 85
/
reference.md
722 lines (554 loc) · 27.6 KB
/
reference.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
---
layout: default
parent: scripting
grand_parent: crow
has_children: false
title: script reference
nav_order: 4
has_toc: false
permalink: /crow/reference/
---
# Reference
[input](#input) --- [output](#output) --- [asl](#asl) --- [sequins](#sequins) --- [metro](#metro) --- [delay](#delay) --- [clock](#clock) --- [ii/i2c](#ii) --- [public](#public) --- [cal](#cal) --- [globals](#globals)
## input
`input` is a table representing the 2 CV inputs
### input queries
```lua
_ = input[n].volts -- returns the current value on input n
input[n].query -- send input n's value to the host -> ^^stream(channel, volts)
```
### input modes
available modes are: `'none'`, `'stream'`, `'change'`, `'window'`, `'scale'`, `'volume'`, `'peak'`, `'freq'`
```lua
input[n].mode = 'stream' -- set input n to stream with default time
input[n].mode( 'none' ) -- set input n to 'none' mode (ie. disable events)
input[n].mode( 'stream', time ) -- set input n to 'stream' every time seconds
input[n].mode( 'change', threshold, hysteresis, direction ) -- set input n to:
-- 'change': create an event each time the threshold is crossed
-- threshold: set the voltage around which to change state
-- hysteresis: avoid noise of this size (in volts)
-- direction: 'rising', 'falling', or 'both' transitions create events
input[n].mode( 'window', windows, hysteresis ) -- set input n to:
-- 'window': create an event when the input enters a new voltage window
-- windows: a table of voltages defining window boundaries, eg: {-3,-1,1,3}
-- threshold: sets the hysteresis voltage at each boundary
input[n].mode( 'scale', notes, temperament, scaling ) -- set input n to:
-- 'scale': quantize the input, raising an event when a new note is entered
-- notes: a table of notes representing the scale, eg: {0,2,4,5,7,9,11}
-- temperament: number of possible notes per octave. defaults to 12
-- scaling: volts-per-octave. default to 1.0, but use 1.2 for buchla systems
-- 'scale' also supports just intonation ratios in the 'notes' position
-- notes appears as {1/1, 3/2, 9/8} etc
-- temperament must be set to 'ji'
input[n].mode( 'volume', time ) -- set input n to stream the volume at the input
-- 'volume': tracks the RMS amplitude of an audio signal (not a direct voltage)
-- time: sets the rate in seconds at which to report the volume
input[n].mode( 'peak', threshold, hysteresis ) -- set input n to:
-- 'stream': creates an event when an audio transient is detected at the input
-- threshold: sets the RMS level required to trigger an event
-- hysteresis: sets how far the RMS level needs to decrease before retriggering
-- NOTE: 'freq' mode is only available on input 1.
input[1].mode( 'freq', time ) -- set input 1 to:
-- 'freq': calculate the frequency of a connected oscillator.
-- time: rate at which frequency is reported.
```
table calling the input will set the mode with named parameters:
```lua
-- set input n to stream every 0.2 seconds
input[n]{ mode = 'stream'
, time = 0.2
}
```
the default behaviour is that the modes call a specific event, sending to the host:
```lua
'stream' -> ^^stream(channel, volts)
'change' -> ^^change(channel, state)
```
you can customize the event handlers:
```lua
input[1].stream = function(volts) <your_inline_function> end
input[1].change = change_handler_function
```
using table call syntax, you can set the event handler at the same time:
```lua
-- set input n to stream every 0.2 seconds & print the value
input[n]{ mode = 'stream'
, time = 0.2
, stream = function(v) print(v) end -- assign the handler inline
}
```
## output
`output` is a table representing the 4 CV outputs
### setting cv
```lua
output[n].volts = 3.0 -- set output n to 3 volts instantly
```
### slewing cv
```lua
output[n].slew = 0.1 -- sets output n's slew time to 0.1 seconds.
output[n].volts = 2.0 -- tell output n to move toward 2.0 volts, over the slew time
_ = output[n].volts -- inspect the instantaneous voltage of output n
```
### shaping cv
```lua
output[n].shape = 'expo' -- travel to the .volts destination with a non-linear path
output[n].slew = 0.1
output[n].volts = 2.0
```
Available options:
- `'linear'`
- `'sine'`
- `'logarithmic'`
- `'exponential'`
- `'now'`: go instantly to the destination then wait
- `'wait'`: wait at the current level, then go to the destination
- `'over'`: move toward the destination and overshoot, before landing
- `'under'`: move away from the destination, then smoothly ramp up
- `'rebound'`: emulate a bouncing ball toward the destination
### quantize to scales
outputs can be quantized with a flexible scale system. these scales are applied *after* slew or actions, so they can be used to eg. convert lfo's into arpeggios.
```lua
output[n].scale( {scale}, temperament, scaling )
-- scale: a table of note values, eg: {0,2,3,5,7,9,10}
-- use the empty table {} for chromatic scale
-- temperament: how many divisions in an octave, default 12 (ie 12TET)
-- scaling: volts per octave, default to 1.0, but use eg. 1.2 for buchla systems
-- deactivate any active scaling with the 'none' argument
output[n].scale( 'none' )
-- scale table can be modified without reactivating scale
output[n].scale = {0,7,2,9} -- note: scale can be out of order to create arpeggios
-- 'ji' as the second argument causes the scale to be interpreted as just ratios
output[n].scale( {1/1, 9/8, 5/4, 4/3, 3/2 11/8}, 'ji' )
```
### clock mode
outputs can be set to output pulses at divisions of the clock. see [clock](#clock) for details.
```
output[n]:clock(division) -- set output to clock mode with division
-- division is in the same units as for clock.sync(division)
output[n]:clock('none') -- disable the output clock
output[n].clock_div = 1/4 -- update the clock division without re-syncing the clock
```
after the clock is assigned, the output `action` can be changed from the default `pulse` to any other shape (see below).
### actions
outputs can have `actions`, not just voltages and slew times. an `action` is a sequence of voltage slopes, like an lfo or envelope.
```lua
output[n].action = lfo() -- set output n's action to be a default LFO
output[n]() -- call the output table to start the action- starts the LFO
output[n]( lfo() ) -- shortcut to set the action and start immediately
```
a small set of actions are included (from `asllib.lua`):
```lua
lfo( time, level, shape ) -- low frequency oscillator
pulse( time, level, polarity ) -- trigger / gate generator
ramp( time, skew, level ) -- triangle LFO with skew between sawtooth and ramp shapes
ar( attack, release, level, shape ) -- attack-release envelope, retriggerable
adsr( attack, decay, sustain, release, shape ) -- ADSR envelope
oscillate( freq, level, shape) -- audio rate oscillator
```
actions can take 'directives' to control them. the `adsr` action needs a `false` directive in order to enter the release phase:
```lua
output[1].action = adsr()
output[1]( true ) -- start attack phase and pause at sustain
output[1]( true ) -- re-start attack phase from the current location
output[1]( false ) -- enter release phase
```
### done event
assign a function to be executed when an ASL action is completed
```lua
output[1].done = function() print 'done!' end
```
## ASL
actions above are implemented using the `ASL` mini-language. you can write your own action functions to further customize your scripts.
```lua
-- a basic triangle lfo
function lfo( time, level )
return loop{ to( level, time/2 )
, to( -level, time/2 )
}
end
```
everything is built on the primitive `to( destination, time, shape )` which takes a destination and time pair (and optional shape), sending the output along a gradient. ASL is just some syntax to tie these short trips into a journey.
```lua
-- an ASL is composed of a sequence of 'to' calls in a table
myjourney = { to(1,1), to(2,2), to(3,3,'log') }
-- often clearer as a vertical sequence
myjourney = { to(1,1)
, to(2,2)
, to(3,3,'log')
}
-- assign to an output and put it in motion
output[1]( myjourney )
```
shape types for the third parameter are [listed above](#shaping-cv).
ASL provides some constructs for doing musical things:
```lua
loop{ <asl> } -- when the sequence is complete, start again
held{ <asl> } -- freeze at end of sequence until a false or 'release' directive
lock{ <asl> } -- ignore all directives until the sequence is complete
times( count, { <asl> } ) -- repeat the sequence `count` times
-- 2 lower-level constructs are also available:
asl._if( pred, { <asl> } ) -- only execute the sequence if `pred` is true
asl._while( pred, { <asl> } ) -- `loop` the sequence so long as `pred` is true
```
See [asllib.lua](https://github.com/monome/crow/blob/main/lua/asllib.lua) to see these constructs in use.
### dynamic variables
ASLs are *descriptions* not programs, meaning variables are fixed when the ASL is created. an ASL can be unfixed by using `dynamic` variables. these named variables can be updated by a script or from the REPL, and will be used by the running ASL.
```lua
-- a standard lfo
output[1].action = loop{ to( 1, 1)
, to(-1, 1)
}
-- adding dynamic control of lfo height
output[1].action = loop{ to( dyn{height = 1}, 1)
, to(-1, 1)
}
-- dyn.height will now control the rising destination
output[1].dyn.height = 2 -- rise to 2V
-- arithmetic operations are permitted over dynamics
-- eg: lfo with dynamic speed control
output[1].action = loop{ to( 1, dyn{time=1}/2)
, to(-1, dyn{time=1}/2)
}
-- if multiple dyn's use the same name (eg. time) they will share state
output[1].dyn.time = 2 -- set the overall LFO time to 2seconds
```
in addition to modifying dynamics from a script, or the REPL, one can attach mutations to a `dyn`. mutations are applied every time the dynamic is used in the script.
```lua
-- ramp lfo, decelerating from 0.1 seconds to infinity
output[1].action =
loop{ to(5, dyn{time=0.1}:step(0.1)) -- step performs addition / subtraction
, to(0, 0)
}
-- the lfo will slow down by 0.1 seconds each time it repeats
output[1].dyn.time = 0.1 -- reset the variable like a normal dyn
-- mutation can be made exponential with 'mul' instead of 'step'
-- ramp lfo, slowing down by 10% on each repeat
output[1].action =
loop{ to(5, dyn{time=0.1}:mul(1.1)) -- mul performs multiplication
, to(0, 0)
}
-- to create looping patterns, one can 'wrap' the values into a range
-- when the ramp reaches 5seconds long, it will reset to 0.1 seconds
output[1].action =
loop{ to(5, dyn{time=0.1}:step(0.1):wrap(0.1,5))
, to(0, 0)
}
-- 'wrap' can be used without 'step' or 'mul' to ensure a dynamic stays in range
output[1].action =
loop{ to(5, dyn{time=0.1}:wrap(0.1,2))
, to(0, 0)
}
output[1].dyn.time = 0.5 -- sets dyn.time to 0.5
output[1].dyn.time = 2.1 -- wraps dyn.time to 0.2
-- reference usage
dyn{k=v}
:step(inc) -- add 'inc' to the dynamic value on each access
:mul(factor) -- multiply the dynamic value by 'factor' on each access
:wrap(min, max) -- wrap the dynamic value into the range from min to max
```
## sequins
sequins are lua tables with associated behaviour. the sequins library is designed for building sequencers and arpeggiators with short scripts.
```lua
seq = sequins{1,2,3} -- create a sequins of 3 values, and save it as seq
_ = seq() --> returns the next value from the sequins
-- eg. the first 4 seq() calls result in: 1, 2, 3, 1
-- it is idiomatic to alias 'sequins' with the letter 's' when in use
s = sequins
-- any datatype is allowed
fnseq = s{lfo, ar, pulse}
strseq = s{'+', '-', '*', '/'}
tabseq = s{ {1,2,3}, {4,5,6} } -- the whole table will be returned as the value
-- the 'step' that occurs on each call can be changed from the default of +1
seq = s{1,2,3,4,5} -- seq() --> 1,2,3,4,5,1,...
seq = s{1,2,3,4,5}:step(2) -- seq() --> 1,3,5,2,4,1,...
-- note how 'step' is "method chained" onto the sequins
-- while it's typical to apply these method chains at initialization, they can be built up sequentially
seq = s{1,2,3,4,5} -- defaults to step(1)
seq:step(2) -- modifies seq, changing to step(2)
-- current active index can be set and queried
seq:select(n) -- at the next seq(), index n will be selected & returned
_ = seq.ix -- value of the index can be queried directly
-- sequins elements can be modified like normal tables
seq[n] = _ -- update the value of table index n
_ = seq[n] -- access the value of the nth element of the sequins
-- to change the whole table (including changing the length of the sequins)
seq:settable(new_table) -- preserves current index
-- sequins can be nested in sequins (good for arranging melodies)
seq = s{1, 2, s{3, 4}}
-- by default the behaviour will take only one value from the nested sequins at a time
-- eg: seq() --> 1, 2, 3, 1, 2, 4 ...
```
to enable more complex arrangement, the standard interleaved flow of nested sequins can be modified. modifications are applied as 'method chains' operating over a sequins object.
when calling a sequins object it will *always* return a result. when a flow-modifier *doesn't* return a value (eg every(2) only returns a value every second time), the outer-sequins will simply request the next value immediately until a value is returned.
```lua
-- flow-modifiers that might return a value, or might skip
seq:every(n) -- produce a value every nth call
seq:times(n) -- only produce a value the first n times it's called
seq:once() -- just a shortuct for :times(1)
seq:cond(pred) -- conditionally produces a value if pred() returns true
-- flow-modifiers that will capture focus
-- these are 'greedy' modifiers, keeping the spotlight on them
seq:count(n) -- produce n values in a row without letting other sequins give a value
seq:all() -- like count(#self), returns all values of seq before returning
-- modifiers that may return a value, and capture focus
seq:condr(pred) -- conditionally produces a value if pred() returns true, and captures focus
-- with nested sequins, you can restart the arrangement
seq:reset() -- resets all flow-modifiers as well as table indices
```
## metro
crow has 8 metros, each able to run at a it's own timebase and trigger a defined event. metros are best used when you want a fixed action to occur at a regular interval.
```lua
-- start a timer that prints a number every second, counting up each time
metro[1].event = function(c) print(c) end
metro[1].time = 1.0
metro[1]:start()
```
```lua
-- create a metro with the name 'mycounter' which calls 'count_event'
function count_event(count)
-- do something fun!
end
mycounter = metro.init{ event = count_event
, time = 2.0
, count = -1 -- nb: -1 is 'forever'
}
mycounter:start() -- begin mycounter
mycounter:stop() -- stop mycounter
```
```lua
-- update params while the timer is running like so:
metro[1].event = a_different_function
mycounter.time = 0.1
```
## delay
delay takes a function and executes it in the future. by default it is only executed once, but can optionally be repeated. delay is the most basic of the timing systems, ideally used when simply delaying execution of an action. for more complex time management, use `metro` or `clock`.
NOTE: if using `delay` and `metro` simultaneously, metros must be initialized with `metro.init` and not directly indexed.
```lua
delay( action, time, [repeats] ) -- delays execution of action (a function)
-- by time (in seconds)
-- (optional) repeat the delay action repeats times
```
## clock
the clock system facilitates various time-based functionality: repeating functions, synchronizing multiple functions, delaying functions. `clock` is preferable to `metro` when synchronizing to the global timebase, or for irregular time intervals.
clocks work by running a function in a 'coroutine'. these functions are special because they can call `clock.sleep(seconds)` and `clock.sync(beats)` to pause execution, but are otherwise just normal functions. to run a special clock function, you don't call it like a normal function, but instead pass it to `clock.run` which manages the coroutine for you.
```
coro_id = clock.run(func [, args]) -- run function "func", and optional [args] get passed
-- to the function "func"
-- (optionally) save coroutine id as "coro_id" so it can be cancelled
clock.cancel(coro_id) -- cancel a clock started by clock.run (requires coro_id)
clock.sleep(seconds) -- sleep specified time (in seconds)
clock.sync(beats) -- sleep until next sync at intervals "beats" (ie, 1/4)
clock.cleanup() -- kill all currently-running clocks
```
clock tempo & timing:
```lua
clock.tempo = t -- assign clock tempo to t beats-per-minute
_ = clock.tempo -- get tempo value (BPM)
_ = clock.get_beats -- get count of beats since the clock started
_ = clock.get_beat_sec -- get the length of a beat in seconds
```
the clock can be stopped & started, and events can occur when doing either. the clock starts running when crow awakens. note start/stopping the clock does not affect `clock.sleep` calls.
```
clock.start( [beat] ) -- start clock (optional: start counting from 'beat')
clock.stop() -- stop clock
clock.transport.start = start_handler -- assign a function to be called when the clock starts
clock.transport.stop = stop_handler -- assign a function to be called when the clock stops
```
example (looping):
```lua
function init()
x = 0
clock.run(forever) -- start a clock which will run the forever function
end
function forever()
while true do -- this will loop forever
x = x + 1
clock.sleep(0.1) -- waits for 100ms before the next loop
end
end
```
example (one-shot):
```lua
function init()
output[2].action = adsr()
dur = 0.6 -- how many seconds should the sustain phase last?
end
function note_on()
clock.run(oneshot, dur) -- start a 'oneshot' clock and pass it duration as an argument
end
function oneshot(seconds)
output[2](true) -- start attack phase, pause at sustain
clock.sleep(seconds) -- hold for time interval specified by 'dur'
output[2](false) -- start release phase
end
```
## ii
crow has functions for *leading* most available ii devices:
```lua
ii.help() -- prints a list of supported ii devices
ii.<device>.help() -- prints available functions for <device>
```
when leading the ii bus, you can send commands, or query values:
```lua
-- mydevice is the name of the recipient, eg: 'jf' in ii.jf.play_note()
-- commands
ii.mydevice.command( [args] ) -- 'command' will be changed to eg: play_note
-- queries
ii.mydevice.get('param' [, args]) -- asks device to send back the value of 'param'
-- the get function does not return a value, instead it will raise an event (see below).
-- response to query. triggered by .get('param')
ii.mydevice.event = function(e, value) ... end
-- e is a table of metadata matching your .get request:
-- 'name': the 'param' name. same as the first argument to the .get
-- 'arg': the first argument sent to the .get
-- 'device': the number of the ii device (usually 1, see below)
-- value is the actual response to .get
```
```lua
-- example usage
ii.jf.event = function(e, value)
if e.name == 'ramp' then
print('ramp is '..value)
end
end
ii.jf.get 'ramp' -- will trigger the above .event function
ii.jf.tr(1, 1) -- sets just friends' 1st trigger to the on state
```
generally `ii` arguments corresponding to pitch, are specified in volts (like `input` and `output`), times are specified in seconds (like `.slew` and `ASL`). other parameters use regular numbers.
multiple ii devices of the same type are supported (eg. txi, er301, jf):
```lua
ii.txi[1].get('param',1) -- get the first param of the first device
ii.txi[2].get('param',1) -- get the first param of the second device
ii.txi.event( e, value ) -- 'e' is a table of: { name, device, arg }
if e.name == 'param' then
print('txi[' .. e.device .. '][' .. e.arg .. ']=' .. value)
-- prints: txi[1][1]=1.0
-- txi[2][1]=3.2
end
end
```
if you are working with an unsupported ii device, or you are developing a new device that will support `ii`, you can use the `ii.raw` functions:
```lua
-- both set & get commands are sent using ii.raw
ii.raw(addr, bytes [, rx_len])
-- addr: i2c address as an int, typically in hex (eg. 0x70)
-- bytes: bytestring of the message (eg. '\x09\x00\x00\x20')
-- rx_len: (optional, default 0) number of response bytes to read
-- capture responses with event_raw
ii.event_raw = function(addr, cmd, data)
-- NOTE: *all* ii events pass through this function, so you must match on your desired address
if addr == 0x70 then
-- handle your custom event here
return true -- if you don't return true, this event will also be processed by the normal ii system (raising an error)
end
end
```
crow has weak pullups for the `ii` line. they are on by default & should probably stay on. if you have some reason to turn them off you can do so with:
```lua
ii.pullup( state ) -- turns on (true) or off (false) the hardware i2c pullups. on by default
```
functions for crow as an `ii` *follower* are handled by the `ii` leader (norns, teletype, etc.) in their own languages.
### just friends
for a longform description of the i2c relationship between crow + Just Friends, please see the [extended reference](https://github.com/whimsicalraps/Just-Friends/blob/main/Just-Type.md).
## public
public variables are the automatic method for exposing crow variables to a connected USB host device. public variables are kept synchronized across both devices, and may be modified by either device.
```lua
-- create a public variable 'name' and set it to 'init_value'
public{name = init_value}
-- equivalent to: public.name = init_value
-- on crow, the variable can be used like a normal variable in a table
public.name = 'jin' -- set public.name to the value 'jin'
_ = public.name -- get the value of public.name --> 'jin'
-- public variables can be most normal lua types:
public{mynum = 33}
public{mystring = 'hello'}
public{mytable = {1,2,3}} -- 'flat' tables are supported
-- *no functions, coroutines, or nested-tables (ie tables within tables)*
-- sequins are supported, but only the first layer (no nested sequins)
-- this is enough for making a traditional step-sequencer public
public{myseq = s{1,2,3,4}}
public.myseq() -- get the next value from the sequins
-- remote hosts (eg norns) can support changing the 'playhead'
```
metadata can be attached to public variables, informing a remote device how that value should be modified. it allows support for explicit datatypes, number ranges, options, and actions that are called when a value is remotely updated.
```lua
-- declare a range so a remote host won't allow out-of-bounds values
public{volts = 0}:range(-5, 10) -- limit voltage to crow's hardware range
public.volts = 20 -- 20 will be clamped down to 10
-- types can be 'hinted' by crow, but relies on USB host to implement
-- see documentation for the USB host to see which types are supported
-- below are examples from norns
public{seconds = 1}:range{0.001, 10}:type('exp') -- use exponential scaling
public{count = 0}:type('int') -- force steps to be in integer multiples
-- special types can be used for other data
public{readonly = 'hello'}:type('@') -- @ stops the host changing the value
public{readint = 22}:type('@int') -- @ can be prepended to other types, for eg readonly-integer
public{myslide = 0}:range{-5,10}:type('slider') -- draws myslide with a visual slider
-- options are variables that can be one of a set of possibilities
-- option types are also known as 'enumerated' types
-- list of options must be a list of strings
public{myopt = '+'}:options{'+', '-', '*', '/'}
_ = public.myopt --> returns the string '+'
public.myopt = '*' --> set the value with the string (not an index)
public.myopt = '%' --> IGNORED: the value is not an option, so ignored
-- an action can be triggered when a variable is remotely updated from the host
-- this is very useful when you need to set hardware i/o or timers
public{speed = 1.0}:action( update_speed ) -- call update_speed whenever changed
function update_speed(value) -- value is passed in by the action
metro[1].time = value
end
```
`public.view` is a special set of public parameters that keep a remote device informed of the state of the input and output jacks. transmission is heavily throttled to reduce USB overhead.
```lua
-- (optional) argument is true/false for on/off. no arg activates view.
public.view.all( [state] ) -- set state of all input & output views
public.view.input[n]( [state] ) -- set state of nth input view
public.view.output[n]( [state] ) -- set state of nth output view
```
when implementing public support on a USB host, these functions enable automated discovery & synchronization of variables.
```lua
-- remote fns to be called by remote host, not a crow userscript
public.discover() -- print a list of all declared public vars to the console
-- terminated with the key '_end'
public.update(name, value [, subkey])
-- equivalent to 'public.name = value' but also triggers any 'action'
-- (optional) 3rd argument allows table updates --> public.name[subkey] = value
```
**norns scripting:** remember to prepend `norns.` when using the above functions. example: `norns.crow.public.discover()`
## cal
crow has hardware to enable self-calibration of the CV inputs & outputs. the calibration procedure is run at the factory, so it's unlikely you'll need to use these features in a script. below are the building blocks for building your own calibration system. the official calibration script is located [here](https://github.com/monome/crow/blob/main/util/recalibrate.lua).
```lua
cal.save() -- save current calibration to flash
cal.source(chan) -- configures output->input multiplexer
_ = cal.input[n].offset -- get input offset
_ = cal.input[n].scale -- get input scale
cal.input[n].offset = _ -- set input offset
cal.input[n].scale = _ -- set input scale
_ = cal.output[n].offset -- get output offset
_ = cal.output[n].scale -- get output scale
cal.output[n].offset = _ -- set output offset
cal.output[n].scale = _ -- set output scale
```
## globals
```lua
-- deactivate input modes, zero outputs and slews, and free all metros
-- equivalent to restarting crow, but supresses any active userscript
crow.reset()
tell( event, <args> ) -> ^^event(arg1, ...) -- send a formatted message to host
quote(...) -- returns a string-representation of it's arguments
-- the string is a reconstructed lua chunk, that can be loaded as lua
-- primarily useful for sending lua tables to a USB host with tell()
time() -- returns a count of milliseconds since crow was turned on
_, _, _ = unique_id() -- returns 3 numbers unique to each crow
-- the first number is the most unique, and will likely be unique
-- use all 3 if you need to ensure uniqueness between crows
cputime() -- prints a count of main loops per dsp block. higher == lower cpu
justvolts(fraction [, offset]) -- convert a just ratio to it's volt-per-octave representation
-- fraction can be a single ratio, or a table of ratios
-- in case of a table, a new table of voltages is returned
-- (optional) offset shifts the results by a just ratio (ie transposition)
just12(fraction [, offset]) -- same as justvolts, but output is in '12TET' semitone form
hztovolts(freq [, reference]) -- convert a frequency to a voltage
-- default reference is middle-C == 0V
-- (optional) reference is the frequency that will be referenced as 0V
```