Current section
Files
Jump to
Current section
Files
src/xt/seq.lfe
;;;; Functions for running sequencers in LFE/Extempore.
(defmodule xt.seq
(export
(defmidi 1)
(midi-opts 9)
(play 1)
(set-midi-notes! 1)
(start 1)
(stop 1)))
(include-lib "logjam/include/logjam.hrl")
(include-lib "include/notes/midi-name.lfe")
(defun midi-tmpl ()
"
(define ~s-sequencer
(lambda (beat dur nlst plst)
;; Uncomment these to print decrementing lists here in Extempore (debugging)
;;(println \"nlst:\")
;;(println nlst)
;;(println \"plst:\")
;;(println plst)
(mplay ~s
(car nlst)
(car plst)
dur
~p)
(if (null? (cdr nlst))
(callback (*metro* (+ beat (* 1 dur)))
'~s-sequencer
(+ beat dur)
~s-note-timing
~s-notes
~s-pulses)
(callback (*metro* (+ beat (* 1 dur)))
'~s-sequencer
(+ beat dur)
~s-note-timing
(cdr nlst)
(cdr plst)))))
")
(defun play-tmpl ()
"
(~s-sequencer (*metro* 'get-beat ~s-beats-per-measure)
~s-note-timing
~s-notes
~s-pulses)
")
(defun stop-tmpl ()
"
(define ~s-sequencer
(lambda (beat dur nlst plst)
#t))
")
(defun midi-opts (name midi-device-name midi-device-id midi-channel
notes pulses bpmn bpms note-timing)
`#m(name ,name
midi-device-name ,midi-device-name
midi-device-id ,midi-device-id
midi-channel ,midi-channel
notes ,notes
pulses ,pulses
beats-per-minute ,bpmn
beats-per-measure ,bpms
note-timing ,note-timing
cc-code undefined
cc-value undefined))
;;; String/formatting functions
(defun midi (opts)
(let ((name (mref opts 'name)))
(format (midi-tmpl)
(list name
(mref opts 'midi-device-name)
(mref opts 'midi-channel)
name name name name name name))))
(defun midi-bpms (opts)
(format "(define ~s-beats-per-measure ~p)"
`(,(mref opts 'name)
,(mref opts 'beats-per-measure))))
(defun midi-note-timing (opts)
(format "(define ~s-note-timing ~s)"
`(,(mref opts 'name)
,(mref opts 'note-timing))))
(defun midi-notes (opts)
(format "(define ~s-notes ~s)"
`(,(mref opts 'name)
,(seq-atoms->xt (mref opts 'notes)))))
(defun midi-pulses (opts)
(format "(define ~s-pulses '~w)"
`(,(mref opts 'name)
,(mref opts 'pulses))))
(defun play-midi (opts)
(let ((name (mref opts 'name)))
(format (play-tmpl)
(list name name name name name))))
(defun stop-midi (opts)
(format (stop-tmpl) (list (mref opts 'name))))
(defun tempo (opts)
(format "(*metro* 'set-tempo ~p)"
`(,(mref opts 'beats-per-minute))))
;;; Message functions
(defun defmidi (opts)
(xt.msg:async (midi opts)))
(defun play (opts)
(xt.msg:async (play-midi opts)))
(defun set-midi-notes! (opts)
(xt.msg:async (midi-bpms opts))
(xt.msg:async (midi-note-timing opts))
(xt.msg:async (midi-pulses opts))
(xt.msg:async (midi-notes opts)))
(defun set-tempo! (opts)
(xt.msg:async (tempo opts)))
(defun start (opts)
(set-tempo! opts)
(defmidi opts)
(set-midi-notes! opts)
(play opts))
(defun stop (opts)
(xt.msg:async (stop-midi opts)))
;;; Utility functions
(defun format (tmpl args)
(lists:flatten
(lfe_io_format:fwrite1 tmpl args)))
(defun seq-atoms->xt (atoms)
(xt.lang:sexp "list"
(string:join
(lists:map #'atom_to_list/1 atoms)
" ")))