clojure-school-of-music/src/music.clj

95 lines
3.2 KiB
Clojure
Raw Normal View History

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
;; [:= 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
;; 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}])
(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))
(defn abs-pitch [pitch-class octave]
"Returns the absolute pitch of a pitch class at an octave"
(+ (* 12 octave) (pc-to-int pitch-class)))
;; TODO
(defn pitch [abs-pitch]
"Returns the pitch class and octave represented by the absolute pitch.
In cases of enharmonic equivalence, returns the sharp rather than the flat."
())
;; TODO exercise 2.1 (ii-V-I function)
;; this will involve being able to answer, "what pitch is n semitones above this one?"
(def ii-V-I
(let [Dm [:= (note 1 [:D 4])
(note 1 [:F 4])
(note 1 [:A 4])]
Gmaj [:= (note 1 [:G 4])
(note 1 [:B 4])
(note 1 [:D 5])]
Cmaj [:= (note 2 [:C 4])
(note 2 [:E 4])
(note 2 [:G 4])]]
[:+ Dm Gmaj Cmaj]))