pxl8/demo/mod/music.fnl
2026-01-08 14:16:49 -06:00

122 lines
3.7 KiB
Fennel

(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)}