summaryrefslogtreecommitdiff
path: root/src/unheard/clock.clj
diff options
context:
space:
mode:
Diffstat (limited to 'src/unheard/clock.clj')
-rw-r--r--src/unheard/clock.clj104
1 files changed, 50 insertions, 54 deletions
diff --git a/src/unheard/clock.clj b/src/unheard/clock.clj
index d38b52a..b711ebe 100644
--- a/src/unheard/clock.clj
+++ b/src/unheard/clock.clj
@@ -2,30 +2,23 @@
(:require [missionary.core :as m]))
;; TODO: Put in missionary util
-(defn poll [init task]
- (m/ap (m/? (m/?> (m/seed (concat [(m/sp init)]
- (repeat task)))))))
+(defn poll
+ [init task]
+ (m/ap (m/? (m/?> (m/seed (concat [(m/sp init)] (repeat task)))))))
;; TODO: Put in missionary util
-(defn feedback [f init & args]
- (m/ap
- (m/amb
- init
- (let [rdv (m/rdv)
- x (m/?> (apply f (poll init rdv) args))]
- (m/amb
- (do
- (m/? (rdv x))
- (m/amb))
- x)))))
+(defn feedback
+ [f init & args]
+ (m/ap (m/amb init
+ (let [rdv (m/rdv)
+ x (m/?> (apply f (poll init rdv) args))]
+ (m/amb (do (m/? (rdv x)) (m/amb)) x)))))
;; Return value
;; TODO: Make part of public API
(defonce mono-clock-freq (atom 120)) ;; hz
-(defonce >mono-clock-freq
- (m/signal
- (m/watch mono-clock-freq)))
+(defonce >mono-clock-freq (m/signal (m/watch mono-clock-freq)))
(def >mono-clock
"This is the base monotonic clock used for driving playback.
@@ -38,57 +31,60 @@
frequency."
;; TODO: Change this behavior. I don't want to wait for
;; the current tick to complete during a frequency change..
- (m/signal
- (m/relieve
- (feedback (fn [v]
- (m/ap
- (let [[t freq] (m/?< (m/latest vector v >mono-clock-freq))]
- (m/? (m/compel (m/sleep (/ 1000 freq))))
- (System/nanoTime))))
- (System/nanoTime)))))
+ (m/signal (m/relieve
+ (feedback (fn [v]
+ (m/ap (let [[t freq]
+ (m/?<
+ (m/latest vector v >mono-clock-freq))]
+ (m/? (m/compel (m/sleep (/ 1000 freq))))
+ (System/nanoTime))))
+ (System/nanoTime)))))
(comment
- (def cancel
- ((m/reduce prn nil >mono-clock) prn prn))
-
+ (def cancel ((m/reduce prn nil >mono-clock) prn prn))
(reset! mono-clock-freq 50)
(reset! mono-clock-freq 10)
(reset! mono-clock-freq 1)
(cancel))
(defonce bpm (atom 120))
-(defonce >bpm
- (m/signal
- (m/watch bpm)))
+(defonce >bpm (m/signal (m/watch bpm)))
(def >beat-clock
"Counts beats at `bpm`. Guaranteed not to lose or gain time."
(m/signal
- (m/relieve
- (let [init-beat 0]
- (m/reductions {} init-beat
- (m/ap
- (let [state (object-array 2)
- last-beat-time-idx 0
- last-beat-number-idx 1
- _ (aset state last-beat-time-idx (System/nanoTime))
- _ (aset state last-beat-number-idx init-beat)
- [t bpm] (m/?< (m/latest vector >mono-clock >bpm))
- last-beat-time (aget state last-beat-time-idx)
- next-beat-time (+ last-beat-time (* 1000000000 (/ 60 bpm)))
- last-beat-number (aget state last-beat-number-idx)
- t-since-beat-start (- t last-beat-time)
- pct-through-current-beat (/ t-since-beat-start (- next-beat-time last-beat-time))]
- (when (<= next-beat-time t)
- ;; TODO confirm this won't lose or gain time
- (aset state last-beat-time-idx next-beat-time)
- (aset state last-beat-number-idx (inc last-beat-number)))
- (+ last-beat-number pct-through-current-beat))))))))
+ (m/relieve
+ (let [init-beat 0]
+ (m/reductions
+ {}
+ init-beat
+ (m/ap
+ (let [state (object-array 2)
+ last-beat-time-idx 0
+ last-beat-number-idx 1
+ _ (aset state last-beat-time-idx (System/nanoTime))
+ _ (aset state last-beat-number-idx init-beat)
+ ;; BUG 8a32cde: BPM changes can cause non-monotonic
+ ;; behavior!
+ [t bpm] (m/?< (m/latest vector >mono-clock >bpm))]
+ (if (= bpm 0)
+ (m/amb)
+ (let [last-beat-time (aget state last-beat-time-idx)
+ next-beat-time (+ last-beat-time
+ (* 1000000000 (/ 60 bpm)))
+ last-beat-number (aget state last-beat-number-idx)
+ t-since-beat-start (- t last-beat-time)
+ pct-through-current-beat (/ t-since-beat-start
+ (- next-beat-time
+ last-beat-time))]
+ (when (<= next-beat-time t)
+ ;; TODO confirm this won't lose or gain time
+ (aset state last-beat-time-idx next-beat-time)
+ (aset state last-beat-number-idx (inc last-beat-number)))
+ (+ last-beat-number pct-through-current-beat))))))))))
(comment
- (def cancel
- ((m/reduce prn nil >beat-clock) prn prn))
-
+ (def cancel ((m/reduce prn nil >beat-clock) prn prn))
(reset! bpm 60)
(reset! bpm 120)
(reset! mono-clock-freq 120)