       mechanism ever offers that), and :status, which is a
       human-oriented summmary of the status."))))
 
+(defn interval-to-bpm
+  "Given an interval between MIDI clock pulses in milliseconds,
+  calculate the implied beats per minute value, to the nearest
+  hundredth of a beat."
+  [interval]
+  (/ (math/round (double (/ 6000000 (* interval 24)))) 100.0))
+
+(defn bpm-to-interval
+  "Given a BPM, calculate the interval between MIDI clock pulses in
+  milliseconds."
+  [bpm]
+  (/ 2500.0 bpm))
+
+(defn std-dev
+  "Calculate the standard deviation of a set of samples."
+  ([samples]
+   (let [n (count samples)
+         mean (/ (reduce + samples) n)]
+     (std-dev samples n mean)))
+  ([samples n mean]
+   (let [intermediate (map #(Math/pow (- %1 mean) 2) samples)]
+     (Math/sqrt (/ (reduce + intermediate) n))))) 
+
+(def abs-tolerance
+  "If we are going to adjust the BPM, the adjustment we are going to
+  make needs to represent at least this many milliseconds per MIDI
+  clock tick (to reduce jitter)."
+  0.005)
+
+(def dev-tolerance
+  "If we are going to adjust the BPM, the adjustment must be at least
+  this many times the standard deviation in observed clock pulse
+  timings, so we can avoid jitter due to unstable timing."
+  2.2)
+
 (defn- sync-handler
   "Called whenever a MIDI message is received for a synced metronome.
   If it is a clock pulse, update the ring buffer in which we are

           [java.util.regex Pattern]))

(def ^:private max-clock-intervals
  "How many MIDI clock pulses should be kept around for averaging?"
  30)

(def ^:private max-tempo-taps
  "How many tempo taps should be kept around for averaging?"
  4)

(def ^:private max-tempo-tap-interval
  "How long is too long for a tap to be considered part of
  establishing a tempo?"
  2000)

                             intervals (dec (count @timestamps))
                             mean (/ passed intervals)]
                         (alter means conj mean)
                         (when (>= (count @means) min-clock-intervals)
                           (let [num-means (count @means)
                                 mean-mean (/ (apply + @means) num-means)
                                 dev (std-dev @means num-means mean-mean)
                                 implied (bpm-to-interval (rhythm/metro-bpm metronome))
                                 adjustment (math/abs (- implied mean-mean))]
                             (when (> adjustment (max abs-tolerance (* dev-tolerance dev)))
                               (timbre/info "dev" dev "mean-mean" (float mean-mean) "adjustment" adjustment)
                               (rhythm/metro-bpm metronome (interval-to-bpm mean-mean))))))))
     (:start :stop) (do (ref-set timestamps (ring-buffer max-clock-intervals)) ; Clock is being reset
                        (ref-set means (ring-buffer max-clock-intervals)))
     :control-change (when (and (some? @traktor-info) (< (:note msg) 5))  ; Traktor beat phase update
