sourceextensions/audio/demos/scope.xtl
1⍝# A live oscilloscope: the synthesizer's music (Instruments.xtl) plays
2⍝# while a window draws the samples under the playhead -- the left
3⍝# channel above, the right below -- each frame, triggered on the left
4⍝# channel's rising zero crossing so a steady chord stands still.
5⍝# Run: just demo audio scope (q or close: quit)
6
7ᵃᵘ⁼u̲se< "Audio"
8ⁱⁿ⁼u̲se< "Instruments"
9ᶜᵛ⁼u̲se< "Canvas"
10
11⍝# the whole piece, made first: 8 bars side by side (2 by 176384)
12first ← ⁱⁿb̲ar 1
13blen ← 2 s̲elect s̲hape first
14ᵘm̲ore ← { s → s c̲at₂ ⁱⁿb̲ar 1 + (2 s̲elect s̲hape s) d̲iv blen }
15song ← 7 'ᵘm̲ore p̲ower first
16left ← 1 s̲elect₁ song
17right ← 2 s̲elect₁ song
18total ← (f̲loat t̲ally left) ÷ ⁱⁿrate
19
20w ← "Oscilloscope -- X_eTaL" ᶜᵛo̲pen! 768 384
21v ← ᵃᵘo̲utput ⁱⁿrate
22
23⍝# feed bar k+1 when need is 1; how many bars are queued
24ᵘf̲eed ← { need k →
25 need = 0 ? k
26 s ← v ᵃᵘq̲ueue! ((k × blen) + r̲ange blen) s̲elect₂ song
27 k + 1
28}
29
30⍝# a trace: 256 samples as a 64 by 256 picture, each column lit from
31⍝# its sample's row to the next one's, so steep edges stay joined
32ᵘt̲race ← { x →
33 y ← 1 m̲ax 64 m̲in f̲loor 32.5 − 30.0 × x
34 z ← (1 d̲rop y) c̲at -1 t̲ake y
35 r ← r̲ange 64
36 (r '≥ t̲able y m̲in z) × r '≤ t̲able y m̲ax z
37}
38
39⍝# the trigger, as a real scope's: from sample i, the first rising zero
40⍝# crossing of x (one sample below zero, the next at or above it) within
41⍝# the next 2048 samples; i itself when there is none. Drawing from it
42⍝# makes a steady note stand still instead of sliding.
43ᵘt̲rigger ← { x i →
44 c ← (t̲ally x) m̲in i + r̲ange 2048
45 s ← c s̲elect x
46 j ← (((-1 d̲rop s) < 0.0) × (1 d̲rop s) ≥ 0.0) i̲ndexOf 1
47 j ≥ t̲ally c ? i
48 (j + 1) s̲elect c
49}
50
51ᵘq̲uit ← { k →
52 x ← ᶜᵛc̲lose! w
53 y ← ᵃᵘc̲lose! v
54 -1
55}
56
57⍝# a frame: an event, the playhead, more music when less than 0.6 s is
58⍝# queued, and the 512 samples from the trigger (every other one) drawn:
59⍝# both channels triggered by the left (the bass), as a scope triggers
60⍝# from one channel
61ᵘf̲rame ← { k →
62 k < 0 ? k
63 e ← ᶜᵛn̲ext! w
64 ((e m̲atch "close") + e m̲atch "key q") > 0 ? ᵘq̲uit k
65 p ← ᵃᵘp̲layed v
66 ⍝ the end (within a tenth of a second: the device's count, converted
67 ⍝ back from its own rate, can stop just short of the last frame)
68 p ≥ total − 0.1 ? ᵘq̲uit k
69 ahead ← ((f̲loat k × blen) ÷ ⁱⁿrate) − p
70 k2 ← ((k < 8) × ahead < 0.6) ᵘf̲eed k
71 i ← f̲loor p × ⁱⁿrate
72 j ← 1 m̲ax (t̲ally left) m̲in (left ᵘt̲rigger i) + 2 × (r̲ange 256) − 1
73 s ← w ᶜᵛs̲how! (ᵘt̲race j s̲elect left) c̲at ᵘt̲race j s̲elect right
74 k2
75}
76last ← 100000 'ᵘf̲rame p̲ower 1 ᵘf̲eed 0
77"bye"