(ns notation "Experimental notation" (:require [missionary.core :as m] [clojure.set :refer [union]])) (comment ;; Parallel groups ;; Notes 1, 2, and 3 simultaneously ;; = should remind you of amb= ;; implicit duration of 1 [= 1 2 3] ;; Compiles to? ;; Same as above, but with duration 3 ([= 1 2 3] 3) ;; Notes 1, 2, and 3 all with different durations [= (1 2) (2 3) (3 4)] ;; Inner values override outer values ;; In this chord, 1 would have a duration of 3 while 2 and 3 would have a duration of 2 ([= (1 3) 2 3] 2) ;; Notes 1, 2, and 3 all with different durations and velocities [= (1 2 100) (2 3 110) (3 4 123)] ;; Sequential groups ;; Note 1, then note 2, then note 3 [1 2 3] ;; Note 1 duration 1, then note 2 duration 2, then note 3 duration 1 [(1 1) (2 2) (3 1)] ;; Three chords played sequentially [[= 1 2 3] [= 1 2 3] [= 1 2 3]] ;; Note 1, followed by a rest, followed by note 3 [1 (r) 3] ;; Unlike notes, rests are at most 2-tuples ;; (Think about it: Rests never have a note value) ;; Assign the note sequence 1 2 3 to the name loop1 ;; The first argument is always the name; the last argument is always either ;; a sequential or parallel group (=loop1 [1 2 3]) ;; Use loop1 [1 (loop1) 2 3] ;; Middle arguments are variable names (=loop2 dur ([1 2 3] dur)) ;; TODO: ;; - Note literals turn into numbers ;; - Represent keyboard as byte array of shorts ;; - play a note increments, stop a note decrements ;; - Multiple instruments ;; - Mapping inputs to vars ;; - Inputs get declared at the top of a track ;; - Devices get mapped to declared inputs ;; - Notion of scenes that change mapping of inputs to vars ;; - Loops ) (def ctr (atom nil)) (def pulse (m/signal (m/watch ctr))) (defn note [value duration] (m/eduction (take-while #(not= ::done %)) (m/ap (m/amb #{value} (let [c (atom 0)] (m/?< pulse) (swap! c inc) (if (> @c duration) (m/amb #{} ::done) (m/amb))))))) (defmacro chord [& notes] (let [atoms (repeatedly (count notes) gensym) let-bindings (vec (mapcat (fn [atom] [atom `(atom #{})]) atoms)) reset-forms (map (fn [atom note] `(m/amb (reset! ~atom (m/?< ~note)))) atoms notes) union-form (cons `union (map (fn [atom] `(deref ~atom)) atoms))] `(m/ap (m/amb (let ~let-bindings (m/amb= ~@reset-forms) ~union-form))))) (defmacro line [& notes] `(m/ap (m/amb ~@(map (fn [note] `(m/?< ~note)) notes)))) (def melody (line (chord (note 1 2) (note 3 2) (note 5 2)) (chord (note 2 2) (note 4 2) (note 6 2)))) (def cancel ((m/reduce prn #{} melody) {} {})) (cancel) (reset! ctr nil)