Skip to content

Commit 67a0be4

Browse files
Merge pull request #329 from ClojureCivitas/noon-intro-article
Bump noon SHA — materialDark-aligned dark theme
2 parents 80e890d + 5998631 commit 67a0be4

File tree

2 files changed

+79
-147
lines changed

2 files changed

+79
-147
lines changed

deps.edn

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
com.hyperphor/multitool {:mvn/version "0.2.3"}
7676
datascript/datascript {:mvn/version "1.7.8"}
7777
io.github.pbaille/noon {:git/url "https://github.com/pbaille/noon.git"
78-
:git/sha "e63bb7c82671293c5610da6a02e3148a48ad71dd"}}
78+
:git/sha "97677727bff20243dc15d5cafb821d4af9019842"}}
7979

8080
:mvn/repos {"osgeo" {:url "https://repo.osgeo.org/repository/release/"}}
8181

src/music/noon_introduction.clj

Lines changed: 78 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
:tags [:music :harmony :functional-programming :midi]}}}
1010

1111
(ns music.noon-introduction
12-
(:require [noon.viz.piano-roll :as pr]
13-
[noon.eval :refer [score]]))
12+
(:require [noon.viz.clay :as nclay]))
1413

1514
;; ## What is Noon?
1615
;;
@@ -27,64 +26,45 @@
2726
;; A noon score is a set of event maps. That's it.
2827
;; Each event represents a MIDI note with properties like position, duration, velocity, and pitch:
2928
;;
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+
3632
;; 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)")
4235
;;
4336
;; Everything in noon is a **transformation** — a function from score to score.
4437
;; You build music by composing transformations:
4538
;;
46-
;;```clj
47-
;; (play dur2 (tup s0 s1 s2 s3))
48-
;;```
49-
;;
5039
;; This plays an ascending arpeggio at double duration.
5140
;; `dur2` doubles the duration, `tup` splits it into equal parts,
5241
;; and `s0` through `s3` are structural intervals (more on those soon).
5342

43+
(nclay/editor "(play dur2 (tup s0 s1 s2 s3))")
44+
5445
;; ## Building Blocks
5546
;;
5647
;; Three core functions handle most of the composition:
5748
;;
5849
;; **`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+
6453
;; **`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+
7057
;; **`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+
7661
;; 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+
8365
;; 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)])")
8868
;;
8969
;; A few more useful combinators:
9070
;;
@@ -93,14 +73,12 @@
9373
;; - `each` — apply a transformation to every event individually
9474
;; - `chans` — like `par` but on separate MIDI channels (for different instruments)
9575
;;
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))")
10482

10583
;; ## How Musicians Think About Pitch
10684
;;
@@ -143,13 +121,8 @@
143121
;;
144122
;; Each event in a noon score carries this full context under its `:pitch` key:
145123
;;
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)")
153126
;;
154127
;; The `:position` map holds an offset at each layer.
155128
;; This is where the note actually *is* within the harmonic context.
@@ -159,27 +132,23 @@
159132
;; Each layer has a corresponding **step** operation.
160133
;; A step moves you to the next position *on that layer*:
161134
;;
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+
168136

169137
;; *The four step types — chromatic, diatonic, structural, and tonic — each walking up from C4.
170138
;; 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))")
183152

184153
;; The chromatic run gives you all 7 semitones.
185154
;; The diatonic run gives you the major scale.
@@ -190,84 +159,58 @@
190159
;; Here's the key insight: **steps are always relative to the current harmonic context**.
191160
;; When you change the scale or structure, the same step operations produce different notes:
192161
;;
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+
203163

204164
;; *Same diatonic steps, three different scales. The code is identical — only the scale context changes.
205165
;; Notice how the intervals between notes shift while the structure remains.*
206166

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))")
216175

217176
;; The same goes for structural steps:
218177
;;
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+
226179

227180
;; *Triad vs tetrad: the same structural steps produce C-E-G-C with a triad structure,
228181
;; but C-E-G-B when the structure is set to tetrad — the seventh appears automatically.*
229182

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))")
237188

238189
;; ## Changing the Context
239190
;;
240191
;; Several functions let you reshape the harmonic context:
241192
;;
242193
;; **`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+
248197
;; **`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+
254201
;; **`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+
260205
;; **`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)))")
265208

266209
;; *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)`!*
268212

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)))")
271214

272215
;; Here `I`, `IV`, `V` are degree changes — they shift the harmonic context
273216
;; so that structural steps target the chord tones of each degree.
@@ -278,18 +221,12 @@
278221
;; The real power emerges when you mix layers.
279222
;; Since each event carries the full context, you can navigate between layers freely:
280223
;;
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-
;;```
287224

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.
289227
;; The structural steps define the skeleton; the diatonic steps fill in the passing tones.*
290228

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)))")
293230

294231
;; This creates a chord arpeggio (`s0 s1 s2 s3`) then decorates each chord tone
295232
;; with upper and lower neighbor scale notes (`d1 d1- d0`).
@@ -299,17 +236,12 @@
299236
;;
300237
;; An arpeggiated chord progression in harmonic minor:
301238
;;
302-
;;```clj
303-
;; (play (scale :harmonic-minor)
304-
;; (lin I IV VII I)
305-
;; (each (tup s0 s1 s2)))
306-
;;```
239+
307240

308241
;; *Harmonic minor progression: I-IV-VII-I with arpeggiated chords.
309242
;; Notice the characteristic G♯ (raised 7th degree) appearing in the VII chord.*
310243

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)))")
313245

314246
;; ## Why This Matters
315247
;;

0 commit comments

Comments
 (0)