|
9 | 9 | :tags [:music :harmony :functional-programming :midi]}}} |
10 | 10 |
|
11 | 11 | (ns music.noon-introduction |
12 | | - (:require [noon.viz.piano-roll :as pr] |
13 | | - [noon.eval :refer [score]])) |
| 12 | + (:require [noon.viz.clay :as nclay])) |
14 | 13 |
|
15 | 14 | ;; ## What is Noon? |
16 | 15 | ;; |
|
27 | 26 | ;; A noon score is a set of event maps. That's it. |
28 | 27 | ;; Each event represents a MIDI note with properties like position, duration, velocity, and pitch: |
29 | 28 | ;; |
30 | | -;;```clj |
31 | | -;; noon.events/DEFAULT_EVENT |
32 | | -;; ;; => {:position 0, :duration 1, :channel 0, :track 0, |
33 | | -;; ;; :velocity 80, :pitch {...}, :voice 0, :patch [0 4]} |
34 | | -;;``` |
35 | | -;; |
| 29 | + |
| 30 | +(nclay/editor "noon.events/DEFAULT_EVENT") |
| 31 | + |
36 | 32 | ;; A fresh score contains a single event — middle C: |
37 | | -;; |
38 | | -;;```clj |
39 | | -;; (score) |
40 | | -;; ;; => #{{:position 0, :duration 1, ...}} |
41 | | -;;``` |
| 33 | + |
| 34 | +(nclay/editor "(score)") |
42 | 35 | ;; |
43 | 36 | ;; Everything in noon is a **transformation** — a function from score to score. |
44 | 37 | ;; You build music by composing transformations: |
45 | 38 | ;; |
46 | | -;;```clj |
47 | | -;; (play dur2 (tup s0 s1 s2 s3)) |
48 | | -;;``` |
49 | | -;; |
50 | 39 | ;; This plays an ascending arpeggio at double duration. |
51 | 40 | ;; `dur2` doubles the duration, `tup` splits it into equal parts, |
52 | 41 | ;; and `s0` through `s3` are structural intervals (more on those soon). |
53 | 42 |
|
| 43 | +(nclay/editor "(play dur2 (tup s0 s1 s2 s3))") |
| 44 | + |
54 | 45 | ;; ## Building Blocks |
55 | 46 | ;; |
56 | 47 | ;; Three core functions handle most of the composition: |
57 | 48 | ;; |
58 | 49 | ;; **`lin`** — concatenate transformations in sequence (a melody): |
59 | | -;; |
60 | | -;;```clj |
61 | | -;; (play (lin C0 E0 G0 B0)) |
62 | | -;;``` |
63 | | -;; |
| 50 | + |
| 51 | +(nclay/editor "(play (lin C0 E0 G0 B0))") |
| 52 | + |
64 | 53 | ;; **`par`** — stack transformations simultaneously (a chord): |
65 | | -;; |
66 | | -;;```clj |
67 | | -;; (play (par C0 Eb0 G0)) ; C minor chord |
68 | | -;;``` |
69 | | -;; |
| 54 | + |
| 55 | +(nclay/editor "(play (par C0 Eb0 G0))") |
| 56 | + |
70 | 57 | ;; **`tup`** — like `lin`, but fitted to the current duration (a tuplet): |
71 | | -;; |
72 | | -;;```clj |
73 | | -;; (play (tup c0 c3 c7 c10)) ; four notes in the space of one |
74 | | -;;``` |
75 | | -;; |
| 58 | + |
| 59 | +(nclay/editor "(play (tup c0 c3 c7 c10))") |
| 60 | + |
76 | 61 | ;; These compose freely. A chord progression with arpeggios: |
77 | | -;; |
78 | | -;;```clj |
79 | | -;; (play (lin I IV V I) |
80 | | -;; (each (tup s0 s1 s2))) |
81 | | -;;``` |
82 | | -;; |
| 62 | + |
| 63 | +(nclay/editor "(play (lin I IV V I) (each (tup s0 s1 s2)))") |
| 64 | + |
83 | 65 | ;; And transformations can be chained using vectors: |
84 | | -;; |
85 | | -;;```clj |
86 | | -;; (play [dur:2 (lin C0 E0 G0)]) |
87 | | -;;``` |
| 66 | + |
| 67 | +(nclay/editor "(play [dur:2 (lin C0 E0 G0)])") |
88 | 68 | ;; |
89 | 69 | ;; A few more useful combinators: |
90 | 70 | ;; |
|
93 | 73 | ;; - `each` — apply a transformation to every event individually |
94 | 74 | ;; - `chans` — like `par` but on separate MIDI channels (for different instruments) |
95 | 75 | ;; |
96 | | -;;```clj |
97 | | -;; (play |
98 | | -;; (chans |
99 | | -;; [(patch :ocarina) (tup s0 s1 s2 s3) (rep 4 s1)] |
100 | | -;; [(patch :vibraphone) vel3 (par s0 s1 s2)] |
101 | | -;; [(patch :acoustic-bass) o1-]) |
102 | | -;; (dup 4)) |
103 | | -;;``` |
| 76 | +(nclay/editor "(play |
| 77 | + (chans |
| 78 | + [(patch :ocarina) (tup s0 s1 s2 s3) (rep 4 s1)] |
| 79 | + [(patch :vibraphone) vel3 (par s0 s1 s2)] |
| 80 | + [(patch :acoustic-bass) o1-]) |
| 81 | + (dup 4))") |
104 | 82 |
|
105 | 83 | ;; ## How Musicians Think About Pitch |
106 | 84 | ;; |
|
143 | 121 | ;; |
144 | 122 | ;; Each event in a noon score carries this full context under its `:pitch` key: |
145 | 123 | ;; |
146 | | -;;```clj |
147 | | -;; (:pitch noon.events/DEFAULT_EVENT) |
148 | | -;; ;; => {:scale [0 2 4 5 7 9 11] ; C major |
149 | | -;; ;; :structure [0 2 4] ; triad |
150 | | -;; ;; :origin {:d 35, :c 60} ; middle C |
151 | | -;; ;; :position {:t 0, :s 0, :d 0, :c 0}} |
152 | | -;;``` |
| 124 | + |
| 125 | +(nclay/editor "(:pitch noon.events/DEFAULT_EVENT)") |
153 | 126 | ;; |
154 | 127 | ;; The `:position` map holds an offset at each layer. |
155 | 128 | ;; This is where the note actually *is* within the harmonic context. |
|
159 | 132 | ;; Each layer has a corresponding **step** operation. |
160 | 133 | ;; A step moves you to the next position *on that layer*: |
161 | 134 | ;; |
162 | | -;;```clj |
163 | | -;; (play (tup c0 c1 c2 c3 c4 c5 c6)) ; chromatic: semitone by semitone |
164 | | -;; (play (tup d0 d1 d2 d3 d4 d5 d6)) ; diatonic: scale degree by degree |
165 | | -;; (play (tup s0 s1 s2 s3)) ; structural: chord tone by chord tone |
166 | | -;; (play (tup t0 t1 t2)) ; tonic: octave by octave |
167 | | -;;``` |
| 135 | + |
168 | 136 |
|
169 | 137 | ;; *The four step types — chromatic, diatonic, structural, and tonic — each walking up from C4. |
170 | 138 | ;; Note how the same ascending pattern selects different notes depending on the layer. |
171 | | -;; Colors indicate each note's role: tonic (dark blue), structural (medium blue), diatonic (light blue), chromatic (grey).* |
172 | | - |
173 | | -^:kindly/hide-code |
174 | | -(pr/piano-roll-group |
175 | | - [{:label "chromatic — (tup c0 c1 c2 c3 c4 c5 c6)" |
176 | | - :score (score (tup c0 c1 c2 c3 c4 c5 c6))} |
177 | | - {:label "diatonic — (tup d0 d1 d2 d3 d4 d5 d6)" |
178 | | - :score (score (tup d0 d1 d2 d3 d4 d5 d6))} |
179 | | - {:label "structural — (tup s0 s1 s2 s3)" |
180 | | - :score (score (tup s0 s1 s2 s3))} |
181 | | - {:label "tonic — (tup t0 t1 t2)" |
182 | | - :score (score (tup t0 t1 t2))}]) |
| 139 | +;; Try editing the code and pressing Eval & Play!* |
| 140 | + |
| 141 | +;; chromatic — semitone by semitone: |
| 142 | +(nclay/editor "(score (tup c0 c1 c2 c3 c4 c5 c6))") |
| 143 | + |
| 144 | +;; diatonic — scale degree by degree: |
| 145 | +(nclay/editor "(score (tup d0 d1 d2 d3 d4 d5 d6))") |
| 146 | + |
| 147 | +;; structural — chord tone by chord tone: |
| 148 | +(nclay/editor "(score (tup s0 s1 s2 s3))") |
| 149 | + |
| 150 | +;; tonic — octave by octave: |
| 151 | +(nclay/editor "(score (tup t0 t1 t2))") |
183 | 152 |
|
184 | 153 | ;; The chromatic run gives you all 7 semitones. |
185 | 154 | ;; The diatonic run gives you the major scale. |
|
190 | 159 | ;; Here's the key insight: **steps are always relative to the current harmonic context**. |
191 | 160 | ;; When you change the scale or structure, the same step operations produce different notes: |
192 | 161 | ;; |
193 | | -;;```clj |
194 | | -;; ;; Major scale |
195 | | -;; (play dur:4 (lin d0 d1 d2 d3 d4 d5 d6 d7)) |
196 | | -;; |
197 | | -;; ;; Dorian scale — same code, different result |
198 | | -;; (play dur:4 (scale :dorian) (lin d0 d1 d2 d3 d4 d5 d6 d7)) |
199 | | -;; |
200 | | -;; ;; Hungarian scale |
201 | | -;; (play dur:4 (scale :hungarian) (lin d0 d1 d2 d3 d4 d5 d6 d7)) |
202 | | -;;``` |
| 162 | + |
203 | 163 |
|
204 | 164 | ;; *Same diatonic steps, three different scales. The code is identical — only the scale context changes. |
205 | 165 | ;; Notice how the intervals between notes shift while the structure remains.* |
206 | 166 |
|
207 | | -^:kindly/hide-code |
208 | | -(pr/piano-roll-group |
209 | | - [{:label "major (default)" |
210 | | - :score (score dur:4 (lin d0 d1 d2 d3 d4 d5 d6 d7))} |
211 | | - {:label "dorian" |
212 | | - :score (score dur:4 (scale :dorian) (lin d0 d1 d2 d3 d4 d5 d6 d7))} |
213 | | - {:label "hungarian" |
214 | | - :score (score dur:4 (scale :hungarian) (lin d0 d1 d2 d3 d4 d5 d6 d7))}] |
215 | | - {:shared-pitch-range true}) |
| 167 | +;; major (default): |
| 168 | +(nclay/editor "(score dur:4 (lin d0 d1 d2 d3 d4 d5 d6 d7))") |
| 169 | + |
| 170 | +;; dorian: |
| 171 | +(nclay/editor "(score dur:4 (scale :dorian) (lin d0 d1 d2 d3 d4 d5 d6 d7))") |
| 172 | + |
| 173 | +;; hungarian: |
| 174 | +(nclay/editor "(score dur:4 (scale :hungarian) (lin d0 d1 d2 d3 d4 d5 d6 d7))") |
216 | 175 |
|
217 | 176 | ;; The same goes for structural steps: |
218 | 177 | ;; |
219 | | -;;```clj |
220 | | -;; ;; Triad arpeggio |
221 | | -;; (play (tup s0 s1 s2 s3)) |
222 | | -;; |
223 | | -;; ;; Tetrad arpeggio — same code, richer chord |
224 | | -;; (play (structure :tetrad) (tup s0 s1 s2 s3)) |
225 | | -;;``` |
| 178 | + |
226 | 179 |
|
227 | 180 | ;; *Triad vs tetrad: the same structural steps produce C-E-G-C with a triad structure, |
228 | 181 | ;; but C-E-G-B when the structure is set to tetrad — the seventh appears automatically.* |
229 | 182 |
|
230 | | -^:kindly/hide-code |
231 | | -(pr/piano-roll-group |
232 | | - [{:label "triad (default) — (tup s0 s1 s2 s3)" |
233 | | - :score (score (tup s0 s1 s2 s3))} |
234 | | - {:label "tetrad — (structure :tetrad) (tup s0 s1 s2 s3)" |
235 | | - :score (score (structure :tetrad) (tup s0 s1 s2 s3))}] |
236 | | - {:shared-pitch-range true}) |
| 183 | +;; triad (default): |
| 184 | +(nclay/editor "(score (tup s0 s1 s2 s3))") |
| 185 | + |
| 186 | +;; tetrad: |
| 187 | +(nclay/editor "(score (structure :tetrad) (tup s0 s1 s2 s3))") |
237 | 188 |
|
238 | 189 | ;; ## Changing the Context |
239 | 190 | ;; |
240 | 191 | ;; Several functions let you reshape the harmonic context: |
241 | 192 | ;; |
242 | 193 | ;; **`scale`** — change the scale: |
243 | | -;; |
244 | | -;;```clj |
245 | | -;; (play (scale :melodic-minor) (rup 8 d1)) |
246 | | -;;``` |
247 | | -;; |
| 194 | + |
| 195 | +(nclay/editor "(play (scale :melodic-minor) (rup 8 d1))") |
| 196 | + |
248 | 197 | ;; **`structure`** — change the chord structure: |
249 | | -;; |
250 | | -;;```clj |
251 | | -;; (play (structure :tetrad) (tup s0 s1 s2 s3)) |
252 | | -;;``` |
253 | | -;; |
| 198 | + |
| 199 | +(nclay/editor "(play (structure :tetrad) (tup s0 s1 s2 s3))") |
| 200 | + |
254 | 201 | ;; **`root`** — change the tonal center: |
255 | | -;; |
256 | | -;;```clj |
257 | | -;; (play (root :Eb) (tup s0 s1 s2)) |
258 | | -;;``` |
259 | | -;; |
| 202 | + |
| 203 | +(nclay/editor "(play (root :Eb) (tup s0 s1 s2))") |
| 204 | + |
260 | 205 | ;; **`degree`** — move to a scale degree (mode change): |
261 | | -;; |
262 | | -;;```clj |
263 | | -;; (play (lin I IV V I) (each (tup s0 s1 s2))) |
264 | | -;;``` |
| 206 | + |
| 207 | +(nclay/editor "(play (lin I IV V I) (each (tup s0 s1 s2)))") |
265 | 208 |
|
266 | 209 | ;; *A I-IV-V-I chord progression, each chord arpeggiated. |
267 | | -;; Dashed lines mark where the harmonic context changes — the same arpeggio pattern adapts to each degree.* |
| 210 | +;; The same arpeggio pattern adapts to each degree. |
| 211 | +;; Try changing `(tup s0 s1 s2)` to `(tup s0 s1 s2 s3)` after switching to `(structure :tetrad)`!* |
268 | 212 |
|
269 | | -^:kindly/hide-code |
270 | | -(pr/piano-roll (score (lin I IV V I) (each (tup s0 s1 s2)))) |
| 213 | +(nclay/editor "(score (lin I IV V I) (each (tup s0 s1 s2)))") |
271 | 214 |
|
272 | 215 | ;; Here `I`, `IV`, `V` are degree changes — they shift the harmonic context |
273 | 216 | ;; so that structural steps target the chord tones of each degree. |
|
278 | 221 | ;; The real power emerges when you mix layers. |
279 | 222 | ;; Since each event carries the full context, you can navigate between layers freely: |
280 | 223 | ;; |
281 | | -;;```clj |
282 | | -;; ;; Structural arpeggio with diatonic passing tones |
283 | | -;; (play dur:2 |
284 | | -;; (tup s0 s1 s2 s3) |
285 | | -;; (each (tup d1 d1- d0))) |
286 | | -;;``` |
287 | 224 |
|
288 | | -;; *Mixing layers: structural chord tones (medium blue) decorated with diatonic neighbor notes (light blue). |
| 225 | + |
| 226 | +;; *Mixing layers: structural chord tones decorated with diatonic neighbor notes. |
289 | 227 | ;; The structural steps define the skeleton; the diatonic steps fill in the passing tones.* |
290 | 228 |
|
291 | | -^:kindly/hide-code |
292 | | -(pr/piano-roll (score dur:2 (tup s0 s1 s2 s3) (each (tup d1 d1- d0)))) |
| 229 | +(nclay/editor "(score dur:2 (tup s0 s1 s2 s3) (each (tup d1 d1- d0)))") |
293 | 230 |
|
294 | 231 | ;; This creates a chord arpeggio (`s0 s1 s2 s3`) then decorates each chord tone |
295 | 232 | ;; with upper and lower neighbor scale notes (`d1 d1- d0`). |
|
299 | 236 | ;; |
300 | 237 | ;; An arpeggiated chord progression in harmonic minor: |
301 | 238 | ;; |
302 | | -;;```clj |
303 | | -;; (play (scale :harmonic-minor) |
304 | | -;; (lin I IV VII I) |
305 | | -;; (each (tup s0 s1 s2))) |
306 | | -;;``` |
| 239 | + |
307 | 240 |
|
308 | 241 | ;; *Harmonic minor progression: I-IV-VII-I with arpeggiated chords. |
309 | 242 | ;; Notice the characteristic G♯ (raised 7th degree) appearing in the VII chord.* |
310 | 243 |
|
311 | | -^:kindly/hide-code |
312 | | -(pr/piano-roll (score (scale :harmonic-minor) (lin I IV VII I) (each (tup s0 s1 s2)))) |
| 244 | +(nclay/editor "(score (scale :harmonic-minor) (lin I IV VII I) (each (tup s0 s1 s2)))") |
313 | 245 |
|
314 | 246 | ;; ## Why This Matters |
315 | 247 | ;; |
|
0 commit comments