Current section
Files
Jump to
Current section
Files
src/uth/note.lfe
(defmodule uth.note
(export all))
(defun timings()
`#m(1 1.0
1/2 0.5
1/4 0.25
1/8 0.125
1/16 0.0625
1/32 0.03125
1/64 0.015625
;; dotted notes
1/2. ,(+ 0.5 0.25)
1/4. ,(+ 0.25 0.125)
1/8. ,(+ 0.125 0.0625)
1/16. ,(+ 0.0625 0.03125)
1/32. ,(+ 0.03125 0.015625)
1/64. ,(+ 0.015625 0.0078125)))
(defun denom->timing
((1) (mref (timings) '1))
((2) (mref (timings) '1/2))
((4) (mref (timings) '1/4))
((8) (mref (timings) '1/8))
((16) (mref (timings) '1/16))
((32) (mref (timings) '1/32))
((64) (mref (timings) '1/64)))
(defun duration-fn
((bpm (= `#(,_beats/meas ,beat-note) _time-sig))
(lambda (k)
(let ((beat (denom->timing beat-note)))
(trunc (* 1000
(/ bpm 60)
beat
(/ (mref (timings) k) beat)))))))
(defun 1oct () 12)
(defun 2oct () 24)
(defun mod-1oct (note) (rem note (1oct)))
(defun mod-2oct (note) (rem note (2oct)))
(defun names ()
"This includes enharmonics."
`(C C# Db D D# Eb E F F# Gb G G# Ab A A# Bb B
,@(enharmonic-sharps)
,@(enharmonic-flats)
,@(double-flats)
,@(double-sharps)))
(defun sharps () '(C# D# F# G# A#))
(defun enharmonic-sharps () '(B# E#))
(defun double-sharps () '(C## D## E## F## G## A## B##))
(defun flats () '(Db Eb Gb Ab Bb))
(defun enharmonic-flats () '(Fb Cb))
(defun double-flats () '(Cbb Dbb Ebb Fbb Gbb Abb Bbb))
(defun !flats () (++ (sharps)
(enharmonic-sharps)
(double-sharps)
(enharmonic-flats)
(double-flats)))
(defun !sharps () (++ (enharmonic-sharps)
(double-sharps)
(flats)
(enharmonic-flats)
(double-flats)))
(defun flat? (note) (not (lists:member note (!flats))))
(defun sharp? (note) (not (lists:member note (!sharps))))
(defun accidental (note)
(cond
((flat? note) 'flat)
((sharp? note) 'sharp)
('true 'natural)))
(defun numbers ()
"Useful for working with intervals; not related to MIDI in any way."
(list 0 1 1 2 3 3 4 5 6 6 7 8 8 9 10 10 11
0 5 ; enharmonic sharps
4 11 ; enharmonic flats
10 0 2 3 5 7 9 ; double-flats
2 4 6 7 9 11 1 ; double-sharps
))
(defun name->number () (lists:zip (names) (numbers)))
(defun number->name () (lists:zip (numbers) (names)))
(defun name-all (number) (proplists:get_all_values number (number->name)))
(defun name
((number '#(flat))
(car
(lists:filter
#'flat?/1
(name-all number))))
((number '#(sharp))
(car
(lists:filter
#'sharp?/1
(name-all number))))
((number '#(all))
(name-all number))
((number _)
(car (name-all number))))
(defun name (number)
(car (name-all number)))
(defun number (name)
(proplists:get_value name (name->number)))
(defun number
((name 'with-error)
(if (lists:member name (names))
(number name)
(uth.errors:note-name))))
(defun ->intervals
((`(,previous . ()))
#(error "list of notes too short"))
((`(,previous . ,notes))
(->intervals previous notes '())))
(defun ->intervals
((previous '() acc)
(lists:reverse acc))
((previous `(,head . ,tail) acc)
(->intervals head tail (cons (- (number head) (number previous)) acc))))
(defun retrograde ()
'not-implemented)
(defun transpose (new-root notes)
(transpose new-root notes #(one)))
(defun transpose (new-root notes opts)
(let ((ivals (->intervals notes)))
(uth.interval:->notes new-root
ivals
opts)))
(defun invert (notes)
(invert (car notes) notes #(one)))
(defun invert (root-note notes)
(invert root-note notes #(one)))
(defun invert (root-note notes opts)
;; TODO: check for error(s):
(let ((ivals (->intervals notes)))
(uth.interval:->notes root-note
(uth.interval:invert ivals)
opts)))