;; (sys:load "libs/external/instruments_ext.xtm")
;; hermite sampler (multi-channels version)
;;
;; slower but less noisy (default)
(bind-func sampler_note
(lambda ()
(let ((amp_env:|4,float| (array 3.0:f 0.0 1.0 20.0)))
(lambda (data:NoteData* samples:|128,SAMPLE*|* samples_length:|128,i64|* samples_offsets:|128,i64|* samples_channels:|128,i64|* index:i64 nargs dargs)
(let ((starttime (note_starttime data))
(freq (note_frequency data))
(amp (note_amplitude data))
(duration (note_duration data))
(gate 1.0:f)
(pan:SAMPLE (if (> nargs 1) (pref dargs 1) 0.5)) ;; first darg is bank
(offset:SAMPLE (if (> nargs 2) (pref dargs 2) 0.0))
(rev:i1 (if (> nargs 3) (if (> (pref dargs 3) 0.01) #t #f) #f))
(a (aref amp_env 0))
(d (aref amp_env 1))
(s (aref amp_env 2))
(r (aref amp_env 3))
(rtime:i64 (convert (* SRf (/ (aref amp_env 3) 1000.0))))
(dt 0) (rt 0)
(total_time (+ duration rtime))
(env (adsr_c))
(eamp 0.0)
(idx_freq (convert (midi2frq (convert index SAMPLE))))
(channels:i64 (convert (aref samples_channels index)))
(phase:double (convert (+ offset (convert (aref samples_offsets index)))))) ;; phase unit is audio frames
(if (and rev (< phase 0.01))
(set! phase (convert (- (aref samples_length index) 10))))
(lambda (time:i64 chan:i64)
(if (= chan 0)
(begin
(set! dt (+ dt 1))
(if (> dt duration) (set! gate 0.0))
(set! eamp (env chan gate a d s r))
(if (< gate 0.1)
(begin (set! rt (+ rt 1))
(if (> rt rtime) (note_active data #f))))))
(let ((rate (/ (convert freq) idx_freq))
(pos:double (if (= chan 0) ;; only increment once per frame
(if rev
(set! phase (- phase rate))
(set! phase (+ phase rate)))
phase))
(posi:i64 (convert (floor pos)))
(posx (+ (* posi channels) (if (< chan channels) chan 0)))
(lgth:i64 (- (aref samples_length index) 10))
(dat (aref samples index)))
(if (< (fabs (- rate 1.0)) 0.01)
(if (or (> posi lgth) (< posi 0)) 0.0 (* amp eamp (panner chan pan) (pref dat posx)))
(let ((y1 (if (or (> posi lgth) (< posi 1)) 0.0
(if rev
(pref dat (+ posx channels))
(pref dat (- posx channels)))))
(x0 (if (or (> posi lgth) (< posi 0)) 0.0 (pref dat posx)))
(x1 (if (or (> (+ posi 1) lgth) (< (- posi 1) 0)) 0.0
(if rev
(pref dat (- posx channels))
(pref dat (+ posx channels)))))
(x2 (if (or (> (+ posi 2) lgth) (< (- posi 2) 0)) 0.0
(if rev
(pref dat (- posx (* 2 channels)))
(pref dat (+ posx (* 2 channels)))))))
(* (panner chan pan)
amp eamp (hermite_interp (dtof (modulo pos 1.0)) y1 x0 x1 x2)))))))))))