;; ============================================================================
;; Theme in A minor
;; Key: Am | Tempo: 95 BPM | Time: 4/4
;; ============================================================================
;;
;; Melody (Triangle, A4 range):
;; Bar 1: A4~ .C5  | E5  D5  | C5  A4  | G4  A4  |
;; Bar 2: A4~ .C5  | E5  G5  | F5  E5  | D5~~~~  |
;; Bar 3: D5~ .E5  | F5  D5  | C5  A4  | G4  A4  |
;; Bar 4: C5~ .D5  | E5  C5  | A4  G4  | A4~~~~  |
;;
;; Bass (Triangle, A2 range, octave jumps):
;; | A2 A2 A3 A2 | A2 A3 A2 A3 | C3 C3 C4 C3 | C3 C4 C3 C4 |
;; | D3 D3 D4 D3 | D3 D4 D3 D4 | A2 A2 A3 A2 | A2 A3 E3 A2 |
;;
;; Legend: ~ = held note, . = sixteenth rest before
;; ============================================================================

(local pxl8 (require :pxl8))

(var time 0)
(var step 0)
(var ctx nil)
(var melody-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 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.create_sfx_context))

  (local reverb (pxl8.create_reverb
    {:room 0.5 :damping 0.4 :mix 0.35}))
  (local compressor (pxl8.create_compressor
    {:threshold -18 :ratio 6 :attack 3 :release 100}))
  (ctx:append_node reverb)
  (ctx:append_node compressor)

  (ctx:attach)

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

  (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 1800 :filter_resonance 0.0
     :fx_send 0.15})))

(fn start []
  (set playing true)
  (set time 0)
  (set step 0))

(fn stop []
  (set playing false)
  (ctx:stop_all))

(fn update [dt]
  (when playing
    (set time (+ time dt))
    (while (>= 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)
        (ctx:play_note melody-note melody-params 0.45 melody-dur))

      (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)
        (ctx:play_note bass-note bass-params 0.55 bass-dur))

      (set step (+ step 1)))))

{:init init
 :start start
 :stop stop
 :update update
 :is-playing (fn [] playing)
 :get-step (fn [] step)}
