programextensions/audio/demos/scope.xtl

A live oscilloscope: the synthesizer's music (Instruments.xtl) plays while a window draws the samples under the playhead -- the left channel above, the right below -- each frame, triggered on the left channel's rising zero crossing so a steady chord stands still. Run: just demo audio scope (q or close: quit)

source · imports au: extensions/audio/lib/Audio.xtl; in: extensions/audio/demos/Instruments.xtl; cv: extensions/canvas/lib/Canvas.xtl

first : Float

value · line 12

the whole piece, made first: 8 bars side by side (2 by 176384)

first ← ⁱⁿb̲ar 1
Used in: blen, song

blen : Int

value · line 13
blen ← 2 s̲elect s̲hape first

ᵘm̲ore : Float -> Float

function · line 14
ᵘm̲ore ← { s → s c̲at₂ ⁱⁿb̲ar 1 + (2 s̲elect s̲hape s) d̲iv blen }
Used in: song

song : Float

value · line 15
song ← 7 'ᵘm̲ore p̲ower first
Used in: left, right, ᵘf̲eed

left : Float

value · line 16
left ← 1 s̲elect₁ song
Used in: total, ᵘf̲rame

total : Float

value · line 18
total ← (f̲loat t̲ally left) ÷ ⁱⁿrate
Used in: ᵘf̲rame

w : Int

value · line 20
w ← "Oscilloscope -- X_eTaL" ᶜᵛo̲pen! 768 384

v : Int

value · line 21
v ← ᵃᵘo̲utput ⁱⁿrate

ᵘf̲eed : Num a => a -> Int -> Int

function · line 24

feed bar k+1 when need is 1; how many bars are queued

ᵘf̲eed ← { need k →
  need = 0 ? k
  s ← v ᵃᵘq̲ueue! ((k × blen) + r̲ange blen) s̲elect₂ song
  k + 1
}
Used in: ᵘf̲rame, last

ᵘt̲race : (Num a, Truthy a) => Float -> a

function · line 32

a trace: 256 samples as a 64 by 256 picture, each column lit from its sample's row to the next one's, so steep edges stay joined

ᵘt̲race ← { x →
  y ← 1 m̲ax 64 m̲in f̲loor 32.5 − 30.0 × x
  z ← (1 d̲rop y) c̲at -1 t̲ake y
  r ← r̲ange 64
  (r '≥ t̲able y m̲in z) × r '≤ t̲able y m̲ax z
}
Used in: ᵘf̲rame

ᵘt̲rigger : Float -> Int -> Int

function · line 43

the trigger, as a real scope's: from sample i, the first rising zero crossing of x (one sample below zero, the next at or above it) within the next 2048 samples; i itself when there is none. Drawing from it makes a steady note stand still instead of sliding.

ᵘt̲rigger ← { x i →
  c ← (t̲ally x) m̲in i + r̲ange 2048
  s ← c s̲elect x
  j ← (((-1 d̲rop s) < 0.0) × (1 d̲rop s) ≥ 0.0) i̲ndexOf 1
  j ≥ t̲ally c ? i
  (j + 1) s̲elect c
}
Used in: ᵘf̲rame

ᵘq̲uit : (Any a, Num b) => a -> b

function · line 51
ᵘq̲uit ← { k →
  x ← ᶜᵛc̲lose! w
  y ← ᵃᵘc̲lose! v
  -1
}
Used in: ᵘf̲rame

ᵘf̲rame : Int -> Int

function · line 61

a frame: an event, the playhead, more music when less than 0.6 s is queued, and the 512 samples from the trigger (every other one) drawn: both channels triggered by the left (the bass), as a scope triggers from one channel

ᵘf̲rame ← { k →
  k < 0 ? k
  e ← ᶜᵛn̲ext! w
  ((e m̲atch "close") + e m̲atch "key q") > 0 ? ᵘq̲uit k
  p ← ᵃᵘp̲layed v
  ⍝ the end (within a tenth of a second: the device's count, converted
  ⍝ back from its own rate, can stop just short of the last frame)
  p ≥ total − 0.1 ? ᵘq̲uit k
  ahead ← ((f̲loat k × blen) ÷ ⁱⁿrate) − p
  k2 ← ((k < 8) × ahead < 0.6) ᵘf̲eed k
  i ← f̲loor p × ⁱⁿrate
  j ← 1 m̲ax (t̲ally left) m̲in (left ᵘt̲rigger i) + 2 × (r̲ange 256) − 1
  s ← w ᶜᵛs̲how! (ᵘt̲race j s̲elect left) c̲at ᵘt̲race j s̲elect right
  k2
}
Used in: last

last : Int

value · line 76
last ← 100000 'ᵘf̲rame p̲ower 1 ᵘf̲eed 0