summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--DEVLOG.md256
1 files changed, 256 insertions, 0 deletions
diff --git a/DEVLOG.md b/DEVLOG.md
index e56a920..25e7557 100644
--- a/DEVLOG.md
+++ b/DEVLOG.md
@@ -931,3 +931,259 @@ TODO upcoming:
- Inspired by strudel, define a language of musical modifiers
- Read notes of Nov. 28
+
+## December 3rd, 2025
+
+Unheard made sound for the first time yesterday! Very exciting.
+
+Some ideas from yesterday that I want to carry forward:
+
+1. Create a function that takes channel, etc., and returns a note flow.
+2. Play with using dynamic variables? For... idk.
+
+WHOA! Strudel keeps blowing my mind.
+I just discovered that anything can take a pattern, e.g. scales: https://strudel.cc/workshop/first-notes/#scales
+WTF, so cool! This has broken my brain.
+
+Trying to unpack this.
+I guess this is how I'd think about this: each pattern describes
+values. (This is the hole in the mini-notation pattern.) Each pattern
+is associated with an _attribute type_: One might be pitch,
+another instrument. The attribute patterns are all merged together
+as if by parallel composition. In my DSL, I would think about the
+value of each attribute being a tuple of e.g. [:pitch :e4] or
+[:instrument :piano].
+
+What does this mean for me?
+
+One takeaway (I think) is that a composition has multiple instrument-like
+things, and that each instrument-like thing is a union of these
+various patterened attributes. At the top level, all of these unions play
+together via parallel composition.
+
+What would it mean for me to completely invert my playback model,
+where each instrument is a flow rather than each note? I don't think
+this is quite right, though.
+
+In particular, I'm not sure how to merge this idea into my functional composition
+model.
+
+But there is definitely something cool here: the attributes of
+an instrument are the timewise union of all patterns.
+
+Like, what if a rhythmic flow took as its input flows of other associated
+properties. (But what is a rhythmic flow? That idea doesn't exist yet.)
+
+Need to keep thinking about this
+
+I'm looking at this version of compiled-tetris:
+
+```clojure
+(defn n [>ch >val >vel]
+ (let [[ch val vel] (m/?< (m/latest vector >ch >val >vel))]
+ {ch {val :on}})))
+
+(def compiled-tetris
+ (p
+ (let [n (fn [val] (n (m/ap 0) (m/ap val) (m/ap 100)))]
+ (f (l (n e5) (l (n b4) (n c5)) (n d5) (l (n c5) (n b4)))
+ (l (n a4) (l (n a4) (n c5)) (n e5) (l (n d5) (n c5)))
+ (l (n b4) (l r (n c5)) (n d5) (n e5))
+ (l (n c5) (n a4) (n a4) r)
+ (l (l r (n d5)) (l r (n f5)) (n a5) (l (n g5) (n f5)))
+ (l (n e5) (l r (n c5)) (n e5) (l (n d5) (n c5)))
+ (l (n b4) (l (n b4) (n c5)) (n d5) (n e5))
+ (l (n c5) (n a4) (n a4) r)))
+
+ (let [n (fn [val] (n (m/ap 1) (m/ap val) (m/ap 100)))]
+ (f (rate 4 (l (n e2) (n e3)))
+ (rate 4 (l (n a2) (n a3)))
+ (l (rate 2 (l (n gs2) (n gs3))) (rate 2 (l (n e2) (n e3))))
+ (l (n a2) (n a3) (n a2) (n a3) (n a2) (n a3) (n b1) (n c2))
+ (rate 4 (l (n d2) (n d3)))
+ (rate 4 (l (n c2) (n c3)))
+ (l (rate 2 (l (n b1) (n b2))) (rate 2 (l (n e2) (n e3))))
+ (rate 4 (l (n a1) (n a2)))))))
+```
+
+Notice how it's possible to arbitrarily parameterize the various qualities of `note`.
+Cool. But how can I make it possible to _also_ parameterize an attribute of the notes
+using strudel syntax? For example, I also want octave to impact this instruments in
+this phrase:
+
+```clojure
+(defn n [>ch >val >vel]
+ (let [[ch val vel] (m/?< (m/latest vector >ch >val >vel))]
+ {ch {val :on}})))
+
+(def compiled-tetris
+ (p
+ ;; melody block
+ (let [n (fn [val] (n (m/ap 0) (m/ap val) (m/ap 100)))]
+ (f (l (n e5) (l (n b4) (n c5)) (n d5) (l (n c5) (n b4)))
+ (l (n a4) (l (n a4) (n c5)) (n e5) (l (n d5) (n c5)))
+ (l (n b4) (l r (n c5)) (n d5) (n e5))
+ (l (n c5) (n a4) (n a4) r)
+ (l (l r (n d5)) (l r (n f5)) (n a5) (l (n g5) (n f5)))
+ (l (n e5) (l r (n c5)) (n e5) (l (n d5) (n c5)))
+ (l (n b4) (l (n b4) (n c5)) (n d5) (n e5))
+ (l (n c5) (n a4) (n a4) r)))
+
+ ;; bass block
+ (let [n (fn [val] (n (m/ap 1) (m/ap val) (m/ap 100)))]
+ (f (rate 4 (l (n e2) (n e3)))
+ (rate 4 (l (n a2) (n a3)))
+ (l (rate 2 (l (n gs2) (n gs3))) (rate 2 (l (n e2) (n e3))))
+ (l (n a2) (n a3) (n a2) (n a3) (n a2) (n a3) (n b1) (n c2))
+ (rate 4 (l (n d2) (n d3)))
+ (rate 4 (l (n c2) (n c3)))
+ (l (rate 2 (l (n b1) (n b2))) (rate 2 (l (n e2) (n e3))))
+ (rate 4 (l (n a1) (n a2)))))
+
+ ;; octave block
+ (l (octave 0) (octave 1))))
+
+```
+
+Here, octave and note both are returning flows. How do we define
+the merge semantics of octave block? Should it merge with the bass
+block? The melody block? The melody block has many entities in it.
+
+This feels on the one hand like a lexical problem. _Maybe_ the
+solution should be limited to the speific semantics of the strudel
+mini-notation format. But I think probably not?
+
+Oh, this is very helpful:
+https://strudel.cc/learn/effects/#signal-chain
+
+Strudel has made this very concrete. Each pattern gets a sound,
+asdr, some filters, effects, and delay/reverb.
+
+What if I don't want that?
+
+Let's jump to a very different idea: name trees
+The idea is that nested phrases introduce nested names
+
+```clojure
+(def a (phrase ...))
+
+{`[a] ...
+ `[b a] ...}
+
+(def b
+ (phrase (a)))
+```
+
+Hm, what if `a` is used twice in `b`?
+Oh, I wrote about this on Nov. 28. The answer has to do with providing the name at invocation time.
+
+```clojure
+(def b (phrase ...))
+
+(def a
+ (phrase (b :b)))
+
+(a :a)
+
+{[:a :b] ...
+ [:a] ...}
+```
+
+I bring this up now because it might relate to this merging question.
+
+```clojure
+(defn n [>ch >val >vel]
+ (let [[ch val vel] (m/?< (m/latest vector >ch >val >vel))]
+ {ch {val :on}})))
+
+(def a
+ (phrase
+ (let [n (fn [val] (n (m/ap 0) (m/ap val) (m/ap 100)))]
+ (f (l (n e5) (l (n b4) (n c5)) (n d5) (l (n c5) (n b4)))
+ (l (n a4) (l (n a4) (n c5)) (n e5) (l (n d5) (n c5)))
+ (l (n b4) (l r (n c5)) (n d5) (n e5))
+ (l (n c5) (n a4) (n a4) r)
+ (l (l r (n d5)) (l r (n f5)) (n a5) (l (n g5) (n f5)))
+ (l (n e5) (l r (n c5)) (n e5) (l (n d5) (n c5)))
+ (l (n b4) (l (n b4) (n c5)) (n d5) (n e5))
+ (l (n c5) (n a4) (n a4) r)))))
+
+(def b
+ (phrase
+ (let [n (fn [val] (n (m/ap 1) (m/ap val) (m/ap 100)))]
+ (f (rate 4 (l (n e2) (n e3)))
+ (rate 4 (l (n a2) (n a3)))
+ (l (rate 2 (l (n gs2) (n gs3))) (rate 2 (l (n e2) (n e3))))
+ (l (n a2) (n a3) (n a2) (n a3) (n a2) (n a3) (n b1) (n c2))
+ (rate 4 (l (n d2) (n d3)))
+ (rate 4 (l (n c2) (n c3)))
+ (l (rate 2 (l (n b1) (n b2))) (rate 2 (l (n e2) (n e3))))
+ (rate 4 (l (n a1) (n a2)))))))
+
+(def octave
+ (phrase
+ (l (octave 0) (octave 1))))
+
+(def tetris
+ (phrase
+ (p
+ ;; melody block
+ (a :melody)
+ (octave :melody)
+
+ ;; bass block
+ (b :bass)
+ (octave :bass)
+ )))
+
+(tetris :t)
+
+;; end up with
+[[0 1 [[:t :melody] melody-note-flow-1]]
+ [0 1 [[:t :melody] melody-note-flow-2]]
+ [0 1 [[:t :melody] melody-octave-flow]]]
+
+;; melody-note-flow-1 might emit
+{:kind :note
+ :note 60}
+
+;; melody-octave-flow might emit
+{:kind :octave-transform
+ :dx 10}
+
+;; Maybe elements with the same name
+;; are paired with elements of a different kind?
+;; e.g.
+
+[{:kind :note :note 60} {:kind :octave-transform :dx 10} ;; note-1
+ {:kind :note :note 70} {:kind :octave-transform :dx 10} ;; note-2
+ ]
+
+;; Kind of interesting. But where does this merge order come from? Is it global?
+;; Maybe local to a phrase?
+;; And crucially: how do we know how to interpret any of this when it comes out the pipe at the end?
+
+;; One answer to the merge question is: merging must be order-independent. Though, I don't know how
+;; to prevent merging issues caused by duplicate invocations of the same kind.
+
+ ;; TODO:
+ ;; What about something with two notes, one octave transform, two cc params? Can these multiply?
+ ;; [note-1 octave cc-1]
+ ;; [note-1 octave cc-2]
+ ;; [note-2 octave cc-1]
+ ;; [note-2 octave cc-2]
+ ;; This doesn't really work, does it! Suddenly each note is appearing twice.
+
+;; This is a contrived example, though. Is there a better one?
+```
+
+---
+
+Oh, but here's another cool observation. The data structure that feeds
+into `timeline` - that is, a list of [start end value] tuples - is
+more or less ready to lock in. The only question in my mind is how
+`v` needs to be defined.
+
+This is a key observation to document:
+I can support arbitrary musical syntaxes, they just need to
+compile down to the [start end value] representation.