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"