   (doseq [handler (vals (get-in @control-mappings [(:name (:device msg)) (:channel msg) (:note msg)]))]
     (handler msg)))
 
-(defonce ^:private ^{:doc "The channel used to hand MIDI events from
+(defonce ^:private ^{:doc "The queue used to hand MIDI events from
   the extension thread to our world, since there seem to be
   classloader compatibility issues, and the Java3d classes are not
   available to threads that are directly dispatching MIDI events."}
-  channel
+  midi-queue
+  (LinkedBlockingQueue.))
+
+(defonce ^:private ^{:doc "The thread used to hand MIDI events from
+  the extension thread to our world, since there seem to be
+  classloader compatibility issues, and the Java3d classes are not
+  available to threads that are directly dispatching MIDI events."}
+  midi-transfer-thread
   (atom nil))
 
 (defn- delegated-message-handler

            (:timing-clock :start :stop) (clock-message-handler msg)
            :control-change (cc-message-handler msg)
            nil)
          (catch Throwable t
            (error t "Problem running MIDI event handler")))
        (recur (<! @channel))))
    ;; The channel has been closed.
    (reset! channel nil)))

(defn- incoming-message-handler
  "Attached to all midi input devices we manage. Puts the message on a

            (not (mmj-descriptions (:name device))))))
    identity))

(defn- start-handoff-thread
  "Creates the thread used to deliver MIDI events when needed."
  [old-thread]
  (or old-thread
      (doto (Thread. delegated-message-handler)
        (.setDaemon true)
        (.start))))

;; TODO: This and open-outputs will have to change once hot-plugging works.
(defn open-inputs-if-needed!
  "Make sure the MIDI input ports are open and ready to distribute events.
  Returns the opened inputs."
  []
  (swap! midi-transfer-thread start-handoff-thread)
  (let [port-filter (create-midi-port-filter)
        result (swap! midi-inputs #(if (empty? %)
                                     (map connect-midi-in
