2018-07-16 22:39:51 +00:00
|
|
|
(ns music)
|
|
|
|
|
|
|
|
;; an octave is an int, e.g. 4
|
|
|
|
;; a pitch class is a keyword from :Abb to G##
|
|
|
|
;; a pitch is a [pitch class, octave] vector, e.g. [:C 4] is middle C
|
|
|
|
;; a duration is a rational number, e.g. 1/8
|
|
|
|
;; a note has a duration, optionally a pitch, and optionally any other necessary keys (e.g. loudness)
|
|
|
|
;; {:duration 1/8 :pitch [:A# 3]} is a note. {:duration 1/4} is a note (really, a rest)
|
2018-07-17 03:12:32 +00:00
|
|
|
|
2018-07-19 03:22:20 +00:00
|
|
|
;; [:= noteA noteB] represents noteA played simultaneously with noteB
|
|
|
|
;; [:+ noteA noteB] represents noteA played followed by noteB
|
|
|
|
|
|
|
|
;; [:modify noteA control] annotates noteA with the control map
|
2018-07-17 03:12:32 +00:00
|
|
|
;; control maps have keys like ::tempo, ::transpose, ::instrument, ::phrase, ::player, ::keysig
|
2018-07-19 03:22:20 +00:00
|
|
|
|
|
|
|
;; primitives
|
|
|
|
(def note1 {:duration 1/4, :pitch [:Eb 4]})
|
|
|
|
(def note2 {:duration 1/4, :pitch [:G 4]})
|
|
|
|
(def note3 {:duration 1/8, :pitch [:F 4]})
|
|
|
|
(def rest1 {:duration 1/8})
|
|
|
|
|
|
|
|
[:= note1 note2] ;; note1 simultaneous with note2
|
|
|
|
[:+ note1 note3] ;; note1 followed by note3
|
|
|
|
[:+ [:= note1 note2] note3] ;; note1 simultaneous with note2, followed by note3
|
|
|
|
[:modify note1 {::tempo 120}] ;; note1 modified to have tempo 120
|
|
|
|
[:modify [:= note1 note2] {::tempo 120 ::instrument "Dope Organ"}] ;; note1 simultaneous with note2 modified with tempo and instrument
|
|
|
|
|
|
|
|
;; can be nested to arbitrary complexity
|
|
|
|
(def song
|
|
|
|
[:= [:+ {:duration 1/4, :pitch [:D 3]}
|
|
|
|
{:duration 1/4, :pitch [:E 3]}
|
|
|
|
{:duration 1/4, :pitch [:F 3]}]
|
|
|
|
[:+ {:duration 1/4, :pitch [:F 3]}
|
|
|
|
{:duration 1/4, :pitch [:G 3]}
|
|
|
|
{:duration 1/4, :pitch [:A 3]}]])
|
|
|
|
|
|
|
|
(defn note [duration pitch]
|
|
|
|
{:duration duration, :pitch pitch})
|
|
|
|
|
|
|
|
(defn rest [duration]
|
|
|
|
{:duration duration})
|
|
|
|
|
|
|
|
(defn tempo [tempo music]
|
|
|
|
[:modify music {::tempo tempo}])
|
|
|
|
|
|
|
|
(defn transpose [abs-pitch music]
|
|
|
|
[:modify music {::transpose abs-pitch}])
|
|
|
|
|
|
|
|
(defn instrument [instrument music]
|
|
|
|
[:modify music {::instrument instrument}])
|
|
|
|
|
|
|
|
(defn phrase [phrase-attributes music]
|
|
|
|
[:modify music {::phrase phrase-attributes}])
|
|
|
|
|
|
|
|
(defn player [player-name music]
|
|
|
|
[:modify music {::player player-name}])
|
|
|
|
|
2018-07-19 21:50:11 +00:00
|
|
|
(defn keysig [pitch-class mode music]
|
|
|
|
[:modify music {::keysig [pitch-class mode]}])
|
|
|
|
|
|
|
|
(defn pc-to-int [pitch-class]
|
|
|
|
(case pitch-class
|
|
|
|
:Cbb -2 :Cb -1 :C 0 :C# 1 :C## 2
|
|
|
|
:Dbb 0 :Db 1 :D 2 :D# 3 :D## 4
|
|
|
|
:Ebb 2 :Eb 3 :E 4 :E# 5 :E## 6
|
|
|
|
:Fbb 3 :Fb 4 :F 5 :F# 6 :F## 7
|
|
|
|
:Gbb 5 :Gb 6 :G 7 :G# 8 :G## 9
|
|
|
|
:Abb 7 :Ab 8 :A 9 :A# 10 :A## 11
|
|
|
|
:Bbb 9 :Bb 10 :B 11 :B# 12 :B## 13))
|
|
|
|
|
2018-07-20 15:37:17 +00:00
|
|
|
(defn abs-pitch
|
2018-07-19 21:50:11 +00:00
|
|
|
"Returns the absolute pitch of a pitch class at an octave"
|
2018-07-20 15:37:17 +00:00
|
|
|
([pitch-class octave] (abs-pitch [pitch-class octave]))
|
|
|
|
([[pitch-class octave]]
|
|
|
|
(+ (* 12 octave) (pc-to-int pitch-class))))
|
2018-07-19 21:50:11 +00:00
|
|
|
|
2018-07-20 15:37:17 +00:00
|
|
|
(defn pitch
|
2018-07-19 21:50:11 +00:00
|
|
|
"Returns the pitch class and octave represented by the absolute pitch.
|
|
|
|
In cases of enharmonic equivalence, returns the sharp rather than the flat."
|
2018-07-20 15:37:17 +00:00
|
|
|
[abs-pitch]
|
2018-07-20 00:10:03 +00:00
|
|
|
(let [scale [:C :C# :D :D# :E :F :F# :G :G# :A :A# :B]
|
|
|
|
octave (quot abs-pitch 12)
|
|
|
|
index (rem abs-pitch 12)]
|
|
|
|
[(nth scale index) octave]))
|
|
|
|
|
|
|
|
(defn trans [n [pitch-class octave]]
|
|
|
|
(pitch (+ (abs-pitch pitch-class octave) n)))
|
2018-07-19 21:50:11 +00:00
|
|
|
|
2018-07-20 15:37:17 +00:00
|
|
|
(defn line
|
2018-07-20 02:41:53 +00:00
|
|
|
"Combines musics via the sequential (:+) operator"
|
2018-07-20 15:37:17 +00:00
|
|
|
[[m & ms :as musics]]
|
|
|
|
(if (nil? (seq musics))
|
2018-07-20 02:41:53 +00:00
|
|
|
(rest 0)
|
|
|
|
[:+ m (line ms)]))
|
2018-07-20 15:37:17 +00:00
|
|
|
|
|
|
|
(defn chord
|
|
|
|
"Combines musics via the simultaneous (:=) operator"
|
|
|
|
[[m & ms :as musics]]
|
|
|
|
(if (nil? (seq musics))
|
|
|
|
(rest 0)
|
|
|
|
[:= m (line ms)]))
|
|
|
|
|
|
|
|
(defn max-pitch [[p & ps :as pitches]]
|
|
|
|
(if (nil? (seq pitches))
|
|
|
|
(pitch 0)
|
|
|
|
(let [max-ps (max-pitch ps)]
|
|
|
|
(if (> (abs-pitch p) (abs-pitch max-ps))
|
|
|
|
p
|
|
|
|
max-ps))))
|