summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJake Zerrer <him@jakezerrer.com>2025-12-10 15:44:52 -0500
committerJake Zerrer <him@jakezerrer.com>2025-12-10 15:45:04 -0500
commit58a0f0ccbf76fda911d5ce1110d314d438194a4a (patch)
treec0b72974808cfa532b2b5c3bed96c79fc0d4bb45
parent02dce7d08f92b1d255e3afca33034f481d1371a7 (diff)
Devlog: Consider tags
-rw-r--r--DEVLOG.md378
1 files changed, 378 insertions, 0 deletions
diff --git a/DEVLOG.md b/DEVLOG.md
index 9d9f158..089270a 100644
--- a/DEVLOG.md
+++ b/DEVLOG.md
@@ -1194,3 +1194,381 @@ A few more observations:
1. parallel composition is closely related to instruments. (Instruments are trees playing in parallel.)
2. It might make sense to pass instrument specifiers down as arguments
+
+## December 10th, 2025
+
+What about tags?
+
+Consider:
+
+
+```clojure
+;; tetris
+(p
+ (tag :melody
+ (f (l _ (l _ _) _ (l _ _))
+ (l _ (l _ _) _ (l _ _))
+ (l _ (l r _) _ _)
+ (l _ _ _ r)
+ (l (l r _) (l r _) _ (l _ _))
+ (l _ (l r _) _ (l _ _))
+ (l _ (l _ _) _ _)
+ (l _ _ _ r)))
+ (tag :bass
+ (f (rate 4 (l _ _))
+ (rate 4 (l _ _))
+ (l (rate 2 (l _ _)) (rate 2 (l _ _)))
+ (l _ _ _ _ _ _ _ _)
+ (rate 4 (l _ _))
+ (rate 4 (l _ _))
+ (l (rate 2 (l _ _)) (rate 2 (l _ _)))
+ (rate 4 (l _ _)))))
+```
+
+When compiled, each interval of the above (except the outer p) would be tagged with either :melody or :bass.
+
+
+```clojure
+(tag :theme
+ (p
+ (tag :melody
+ (f (l _ (l _ _) _ (l _ _))
+ (l _ (l _ _) _ (l _ _))
+ (l _ (l r _) _ _)
+ (l _ _ _ r)
+ (l (l r _) (l r _) _ (l _ _))
+ (l _ (l r _) _ (l _ _))
+ (l _ (l _ _) _ _)
+ (l _ _ _ r)))
+ (tag :bass
+ (f (rate 4 (l _ _))
+ (rate 4 (l _ _))
+ (l (rate 2 (l _ _)) (rate 2 (l _ _)))
+ (l _ _ _ _ _ _ _ _)
+ (rate 4 (l _ _))
+ (rate 4 (l _ _))
+ (l (rate 2 (l _ _)) (rate 2 (l _ _)))
+ (rate 4 (l _ _))))))
+```
+
+Here, every interval would also be tagged with :theme.
+
+What might we do with tags?
+We could turn the output into tuples like this:
+
+
+```
+[0 1 #{:theme :melody} :c4]
+[0 1 #{:theme :bass} :d4]
+```
+
+That information could aid in interpretation at playback time?
+
+
+Note that every slot (that is, every `_`) is a place where a tag
+set can be placed.
+
+Without a tagging context, the tag is just the empty set.
+
+That is:
+
+```clojure
+(f (l _ (l _ _) _ (l _ _))
+ (l _ (l _ _) _ (l _ _))
+ (l _ (l r _) _ _)
+ (l _ _ _ r)
+ (l (l r _) (l r _) _ (l _ _))
+ (l _ (l r _) _ (l _ _))
+ (l _ (l _ _) _ _)
+ (l _ _ _ r))
+```
+
+Would result in each slot (that is, each interval) having a
+tag set of `#{}`.
+
+Let's see how tagging would accomplish a few goals.
+
+1. Play midi notes, alternating between two instrument types.
+2. Play two voices, swapping positions.
+3. Write a phrase, and then output it both in OSC and MIDI
+4. Write a phrase based on intervals, and then map those intervals
+ to a scale.
+5. Same as 4, but move the scale based on a knob. During t1, the
+ knob swaps between ionian and mixolydian. During t2, the knob
+ swaps between dorian and locrian.
+6. Two different instruments playing the same phrase, one shifted up
+ an octave
+
+### No. 1:
+
+```clojure
+
+(import [midi-notes :as m])
+
+(def melody
+ (tag
+ :m/notes
+ (l :m/c1 :m/c2 :m/c3 :m/c2)))
+
+(def inst-changes
+ (tag
+ :inst
+ (l :synth-1 :synth-2)))
+
+(def song
+ (tag
+ :song
+ (p melody inst-changes)))
+
+[0 1 #{:song :m/notes} :m/c1]
+[0 2 #{:song :inst} :synth-1]
+[1 2 #{:song :m/notes} :m/c2]
+[2 3 #{:song :m/notes} :m/c3]
+[2 4 #{:song :inst} :synth-2]
+[3 4 #{:song :m/notes} :m/c2]
+```
+
+### No. 2:
+
+```clojure
+(import [midi-notes :as m])
+
+(def voice-1
+ (tag
+ :m/notes
+ (l :m/a1 :m/a2)))
+
+(def voice-2
+ (tag
+ :m/notes
+ (l :m/a2 :m/a1)))
+
+(def song
+ (p
+ (tag :v1 voice-1)
+ (tag :v2 voice-2)))
+
+[0 1 #{:v1 :m/notes} :m/a1]
+[0 1 #{:v2 :m/notes} :m/a2]
+[1 2 #{:v1 :m/notes} :m/a2]
+[1 2 #{:v2 :m/notes} :m/a1]
+
+```
+
+### No. 3
+
+```clojure
+(def melody
+ (tag
+ :an/notes
+ (l :an/a1 :an/a2 :an/a3)))
+
+[0 1 #{:an/notes} :an/a1]
+[1 2 #{:an/notes} :an/a2]
+[2 3 #{:an/notes} :an/a3]
+```
+
+One thing interesting about the above:
+:an/a1 could include its own tag. that is,
+it could be replaced with:
+
+```clojure
+(def a1
+ (tag
+ :an/notes
+ (l :an/a1)))
+```
+
+Then the whole thing could be rewritten:
+
+```clojure
+(def a1
+ (tag
+ :an/notes
+ (l :an/a1)))
+
+(def a2
+ (tag
+ :an/notes
+ (l :an/a2)))
+
+(def a3
+ (tag
+ :an/notes
+ (l :an/a3)))
+
+(def melody
+ (l a1 a2 a3))
+
+[0 1 #{:an/notes} :an/a1]
+[1 2 #{:an/notes} :an/a2]
+[2 3 #{:an/notes} :an/a3]
+```
+
+Note that the result is the same.
+
+### No. 4
+
+```clojure
+(def d1
+ (tag
+ :scale/degree
+ (l :sd/d1)))
+
+(def d2
+ (tag
+ :scale/degree
+ (l :sd/d2)))
+
+(def d3
+ (tag
+ :scale/degree
+ (l :sd/d3)))
+
+(def ionian
+ (tag
+ :mode
+ (l :ionian)))
+
+(def mixolydian
+ (tag
+ :mode
+ (l :mixolydian)))
+
+(def melody
+ (tag :tune
+ (p
+ (l d1 d2 d3)
+ (l ionian mixolydian))))
+
+[0 1 #{:tune :scale/degree} :sd/d1]
+[0 1.5 #{:tune :mode} :ionian]
+[1 2 #{:tune :scale/degree} :sd/d2]
+[1.5 3 #{:tune :mode} :mixolydian]
+[2 3 #{:tune :scale/degree} :sd/d3]
+```
+
+I think this one makes sense.
+
+### No. 5
+
+```clojure
+(def d1
+ (tag
+ :scale/degree
+ (l :sd/d1)))
+
+(def d2
+ (tag
+ :scale/degree
+ (l :sd/d2)))
+
+(def d3
+ (tag
+ :scale/degree
+ (l :sd/d3)))
+
+(def ionian
+ (tag
+ :mode
+ (l :ionian)))
+
+(def mixolydian
+ (tag
+ :mode
+ (l :mixolydian)))
+
+(def dorian ...)
+(def locrian ...)
+
+(def melody
+ (tag :tune
+ (p
+ (l d1 d2 d3)
+ (l
+ ;; x: flow combinator
+ (x
+ (m/ap
+ (if (< 0 (rescale 0 1 (m/?< (cv :k1-knob-1))) 0.5)
+ ionian
+ mixolydian)))
+ (x
+ (m/ap
+ (if (< 0 (rescale 0 1 (m/?< (cv :k1-knob-1))) 0.5)
+ dorian
+ locrian)))))))
+
+[0 1 #{:tune :scale/degree} :sd/d1]
+[0 1.5 #{:tune :mode} first-flow]
+[1 2 #{:tune :scale/degree} :sd/d2]
+[1.5 3 #{:tune :mode} second-flow]
+[2 3 #{:tune :scale/degree} :sd/d3]
+```
+
+Hm, we're back to flows in the value position.
+This makes me think that we should _always have a flow in value position_.
+
+```clojure
+(def d1
+ (tag
+ :scale/degree
+ (l (m/ap :sd/d1))))
+
+(def ionian
+ (tag
+ :mode
+ (l (m/ap :ionian))))
+```
+
+
+## No. 6
+
+This is a dumb way to accomplish no.6:
+
+```clojue
+(def c3
+ (tag
+ :an/notes
+ (l :an/c3)))
+
+(def d3
+ (tag
+ :an/notes
+ (l :an/d3)))
+
+(def e3
+ (tag
+ :an/notes
+ (l :an/e3)))
+
+(def f3
+ (tag
+ :an/notes
+ (l :an/f3)))
+
+(def riff
+ (l c3 d3 e3 f3))
+
+(def song
+ (p
+ (tag :inst-1
+ riff)
+ (tag :inst-3
+ (paste riff c4 d4 e4 f4))))
+```
+
+I don't love this one.
+
+### No. 7
+
+---
+
+Next, need to think about interpretation.
+
+Should tags be a set? That loses hierarchical information.
+Is that bad?
+
+Oh, what about temporally relative structures, such as +1 tone?
+
+And counterpoint-like relations, like in no. 6?
+