summaryrefslogtreecommitdiff
path: root/src/notation.clj
blob: 6e266cecba8feb578f31f5ccf44ed17a53b78ea8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
(ns 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
  )

;; TODO: Move elsewhere
(defn clock []
  (let [clock (atom 0)
        >clock (m/signal (m/watch clock))]
    [>clock (fn [v] (reset! clock v))]
    ))

(defn note [clock start duration value]
  (m/cp
   (if (m/?< (m/latest #(<= start % (dec (+ start duration))) clock))
     #{value}
     #{})))

(defn poly [& notes]
  (m/ap
    (apply union (m/?< (apply m/latest vector notes)))))

;; TODO: Group could actually wrap note, rather than using explicitly
;; WIll introduce a lot of GC churn, though
(defn group
  [clock start end content]
  (m/cp
   (let [content (m/signal content)]
     (if (m/?< (m/latest #(<= start % end) clock))
       (m/?< content)
       (m/amb #{})))))

#_(reset! clock 0)
#_(swap! clock inc)
#_(swap! clock dec)