   "Add a function to be called whenever any MIDI message is received.
   The function will be called with the message, and must return
   quickly, so as to not block delivery to other recipients."
-  [handler]
-  (swap! global-handlers conj handler))
+  [f]
+  {:pre [(fn? f)]}
+  (swap! global-handlers conj f))
 
 (defn remove-global-handler!
   "Remove a function that was being called whenever any MIDI message is
   received."
-  [handler]
-  (swap! global-handlers disj handler))
+  [f]
+  (swap! global-handlers disj f))
+
+(defonce ^{:private true
+           :doc "Functions to be called when a particular device has
+           disappeared from the MIDI environment."}
+  disconnect-handlers (atom #{}))
 
 (defn- clock-message-handler
   "Invoked whenever any midi input device being managed receives a
   clock message. Checks whether there are any metronomes being synced
-  to that device, and if so, passes along the event. Also makes note
-  of the sender as a potential source of MIDI synchronization."
+  to that device, and if so, passes along the event."
   [msg]
-  (doseq [handler (vals (get @synced-metronomes (:device msg)))]
+  (doseq [handler (vals (get @synced-metronomes (:device (:device msg))))]
+    (handler msg)))
+
+(defn- cc-message-handler
+  "Invoked whenever any midi input device being managed receives a
+  control change message. Checks whether there are any handlers (such
+  as for launching cues or mapping show variables) attached to it, and
+  if so, calls them."
+  [msg]
+  (doseq [handler (vals (get-in @control-mappings [(:device (:device msg)) (:channel msg) (:note msg)]))]
     (handler msg)))
 
 (defn- note-message-handler

  aftertouch (polyphonic key pressure) message. Checks whether there
  are any handlers attached to it, and if so, calls them."
  [msg]
  (doseq [handler (vals (get-in @aftertouch-mappings [(:name (:device msg)) (:channel msg) (:note msg)]))]
    (handler msg)))

(defn- cc-message-handler
  "Invoked whenever any midi input device being managed receives a
  control change message. Checks whether there are any handlers (such
  as for launching cues or mapping show variables) attached to it, and
  if so, calls them."
  [msg]
  (doseq [handler (vals (get-in @control-mappings [(:name (:device msg)) (:channel msg) (:note msg)]))]
    (handler msg)))

(defonce

  ([]
   (sync-to-midi-clock nil))
  ([name-filter]
   (let [result (filter-devices (current-clock-sources) name-filter)]
     (case (count result)
       0 (throw (IllegalArgumentException. (str "No MIDI clock sources " (describe-name-filter name-filter)
