1 AbstractResponderFunc {
2 classvar allFuncProxies;
3 var <func, <srcID, <enabled = false, <dispatcher, <permanent = false;
5 *initClass { Class.initClassTree(AbstractDispatcher); allFuncProxies = IdentitySet.new; }
9 if(permanent.not, { CmdPeriod.add(this) });
12 allFuncProxies.add(this);
17 if(permanent.not, { CmdPeriod.remove(this) });
18 dispatcher.remove(this);
24 this.changed(\function);
27 add {|newFunc| func = func.addFunc(newFunc); this.changed(\function); }
29 remove {|removeFunc| func = func.removeFunc(removeFunc); this.changed(\function); }
31 gui { this.subclassResponsibility(thisMethod) }
33 cmdPeriod { this.free }
36 var oneShotFunc, wrappedFunc;
38 oneShotFunc = { arg ...args; wrappedFunc.value(*args); this.free };
39 this.prFunc_(oneShotFunc);
44 if(bool && enabled, { CmdPeriod.remove(this) }, {CmdPeriod.add(this) })
47 fix { this.permanent_(true) }
49 free { allFuncProxies.remove(this); this.disable }
51 clear { this.prFunc_(nil) }
55 result = IdentityDictionary.new;
56 allFuncProxies.do({|funcProxy|
58 key = funcProxy.dispatcher.typeKey;
59 result[key] = result[key].add(funcProxy)
66 result = IdentityDictionary.new;
67 allFuncProxies.select(_.enabled).do({|funcProxy|
69 key = funcProxy.dispatcher.typeKey;
70 result[key] = result[key].add(funcProxy)
77 result = IdentityDictionary.new;
78 allFuncProxies.reject(_.enabled).do({|funcProxy|
80 key = funcProxy.dispatcher.typeKey;
81 result[key] = result[key].add(funcProxy)
88 // defines the required interface
91 var registered = false;
93 *new { ^super.new.init; }
95 init { all.add(this);}
97 *initClass { all = IdentitySet.new }
99 add {|funcProxy| this.subclassResponsibility(thisMethod) } // proxies call this to add themselves to this dispatcher; should register this if needed
101 remove {|funcProxy| this.subclassResponsibility(thisMethod) } // proxies call this to remove themselves from this dispatcher; should unregister if needed
103 value { this.subclassResponsibility(thisMethod) }
105 valueArray {arg args; ^this.value(*args) } // needed to work in FunctionLists
107 register { this.subclassResponsibility(thisMethod) } // register this dispatcher to listen for its message type
109 unregister { this.subclassResponsibility(thisMethod) } // unregister this dispatcher so it no longer listens
111 free { this.unregister; all.remove(this) } // I'm done
113 typeKey { this.subclassResponsibility(thisMethod) } // a Symbol
115 update { } // code here to update any changed state in this dispatcher's proxies, e.g. a new function; default does nothing
119 // basis for the default dispatchers
120 // uses function wrappers for matching
121 AbstractWrappingDispatcher : AbstractDispatcher {
122 var active, <wrappedFuncs;
124 init { super.init; active = IdentityDictionary.new; wrappedFuncs = IdentityDictionary.new; }
128 funcProxy.addDependant(this);
129 func = this.wrapFunc(funcProxy);
130 wrappedFuncs[funcProxy] = func;
131 keys = this.getKeysForFuncProxy(funcProxy);
132 keys.do({|key| active[key] = active[key].addFunc(func) }); // support multiple keys
133 if(registered.not, {this.register});
138 funcProxy.removeDependant(this);
139 keys = this.getKeysForFuncProxy(funcProxy);
140 func = wrappedFuncs[funcProxy];
141 keys.do({|key| active[key] = active[key].removeFunc(func) }); // support multiple keys
142 wrappedFuncs[funcProxy] = nil;
143 if(active.size == 0, {this.unregister});
146 // old Funk vs. new Funk
147 updateFuncForFuncProxy {|funcProxy|
148 var func, oldFunc, keys;
149 func = this.wrapFunc(funcProxy);
150 oldFunc = wrappedFuncs[funcProxy];
151 wrappedFuncs[funcProxy] = func;
152 keys = this.getKeysForFuncProxy(funcProxy);
153 keys.do({|key| active[key] = active[key].replaceFunc(oldFunc, func) }); // support multiple keys
156 wrapFunc { this.subclassResponsibility(thisMethod) }
158 getKeysForFuncProxy { this.subclassResponsibility(thisMethod) }
160 update {|funcProxy, what| if(what == \function, { this.updateFuncForFuncProxy(funcProxy) }) }
162 free { wrappedFuncs.keys.do({|funcProxy| funcProxy.removeDependant(this) }); super.free }
166 // The default dispatchers below store by the 'most significant' message argument for fast lookup
167 // These are for use when more than just the 'most significant' argument needs to be matched
168 AbstractMessageMatcher {
171 value { this.subclassResponsibility(thisMethod) }
173 valueArray {arg args; ^this.value(*args) } // needed to work in FunctionLists
177 ///////////////////// OSC
179 OSCMessageDispatcher : AbstractWrappingDispatcher {
181 wrapFunc {|funcProxy|
182 var func, srcID, recvPort;
183 func = funcProxy.func;
184 srcID = funcProxy.srcID;
185 recvPort = funcProxy.recvPort;
187 { srcID.notNil && recvPort.notNil }, { OSCFuncBothMessageMatcher(srcID, recvPort, func) },
188 { srcID.notNil }, { OSCFuncAddrMessageMatcher(srcID, func) },
189 { recvPort.notNil }, { OSCFuncRecvPortMessageMatcher(recvPort, func) },
194 getKeysForFuncProxy {|funcProxy| ^[funcProxy.path];}
196 value {|time, addr, recvPort, msg| active[msg[0]].value(msg, time, addr, recvPort);}
199 thisProcess.recvOSCfunc = thisProcess.recvOSCfunc.addFunc(this);
204 thisProcess.recvOSCfunc = thisProcess.recvOSCfunc.removeFunc(this);
208 typeKey { ^('OSC unmatched').asSymbol }
212 OSCMessagePatternDispatcher : OSCMessageDispatcher {
214 value {|time, addr, recvPort, msg|
217 active.keysValuesDo({|key, func|
218 if(key.matchOSCAddressPattern(pattern), {func.value(msg, time, addr, recvPort);});
222 typeKey { ^('OSC matched').asSymbol }
226 OSCFunc : AbstractResponderFunc {
227 classvar <>defaultDispatcher, <>defaultMatchingDispatcher, traceFunc, traceRunning = false;
228 var <path, <recvPort;
231 defaultDispatcher = OSCMessageDispatcher.new;
232 defaultMatchingDispatcher = OSCMessagePatternDispatcher.new;
233 traceFunc = {|time, replyAddr, recvPort, msg|
234 "OSC Message Received:\n\ttime: %\n\taddress: %\n\trecvPort: %\n\tmsg: %\n\n".postf(time, replyAddr, recvPort, msg);
238 *new { arg func, path, srcID, recvPort, dispatcher;
239 ^super.new.init(func, path, srcID, recvPort, dispatcher ? defaultDispatcher);
242 *newMatching { arg func, path, srcID, recvPort;
243 ^super.new.init(func, path, srcID, recvPort, defaultMatchingDispatcher);
246 *trace {|bool = true|
248 if(traceRunning.not, {
249 thisProcess.addOSCFunc(traceFunc);
254 thisProcess.removeOSCFunc(traceFunc);
255 CmdPeriod.remove(this);
256 traceRunning = false;
260 *cmdPeriod { this.trace(false) }
262 init {|argfunc, argpath, argsrcID, argrecvPort, argdisp|
263 path = (argpath ? path).asString;
264 if(path[0] != $/, {path = "/" ++ path}); // demand OSC compliant paths
265 path = path.asSymbol;
266 srcID = argsrcID ? srcID;
267 recvPort = argrecvPort ? recvPort;
269 dispatcher = argdisp ? dispatcher;
271 allFuncProxies.add(this);
274 printOn { arg stream; stream << this.class.name << "(" <<* [path, srcID] << ")" }
283 all = IdentityDictionary.new;
286 *new { arg key, func, path, srcID, recvPort, dispatcher;
287 var res = all.at(key);
289 ^super.new(func, path, srcID, recvPort, dispatcher).addToAll(key);
294 res.init(func, path, srcID, recvPort, dispatcher);
295 }, { res.init(func, path, srcID, recvPort, dispatcher).disable; });
301 addToAll {|argkey| key = argkey; all.put(key, this) }
303 free { all[key] = nil; super.free; }
305 printOn { arg stream; stream << this.class.name << "(" <<* [key, path, srcID] << ")" }
310 // if you need to test for address func gets wrapped in this
311 OSCFuncAddrMessageMatcher : AbstractMessageMatcher {
314 *new {|addr, func| ^super.new.init(addr, func);}
316 init {|argaddr, argfunc| addr = argaddr; func = argfunc; }
318 value {|msg, time, testAddr, recvPort|
319 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)}, {
320 func.value(msg, time, testAddr, recvPort)
325 // if you need to test for recvPort func gets wrapped in this
326 OSCFuncRecvPortMessageMatcher : AbstractMessageMatcher {
329 *new {|recvPort, func| ^super.new.init(recvPort, func);}
331 init {|argrecvPort, argfunc| recvPort = argrecvPort; func = argfunc; }
333 value {|msg, time, addr, testRecvPort|
334 if(testRecvPort == recvPort, {
335 func.value(msg, time, addr, testRecvPort)
340 OSCFuncBothMessageMatcher : AbstractMessageMatcher {
343 *new {|addr, recvPort, func| ^super.new.init(addr, recvPort, func);}
345 init {|argaddr, argrecvPort, argfunc| addr = argaddr; recvPort = argrecvPort; func = argfunc; }
347 value {|msg, time, testAddr, testRecvPort|
348 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)} and: {testRecvPort == recvPort}, {
349 func.value(msg, time, testAddr, testRecvPort)
354 ///////////////////// MIDI
356 // for \noteOn, \noteOff, \control, \polytouch
357 MIDIMessageDispatcher : AbstractWrappingDispatcher {
360 *new {|messageType| ^super.new.messageType_(messageType) }
362 getKeysForFuncProxy {|funcProxy| ^(funcProxy.msgNum ? (0..127)).asArray;} // noteNum, etc.
364 value {|src, chan, num, val| active[num].value(val, num, chan, src);}
367 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).addFunc(this));
372 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).removeFunc(this));
376 // wrapper objects based on arg type and testing requirements
377 wrapFunc {|funcProxy|
378 var func, chan, srcID;
379 func = funcProxy.func;
380 chan = funcProxy.chan;
381 srcID = funcProxy.srcID;
383 { srcID.notNil && chan.isArray }, {MIDIFuncBothCAMessageMatcher(chan, srcID, func)},
384 { srcID.notNil && chan.notNil }, {MIDIFuncBothMessageMatcher(chan, srcID, func)},
385 { srcID.notNil }, {MIDIFuncSrcMessageMatcher(srcID, func)},
386 { chan.isArray }, {MIDIFuncChanArrayMessageMatcher(chan, func)},
387 { chan.notNil }, {MIDIFuncChanMessageMatcher(chan, func)},
392 typeKey { ^('MIDI ' ++ messageType).asSymbol }
395 // for \touch, \program, \bend
396 MIDIMessageDispatcherNV : MIDIMessageDispatcher {
398 getKeysForFuncProxy {|funcProxy| ^(funcProxy.chan ? (0..15)).asArray;} // chan
400 value {|src, chan, val| active[chan].value(val, chan, src);}
402 // wrapper objects based on arg type and testing requirements
403 wrapFunc {|funcProxy|
404 var func, chan, srcID;
405 func = funcProxy.func;
406 chan = funcProxy.chan;
407 srcID = funcProxy.srcID;
410 { srcID.notNil }, {MIDIFuncSrcMessageMatcherNV(srcID, func)},
417 MIDIFunc : AbstractResponderFunc {
418 classvar <>defaultDispatchers;
419 var <chan, <msgNum, <msgVal, <msgType;
422 defaultDispatchers = IdentityDictionary.new;
423 [\noteOn, \noteOff, \control, \polytouch].do({|type|
424 defaultDispatchers[type] = MIDIMessageDispatcher(type);
426 [\touch, \program, \bend].do({|type|
427 defaultDispatchers[type] = MIDIMessageDispatcherNV(type);
431 *new { arg func, msgNum, chan, msgType, srcID, dispatcher;
432 ^super.new.init(func, msgNum, chan, msgType, srcID, dispatcher ? defaultDispatchers[msgType]);
435 *cc { arg func, ccNum, chan, srcID, dispatcher;
436 ^this.new(func, ccNum, chan, \control, srcID, dispatcher);
439 *noteOn { arg func, noteNum, chan, srcID, dispatcher;
440 ^this.new(func, noteNum, chan, \noteOn, srcID, dispatcher);
443 *noteOff { arg func, noteNum, chan, srcID, dispatcher;
444 ^this.new(func, noteNum, chan, \noteOff, srcID, dispatcher);
447 *polytouch { arg func, noteNum, chan, srcID, dispatcher;
448 ^this.new(func, noteNum, chan, \polytouch, srcID, dispatcher);
451 *touch { arg func, chan, srcID, dispatcher;
452 ^this.new(func, nil, chan, \touch, srcID, dispatcher);
455 *bend { arg func, chan, srcID, dispatcher;
456 ^this.new(func, nil, chan, \bend, srcID, dispatcher);
459 *program { arg func, chan, srcID, dispatcher;
460 ^this.new(func, nil, chan, \program, srcID, dispatcher);
463 init {|argfunc, argmsgNum, argchan, argType, argsrcID, argdisp|
464 msgNum = msgNum ? argmsgNum;
465 chan = chan ? argchan;
466 srcID = argsrcID ? srcID;
468 msgType = argType ? msgType;
469 dispatcher = argdisp ? dispatcher;
471 allFuncProxies.add(this);
475 printOn { arg stream; stream << this.class.name << "(" <<* [msgType, msgNum, chan] << ")" }
480 classvar <>all; // same as other def classes, do we need a setter really?
484 all = IdentityDictionary.new;
487 *new { arg key, func, msgNum, chan, msgType, srcID, dispatcher;
488 var res = all.at(key);
490 ^super.new(func, msgNum, chan, msgType, srcID, dispatcher).addToAll(key);
495 res.init(func, msgNum, chan, msgType, srcID, dispatcher ? defaultDispatchers[msgType]);
496 }, { res.init(func, msgNum, chan, msgType, srcID, dispatcher ? defaultDispatchers[msgType]).disable; });
502 *cc { arg key, func, ccNum, chan, srcID, dispatcher;
503 ^this.new(key, func, ccNum, chan, \control, srcID, dispatcher);
506 *noteOn { arg key, func, noteNum, chan, srcID, dispatcher;
507 ^this.new(key, func, noteNum, chan, \noteOn, srcID, dispatcher);
510 *noteOff { arg key, func, noteNum, chan, srcID, dispatcher;
511 ^this.new(key, func, noteNum, chan, \noteOff, srcID, dispatcher);
514 *polytouch { arg key, func, noteNum, chan, srcID, dispatcher;
515 ^this.new(key, func, noteNum, chan, \polytouch, srcID, dispatcher);
518 *touch { arg key, func, chan, srcID, dispatcher;
519 ^this.new(key, func, nil, chan, \touch, srcID, dispatcher);
522 *bend { arg key, func, chan, srcID, dispatcher;
523 ^this.new(key, func, nil, chan, \bend, srcID, dispatcher);
526 *program { arg key, func, chan, srcID, dispatcher;
527 ^this.new(key, func, nil, chan, \program, srcID, dispatcher);
530 addToAll {|argkey| key = argkey; all.put(key, this) }
532 free { all[key] = nil; super.free; }
535 printOn { arg stream; stream << this.class.name << "(" <<* [key, msgType, msgNum, chan] << ")" }
540 // if you need to test for srcID func gets wrapped in this
541 MIDIFuncSrcMessageMatcher : AbstractMessageMatcher {
544 *new {|srcID, func| ^super.new.init(srcID, func);}
546 init {|argsrcID, argfunc| srcID = argsrcID; func = argfunc; }
548 value {|value, num, chan, testSrc|
549 if(srcID == testSrc, {func.value(value, num, chan, testSrc)})
553 // if you need to test for srcID func gets wrapped in this
554 MIDIFuncChanMessageMatcher : AbstractMessageMatcher {
557 *new {|chan, func| ^super.new.init(chan, func);}
559 init {|argchan, argfunc| chan = argchan; func = argfunc; }
561 value {|value, num, testChan, srcID|
562 if(chan == testChan, {func.value(value, num, testChan, srcID)})
566 // if you need to test for chanArray func gets wrapped in this
567 MIDIFuncChanArrayMessageMatcher : AbstractMessageMatcher {
568 var chanBools, <>func;
570 *new {|chanArray, func|
572 // lookup bool by index fastest, so construct an Array here
573 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
574 ^super.new.init(chanBools, func);
577 init {|argchanbools, argfunc| chanBools = argchanbools; func = argfunc; }
579 value {|value, num, testChan, srcID|
580 // lookup bool by index fastest
581 if(chanBools[testChan], {func.value(value, num, testChan, srcID)})
585 // version for message types which don't pass a val
586 MIDIFuncSrcMessageMatcherNV : MIDIFuncSrcMessageMatcher {
588 value {|num, chan, testSrc|
589 if(srcID == testSrc, {func.value(num, chan, testSrc)})
593 // if you need to test for chan and srcID func gets wrapped in this
594 MIDIFuncBothMessageMatcher : AbstractMessageMatcher {
597 *new {|chan, srcID, func| ^super.new.init(chan, srcID, func);}
599 init {|argchan, argsrcID, argfunc| chan = argchan; srcID = argsrcID; func = argfunc; }
601 value {|value, num, testChan, testSrc|
602 if(srcID == testSrc and: {chan == testChan}, {func.value(value, num, testChan, testSrc)})
607 // if you need to test for chanArray and srcID func gets wrapped in this
608 MIDIFuncBothCAMessageMatcher : AbstractMessageMatcher {
609 var chanBools, srcID;
611 *new {|chanArray, srcID, func|
613 // lookup bool by index fastest, so construct an Array here
614 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
615 ^super.new.init(chanBools, srcID, func);
618 init {|argbools, argsrcID, argfunc| chanBools = argbools; srcID = argsrcID; func = argfunc; }
620 value {|value, num, testChan, testSrc|
621 if(srcID == testSrc and: {chanBools[testChan]}, {func.value(value, num, testChan, testSrc)})