Permalink
Browse files

Reworking events to support faster, synchronous handling, and to add …

…a handler key so we can re-register a handler on reload without having to use defonce. Now with tests :-)
  • Loading branch information...
1 parent 670817c commit cff0fd9b91e45d37a393599101ecda893824d941 @rosejn rosejn committed Oct 6, 2010
View
@@ -10,12 +10,16 @@ for f in "$DEPS"/*.jar; do
CLASSPATH=$CLASSPATH:$f
done
+for dir in "checkouts"; do
+ CLASSPATH=$CLASSPATH:$dir/src
+done
+
# Attempted Windows compatibility
if [[ "$OSTYPE" == 'cygwin' ]]; then
CLASSPATH=`cygpath -wp "$CLASSPATH"`
fi
-DEFINES=-Djava.library.path=native/linux/x86,sun.java2d.opengl=True
+DEFINES=-Djava.library.path=native/linux/x86,sun.java2d.opengl=False
# TODO: Tryout these args to use the parallel GC
#-XX:+UnlockExperimentalVMOptions -XX:+UseG1GC
View
@@ -5,4 +5,4 @@ pkill -9 java
SCRIPT_DIR=`dirname $0`
JAVA_ENV=`$SCRIPT_DIR/java-env`
-exec java $JAVA_ENV com.martiansoftware.nailgun.NGServer 127.0.0.1
+exec java $JAVA_ENV vimclojure.nailgun.NGServer 127.0.0.1
@@ -11,53 +11,77 @@
(def NUM-THREADS (cpu-count))
(defonce thread-pool (Executors/newFixedThreadPool NUM-THREADS))
(defonce event-handlers* (ref {}))
+(defonce sync-event-handlers* (ref {}))
-(defn on
+; * Need to add a handler key for events
+
+(log/level :debug)
+
+(defn- on-event*
+ [handler-ref* event-type key handler]
+ (log/debug "adding-handler for " event-type)
+ (dosync
+ (let [handlers (get @handler-ref* event-type {})]
+ (alter handler-ref* assoc event-type (assoc handlers key handler))
+ true)))
+
+(defn on-event
"Runs handler whenever events of type event-type are fired. The handler can
optionally except a single event argument, which is a map containing the
:event-type property and any other properties specified when it was fired.
- (on ::booted #(do-stuff))
- (on ::midi-note-down (fn [event] (funky-bass (:note event))))
+ (on-event ::booted #(do-stuff))
+ (on-event ::midi-note-down (fn [event] (funky-bass (:note event))))
Handlers can return :done to be removed from the handler list after execution."
- [event-type handler]
- (log/debug "adding-handler for " event-type)
- (dosync
- (let [handlers (get @event-handlers* event-type #{})]
- (alter event-handlers* assoc event-type (conj handlers handler))
- true)))
+ [event-type key handler]
+ (on-event* event-handlers* event-type key handler))
+
+(defn on-sync-event
+ "Synchronously runs handler whenever events of type event-type are fired. The handler can
+ optionally except a single event argument, which is a map containing the
+ :event-type property and any other properties specified when it was fired."
+ [event-type key handler]
+ (on-event* sync-event-handlers* event-type key handler))
(defn remove-handler
"Remove an event handler previously registered to handle events of event-type.
(defn my-foo-handler [event] (do-stuff (:val event)))
- (on ::foo my-foo-handler)
+ (on-event ::foo my-foo-handler)
(event ::foo :val 200) ; my-foo-handler gets called with {:event-type ::foo :val 200}
(remove-handler ::foo my-foo-handler)
(event ::foo :val 200) ; my-foo-handler no longer called
"
- [event-type handler]
+ [event-type key]
(dosync
- (let [handlers (get @event-handlers* event-type #{})]
- (alter event-handlers* assoc event-type (difference handlers #{handler})))))
+ (doseq [handler-ref* [event-handlers* sync-event-handlers*]]
+ (let [handlers (get @handler-ref* event-type {})]
+ (alter handler-ref* assoc event-type (dissoc handlers key))))))
(defn clear-handlers
"Remove all handlers for events of type event-type."
[event-type]
- (dosync (alter event-handlers* dissoc event-type))
+ (dosync
+ (alter event-handlers* dissoc event-type)
+ (alter sync-event-handlers* dissoc event-type))
nil)
(defn- handle-event
"Runs the event handlers for the given event, and removes any handler that returns :done."
- [event]
+ [handlers* event]
(log/debug "handling event: " event)
(let [event-type (:event-type event)
- handlers (get @event-handlers* event-type #{})
- keepers (set (doall (filter #(not (= :done (run-handler % event))) handlers)))]
- (dosync (alter event-handlers* assoc event-type
- (intersection keepers (get @event-handlers* event-type #{}))))))
+ handlers (get @handlers* event-type {})
+ _ (log/debug "handlers: " handlers)
+ drop-keys (doall (map first
+ (filter (fn [[k handler]]
+ (= :done (run-handler handler event)))
+ handlers)))]
+ (dosync
+ (alter handlers* assoc event-type
+ (dissoc (get @handlers* event-type) drop-keys)))))
(defn event
"Fire an event of type event-type with any number of additional properties.
@@ -69,12 +93,6 @@
[event-type & args]
{:pre [(even? (count args))]}
(log/debug "event: " event-type args)
- (.execute thread-pool #(handle-event (apply hash-map :event-type event-type args))))
-
-(defn sync-event
- "Experimental synchronous event send."
- [event-type & args]
- {:pre [(even? (count args))]}
- (log/debug "synchronous event: " event-type args)
- (handle-event (apply hash-map :event-type event-type args)))
-
+ (let [event (apply hash-map :event-type event-type args)]
+ (handle-event sync-event-handlers* event)
+ (.execute thread-pool #(handle-event event-handlers* event))))
@@ -5,7 +5,7 @@
(:use (overtone.core synth ugen sc event util)))
; Define a default wav player synth
-(defsynth mono-player
+(defsynth mono-player
"Plays a single channel audio buffer."
[buf 0 rate 1.0 start-pos 0.0 loop? 0]
(out 0 (pan2
@@ -55,7 +55,7 @@
;(println "loading sample: " path args)
(apply load-sample* path args)))
-(defonce _sample-handler_ (on :connected load-all-samples))
+(on-event :connected :sample-loader load-all-samples)
(defn sample?
[s]
View
@@ -107,8 +107,9 @@
; The base handler for receiving osc messages just forwards the message on
; as an event using the osc path as the event key.
-(on ::osc-msg-received (fn [{{path :path args :args} :msg}]
- (event path :path path :args args)))
+(on-sync-event ::osc-msg-received :osc-receiver
+ (fn [{{path :path args :args} :msg}]
+ (event path :path path :args args)))
(defn snd
"Sends an OSC message."
@@ -190,8 +191,8 @@
(log/debug (format "node-created: %d" id)))
; Setup the feedback handlers with the audio server.
-(on "/n_end" #(node-destroyed (first (:args %))))
-(on "/n_go" #(node-created (first (:args %))))
+(on-event "/n_end" :node-destroyer #(node-destroyed (first (:args %))))
+(on-event "/n_go" :node-creator #(node-created (first (:args %))))
(def N-RETRIES 20)
@@ -225,7 +226,7 @@
(ref-set status* :connecting))
; Runs once when we receive the first status.reply message
- (on "status.reply"
+ (on-event "status.reply" :connected-handler
#(do
(dosync (ref-set status* :connected))
(notify true) ; turn on notifications now that we can communicate
@@ -280,7 +281,7 @@
"Register your intent to wait for a message associated with given path to be received from the server. Returns a promise that will contain the message once it has been received. Does not block current thread (this only happens once you try and look inside the promise and the reply has not yet been received)."
[path]
(let [p (promise)]
- (on path #(do (deliver p %) :done))
+ (on-sync-event path (uuid) #(do (deliver p %) :done))
p))
(defn read-reply
@@ -318,9 +319,10 @@
[]
(if (= :connected @status*)
(let [p (promise)]
- (on "/status.reply" #(do
- (deliver p (parse-status (:args %)))
- :done))
+ (on-event "/status.reply" :status-check
+ #(do
+ (deliver p (parse-status (:args %)))
+ :done))
(snd "/status")
(try
(.get (future @p) STATUS-TIMEOUT TimeUnit/MILLISECONDS)
@@ -367,11 +369,11 @@
:windows []
:mac ["-U" "/Applications/SuperCollider/plugins"] })
-(defonce _jack_connector_
- (if (= :linux (@config* :os))
- (on :connected #(connect-jack-ports))))
+(if (= :linux (@config* :os))
+ (on-event :connected :jack-connector
+ #(connect-jack-ports)))
-(defonce scsynth-server* (ref nil))
+(defonce scsynth-server* (ref nil))
(defn internal-booter [port]
(reset! running?* true)
@@ -392,7 +394,7 @@
(log/debug "Booting SuperCollider internal server (scsynth)...")
(.start sc-thread)
(dosync (ref-set server-thread* sc-thread))
- (on :booted connect)
+ (on-event :booted :on-boot-connector connect)
:booting))))
(defn- sc-log
@@ -448,7 +450,7 @@
"Quit the SuperCollider synth process."
[]
(log/info "quiting supercollider")
- (sync-event :quit)
+ (event :quit)
(when (connected?)
(snd "/quit")
(log/debug "SERVER: " @server*)
@@ -680,10 +682,11 @@
for an example of usage.
"
[path handler]
- (on "/done" #(if (= path (first (:args %)))
- (do
- (handler)
- :done))))
+ (on-event "/done" :done-handler
+ #(if (= path (first (:args %)))
+ (do
+ (handler)
+ :done))))
; TODO: Look into multi-channel buffers. Probably requires adding multi-id allocation
; support to the bit allocator too...
@@ -808,7 +811,7 @@
(println "loading synthdef: " sname)
(snd "/d_recv" (synthdef-bytes sdef))))
-(defonce _synthdef-handler_ (on :connected load-all-synthdefs))
+(on-event :connected :synthdef-loader load-all-synthdefs)
(defn load-synth-file
"Load a synth definition file onto the audio server."
@@ -824,9 +827,9 @@
[]
(clear-msg-queue)
(group-clear SYNTH-GROUP) ; clear the synth group
- (sync-event :reset))
+ (event :reset))
-(defonce _connect-handler_ (on :connected #(group :tail ROOT-GROUP)))
+(on-event :connected :root-group-creator #(group :tail ROOT-GROUP))
(defn restart
"Reset everything and restart the SuperCollider process."
View
@@ -14,14 +14,14 @@
(let [g (group :tail ROOT-GROUP)]
(dosync (ref-set inst-group* g))))
-(defonce _on-connect_ (on :connected create-inst-group))
+(on-sync-event :connected :create-instruments create-inst-group)
; Clear and re-create the instrument groups after a reset
(defn reset-inst-groups []
(doseq [inst @instruments*]
(group-clear (:group inst))))
-(defonce _reset_inst (on :reset #'reset-inst-groups))
+(on-sync-event :reset :reset-instruments reset-inst-groups)
; Add instruments to the session when defined
(defn add-instrument [inst]
File renamed without changes.
@@ -0,0 +1,35 @@
+(ns overtone.core.event-test
+ (:require [overtone.core.log :as log])
+ (:use overtone.core.event
+ clojure.test))
+
+(log/level :debug)
+
+(deftest handler-test
+ (let [counter (atom 0)]
+ (on-sync-event :test-event :a #(swap! counter inc))
+ (on-event :test-event :b #(swap! counter inc))
+ (event :test-event)
+ (Thread/sleep 100)
+ (is (= 2 @counter))
+
+ (remove-handler :test-event :b)
+ (event :test-event)
+ (Thread/sleep 100)
+ (is (= 3 @counter))
+
+ (on-event :test-event :x #(swap! counter inc))
+ (on-event :test-event :y #(swap! counter inc))
+ (on-event :test-event :z #(swap! counter inc))
+ (event :test-event)
+ (Thread/sleep 100)
+ (is (= 7 @counter))
+
+ (clear-handlers :test-event)
+ (event :test-event)
+ (Thread/sleep 100)
+ (is (= 7 @counter))))
+
+(defn event-tests []
+ (binding [*test-out* *out*]
+ (run-tests 'overtone.core.event-test)))
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 comments on commit cff0fd9

Please sign in to comment.