             (:timing-clock :start :stop) (clock-message-handler msg)
             :control-change (cc-message-handler msg)
             nil)
+
+          (catch InterruptedException t
+            (info "MIDI event handler thread interrupted, shutting down.")
+            (reset! running false)
+            (reset! midi-transfer-thread nil))
           (catch Throwable t
             (error t "Problem running MIDI event handler")))
-        (recur (<! @channel))))
-    ;; The channel has been closed.
-    (reset! channel nil)))
+        (when running (recur (.take midi-queue)))))))
 
 (defn- incoming-message-handler
   "Attached to all midi input devices we manage. Puts the message on a

  variable mappings, etc.) and dispatches to the appropriate specific
  handler."
  []
  (go
    (loop [msg (<! @channel)]
      (when msg
        ;; First call the global message handlers
        (doseq [handler @global-handlers]

(ns afterglow.midi
  "Handles MIDI communication, including syncing a show metronome to MIDI clock pulses."
  (:require [afterglow.rhythm :refer :all]
            [amalloy.ring-buffer :refer [ring-buffer]]
            [overtone.at-at :refer [now]]
            [overtone.midi :as midi]
            [taoensso.timbre :refer [info error]])
  (:import [java.util.regex Pattern]
           [java.util.concurrent LinkedBlockingQueue]))

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