|
| 1 | +(ns overtone.sc.machinery.server.comms |
| 2 | + (:use [overtone.sc.machinery.server osc-validator] |
| 3 | + [overtone.libs.event] |
| 4 | + [overtone.util.lib :only [uuid await-promise!]]) |
| 5 | + (:require [overtone.util.log :as log])) |
| 6 | + |
| 7 | +(defonce server-sync-id* (atom 0)) |
| 8 | +(defonce osc-debug* (atom false)) |
| 9 | +(defonce server-osc-peer* (ref nil)) |
| 10 | + |
| 11 | + |
| 12 | +;; The base handler for receiving osc messages just forwards the message on |
| 13 | +;; as an event using the osc path as the event key. |
| 14 | +(on-sync-event :osc-msg-received |
| 15 | + (fn [{{path :path args :args} :msg}] |
| 16 | + (event path :path path :args args)) |
| 17 | + ::osc-receiver) |
| 18 | + |
| 19 | +(defn- massage-numerical-args |
| 20 | + "Massage numerical args to the form SC would like them. Currently this just |
| 21 | + casts all Longs to Integers." |
| 22 | + [args] |
| 23 | + (map (fn [arg] |
| 24 | + (if (instance? Long arg) |
| 25 | + (Integer. arg) |
| 26 | + arg)) |
| 27 | + args)) |
| 28 | + |
| 29 | +(defn server-snd |
| 30 | + "Sends an OSC message to the server. If the message path is a known scsynth |
| 31 | + path, then the types of the arguments will be checked according to what |
| 32 | + scsynth is expecting. Automatically converts any args which are longs to ints. |
| 33 | +
|
| 34 | + (server-snd \"/foo\" 1 2.0 \"eggs\")" |
| 35 | + [path & args] |
| 36 | + (let [args (massage-numerical-args args)] |
| 37 | + (when @osc-debug* |
| 38 | + (println "Sending: " path [args]) |
| 39 | + (log/debug (str "Sending: " path [args]))) |
| 40 | + (apply validated-snd @server-osc-peer* path args))) |
| 41 | + |
| 42 | +(defn- update-server-sync-id |
| 43 | + "update osc-sync-id*. Increments by 1 unless it has maxed out |
| 44 | + in which case it resets it to 0." |
| 45 | + [] |
| 46 | + (swap! server-sync-id* (fn [cur] (if (= Integer/MAX_VALUE cur) |
| 47 | + 0 |
| 48 | + (inc cur))))) |
| 49 | + |
| 50 | +(defn on-server-sync |
| 51 | + "Registers the handler to be executed when all the osc messages generated |
| 52 | + by executing the action-fn have completed. Returns result of action-fn." |
| 53 | + [action-fn handler-fn] |
| 54 | + (let [id (update-server-sync-id) |
| 55 | + key (uuid)] |
| 56 | + (on-event "/synced" |
| 57 | + (fn [msg] (when (= id (first (:args msg))) |
| 58 | + (do |
| 59 | + (handler-fn) |
| 60 | + :done))) |
| 61 | + key) |
| 62 | + |
| 63 | + (let [res (action-fn)] |
| 64 | + (server-snd "/sync" id) |
| 65 | + res))) |
| 66 | + |
| 67 | +(defn server-sync |
| 68 | + "Send a sync message to the server with the specified id. Server will reply |
| 69 | + with a synced message when all incoming messages up to the sync message have |
| 70 | + been handled. See with-server-sync and on-server-sync for more typical |
| 71 | + usage." |
| 72 | + [id] |
| 73 | + (server-snd "/sync" id)) |
| 74 | + |
| 75 | +(defn with-server-self-sync |
| 76 | + "Blocks the current thread until the action-fn explicitly sends a server sync. |
| 77 | + The action-fn is assumed to have one argument which will be the unique sync id. |
| 78 | + This is useful when the action-fn is itself asynchronous yet you wish to |
| 79 | + synchronise with its completion. The action-fn can sync using the fn server-sync. |
| 80 | + Returns the result of action-fn." |
| 81 | + [action-fn] |
| 82 | + (let [id (update-server-sync-id) |
| 83 | + prom (promise) |
| 84 | + key (uuid)] |
| 85 | + (on-event "/synced" |
| 86 | + (fn [msg] (when (= id (first (:args msg))) |
| 87 | + (do |
| 88 | + (deliver prom true) |
| 89 | + :done))) |
| 90 | + key) |
| 91 | + (let [res (action-fn id)] |
| 92 | +< (await-promise! prom) |
| 93 | + res))) |
| 94 | + |
| 95 | +(defn with-server-sync |
| 96 | + "Blocks current thread until all osc messages in action-fn have completed. |
| 97 | + Returns result of action-fn." |
| 98 | + [action-fn] |
| 99 | + (let [id (update-server-sync-id) |
| 100 | + prom (promise) |
| 101 | + key (uuid)] |
| 102 | + (on-event "/synced" |
| 103 | + (fn [msg] (when (= id (first (:args msg))) |
| 104 | + (do |
| 105 | + (deliver prom true) |
| 106 | + :done))) |
| 107 | + key) |
| 108 | + (let [res (action-fn)] |
| 109 | + (server-snd "/sync" id) |
| 110 | + (await-promise! prom) |
| 111 | + res))) |
| 112 | + |
| 113 | +(defn server-recv |
| 114 | + "Register your intent to wait for a message associated with given path to be |
| 115 | + received from the server. Returns a promise that will contain the message once |
| 116 | + it has been received. Does not block current thread (this only happens once |
| 117 | + you try and look inside the promise and the reply has not yet been received). |
| 118 | +
|
| 119 | + If an optional matcher-fn is specified, will only deliver the promise when |
| 120 | + the matcher-fn returns true. The matcher-fn should accept one arg which is |
| 121 | + the incoming event info." |
| 122 | + ([path] (server-recv path nil)) |
| 123 | + ([path matcher-fn] |
| 124 | + (let [p (promise) |
| 125 | + key (uuid)] |
| 126 | + (on-sync-event path |
| 127 | + (fn [info] |
| 128 | + (when (or (nil? matcher-fn) |
| 129 | + (matcher-fn info)) |
| 130 | + (deliver p info) |
| 131 | + :done)) |
| 132 | + key) |
| 133 | + p))) |
| 134 | + |
| 135 | +(defn sc-osc-debug-on |
| 136 | + "Log and print out all outgoing OSC messages" |
| 137 | + [] |
| 138 | + (reset! osc-debug* true )) |
| 139 | + |
| 140 | +(defn sc-osc-debug-off |
| 141 | + "Turns off OSC debug messages (see sc-osc-debug-on)" |
| 142 | + [] |
| 143 | + (reset! osc-debug* false)) |
0 commit comments