(local pxl8 (require :pxl8)) (var time 0) (var step 0) (var ctx nil) (var melody-params nil) (var harmony-params nil) (var bass-params nil) (var playing false) (local bpm 95) (local beat (/ 60 bpm)) (local sixteenth (/ beat 4)) (local eighth (/ beat 2)) (local quarter beat) (local whole (* 4 beat)) (local melody [[69 eighth] [0 sixteenth] [72 sixteenth] [76 eighth] [74 eighth] [72 eighth] [69 eighth] [67 eighth] [69 eighth] [69 eighth] [0 sixteenth] [72 sixteenth] [76 eighth] [79 eighth] [77 eighth] [76 eighth] [74 quarter] [74 eighth] [0 sixteenth] [76 sixteenth] [77 eighth] [74 eighth] [72 eighth] [69 eighth] [67 eighth] [69 eighth] [72 eighth] [0 sixteenth] [74 sixteenth] [76 eighth] [72 eighth] [69 eighth] [67 eighth] [69 quarter]]) (local harmony [[57 whole] ; A3 [60 whole] ; C4 [62 whole] ; D4 [57 whole]]) ; A3 (local bass [[45 eighth] [45 eighth] [57 eighth] [45 eighth] [45 eighth] [57 eighth] [45 eighth] [57 eighth] [48 eighth] [48 eighth] [60 eighth] [48 eighth] [48 eighth] [60 eighth] [48 eighth] [60 eighth] [50 eighth] [50 eighth] [62 eighth] [50 eighth] [50 eighth] [62 eighth] [50 eighth] [62 eighth] [45 eighth] [45 eighth] [57 eighth] [45 eighth] [45 eighth] [57 eighth] [52 eighth] [45 eighth]]) (local step-duration sixteenth) (fn init [] (set ctx (pxl8.sfx_context_create)) (pxl8.sfx_mixer_attach ctx) (set melody-params (pxl8.sfx_voice_params {:waveform pxl8.SFX_WAVE_TRIANGLE :attack 0.02 :decay 0.15 :sustain 0.5 :release 0.15 :filter_type pxl8.SFX_FILTER_LOWPASS :filter_cutoff 4000 :filter_resonance 0.0 :fx_send 0.0})) (set harmony-params (pxl8.sfx_voice_params {:waveform pxl8.SFX_WAVE_TRIANGLE :attack 0.05 :decay 0.2 :sustain 0.3 :release 0.2 :filter_type pxl8.SFX_FILTER_LOWPASS :filter_cutoff 2500 :filter_resonance 0.0 :fx_send 0.0})) (set bass-params (pxl8.sfx_voice_params {:waveform pxl8.SFX_WAVE_TRIANGLE :attack 0.015 :decay 0.1 :sustain 0.7 :release 0.1 :filter_type pxl8.SFX_FILTER_LOWPASS :filter_cutoff 1200 :filter_resonance 0.0 :fx_send 0.0}))) (fn start [] (set playing true) (set time 0) (set step 0)) (fn stop [] (set playing false) (pxl8.sfx_stop_all ctx)) (fn update [dt] (when playing (set time (+ time dt)) (when (>= time step-duration) (set time (- time step-duration)) (local melody-idx (+ 1 (% step (length melody)))) (local melody-entry (. melody melody-idx)) (local melody-note (. melody-entry 1)) (local melody-dur (. melody-entry 2)) (when (> melody-note 0) (pxl8.sfx_play_note ctx melody-note melody-params 0.45 melody-dur)) (local bar (math.floor (/ step 32))) (local harmony-idx (+ 1 (% bar (length harmony)))) (local harmony-entry (. harmony harmony-idx)) (local harmony-note (. harmony-entry 1)) (when (= (% step 32) 0) (pxl8.sfx_play_note ctx harmony-note harmony-params 0.25 whole)) (local bass-step (math.floor (/ step 2))) (local bass-idx (+ 1 (% bass-step (length bass)))) (local bass-entry (. bass bass-idx)) (local bass-note (. bass-entry 1)) (local bass-dur (. bass-entry 2)) (when (= (% step 2) 0) (pxl8.sfx_play_note ctx bass-note bass-params 0.55 bass-dur)) (set step (+ step 1))))) {:init init :start start :stop stop :update update :is-playing (fn [] playing)}