Skip to content

Instantly share code, notes, and snippets.

@mackenziestarr
Created October 24, 2017 21:11
Show Gist options
  • Save mackenziestarr/64cf243fe124fe1392adaf165efc6b3b to your computer and use it in GitHub Desktop.
Save mackenziestarr/64cf243fe124fe1392adaf165efc6b3b to your computer and use it in GitHub Desktop.
(ns twelve-tone.core
(:use overtone.live))
;; reference: http://www.carolingianrealm.info/Music.php?MusicID=29
;; choose 12 notes
(def prime (shuffle (range 0 12)))
;; mathy function for generating the tone rows
(defn generate-tone-rows [prime]
(let [note-count (count prime)
inversion (map #(mod (- note-count %) note-count) prime)
matrix (for [x prime
y inversion
:let [z (mod (+ x y) 12)]]
z)]
(partition note-count matrix)))
(def tone-rows (generate-tone-rows prime)))
;; the above call produces something like this:
;;[[0 11 5 10 3 7 1 6 8 9 2 4]
;; [1 0 6 11 4 8 2 7 9 10 3 5]
;; [7 6 0 5 10 2 8 1 3 4 9 11]
;; [2 1 7 0 5 9 3 8 10 11 4 6]
;; [9 8 2 7 0 4 10 3 5 6 11 1]
;; [5 4 10 3 8 0 6 11 1 2 7 9]
;; [11 10 4 9 2 6 0 5 7 8 1 3]
;; [6 5 11 4 9 1 7 0 2 3 8 10]
;; [4 3 9 2 7 11 5 10 0 1 6 8]
;; [3 2 8 1 6 10 4 9 11 0 5 7]
;; [10 9 3 8 1 5 11 4 6 7 0 2]
;; [8 7 1 6 11 3 9 2 4 5 10 0]]
;; the composition starts from :a3 as the root
(def root (note :a3))
;; these a note length values => milliseconds
;; from: https://msu.edu/course/asc/232/song_project/dectalk_pages/note_to_%20ms.html
(def rhythm [545 818 2180 1635 1090 273 136])
(def melody
(partition 2 (interleave (apply concat (shuffle tone-rows))
(cycle (shuffle rhythm)))))
;; the above expression produces [note milliseconds] tuples from fusing together all the
;; tone rows in a random sequence with accompanying rhythm values
;; looks like this:
;;((0 2180)
;; (11 818)
;; (5 545)
;; (10 1090)
;; ...
;; )
(def harmony
(partition 2 (interleave (apply concat (shuffle tone-rows))
(cycle (shuffle rhythm)))))
;; these are defining the instruments
(definst sine-blip [freq 400]
(let [snd (mix (sin-osc (* freq [0.99 1.01])))
env (env-gen (perc 0.02 0.7) :action FREE)]
(free-verb (* 0.8 env snd) 0.7)))
(definst saw-blip [freq 400]
(let [snd (mix (saw (* freq [0.99 1.01])))
env (env-gen (perc 0.02 0.7) :action FREE)]
(free-verb (* 0.3 env snd) 0.7)))
;; this is a thing that takes the format of `melody` and `harmony` and
;; schedules the notes to be played in the future using a pattern
;; called "temporal recursion"
(defn play
([line sound]
(play (now) line sound))
([clock line sound]
(let [[note time] (first line)]
(at time
(sound (midi->hz (+ root note))))
(apply-at (+ clock time) #'play [(+ clock time) (next line) sound]))))
;; finally the magic, lets play the tune!
(do
(play melody sine-blip)
(play harmony saw-blip))
(recording-start "~/Desktop/12tone.wav")
(recording-stop)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment