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; this.free; wrappedFunc.value(*args); };
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 { |funcProxy| 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, argTemplate;
183 func = funcProxy.func;
184 srcID = funcProxy.srcID;
185 recvPort = funcProxy.recvPort;
186 argTemplate = funcProxy.argTemplate;
187 if(argTemplate.notNil, { func = OSCArgsMatcher(argTemplate, func)});
189 { srcID.notNil && recvPort.notNil }, { OSCFuncBothMessageMatcher(srcID, recvPort, func) },
190 { srcID.notNil }, { OSCFuncAddrMessageMatcher(srcID, func) },
191 { recvPort.notNil }, { OSCFuncRecvPortMessageMatcher(recvPort, func) },
196 getKeysForFuncProxy {|funcProxy| ^[funcProxy.path];}
198 value {|msg, time, addr, recvPort| active[msg[0]].value(msg, time, addr, recvPort);}
201 thisProcess.addOSCRecvFunc(this);
206 thisProcess.removeOSCRecvFunc(this);
210 typeKey { ^('OSC unmatched').asSymbol }
214 OSCMessagePatternDispatcher : OSCMessageDispatcher {
216 value {|msg, time, addr, recvPort|
219 active.keysValuesDo({|key, func|
220 if(key.matchOSCAddressPattern(pattern), {func.value(msg, time, addr, recvPort);});
224 typeKey { ^('OSC matched').asSymbol }
228 OSCFunc : AbstractResponderFunc {
229 classvar <>defaultDispatcher, <>defaultMatchingDispatcher, traceFunc, traceRunning = false;
230 var <path, <recvPort, <argTemplate;
233 defaultDispatcher = OSCMessageDispatcher.new;
234 defaultMatchingDispatcher = OSCMessagePatternDispatcher.new;
235 traceFunc = {|msg, time, addr, recvPort|
236 "OSC Message Received:\n\ttime: %\n\taddress: %\n\trecvPort: %\n\tmsg: %\n\n".postf(time, addr, recvPort, msg);
240 *new { arg func, path, srcID, recvPort, argTemplate, dispatcher;
241 ^super.new.init(func, path, srcID, recvPort, argTemplate, dispatcher ? defaultDispatcher);
244 *newMatching { arg func, path, srcID, argTemplate, recvPort;
245 ^super.new.init(func, path, srcID, recvPort, argTemplate, defaultMatchingDispatcher);
248 *trace {|bool = true|
250 if(traceRunning.not, {
251 thisProcess.addOSCRecvFunc(traceFunc);
256 thisProcess.removeOSCRecvFunc(traceFunc);
257 CmdPeriod.remove(this);
258 traceRunning = false;
262 *cmdPeriod { this.trace(false) }
264 init {|argfunc, argpath, argsrcID, argrecvPort, argtemplate, argdisp|
265 path = (argpath ? path).asString;
266 if(path[0] != $/, {path = "/" ++ path}); // demand OSC compliant paths
267 path = path.asSymbol;
268 srcID = argsrcID ? srcID;
269 recvPort = argrecvPort ? recvPort;
270 argtemplate = argtemplate.collect({|oscArg|
271 if(oscArg.isKindOf(String), {oscArg.asSymbol}, {oscArg}); // match Symbols not Strings
273 argTemplate = argtemplate ? argTemplate;
275 dispatcher = argdisp ? dispatcher;
277 allFuncProxies.add(this);
280 printOn { arg stream; stream << this.class.name << "(" <<* [path, srcID, argTemplate] << ")" }
289 all = IdentityDictionary.new;
292 *new { arg key, func, path, srcID, recvPort, argTemplate, dispatcher;
293 var res = all.at(key);
295 ^super.new(func, path, srcID, recvPort, argTemplate, dispatcher).addToAll(key);
300 res.init(func, path, srcID, recvPort, argTemplate, dispatcher);
301 }, { res.init(func, path, srcID, recvPort, argTemplate, dispatcher).disable; });
307 *newMatching { arg key, func, path, srcID, argTemplate, recvPort;
308 ^this.new(key, func, path, srcID, recvPort, argTemplate, defaultMatchingDispatcher);
311 addToAll {|argkey| key = argkey; all.put(key, this) }
313 free { all[key] = nil; super.free; }
315 printOn { arg stream; stream << this.class.name << "(" <<* [key, path, srcID, argTemplate] << ")" }
320 // if you need to test for address func gets wrapped in this
321 OSCFuncAddrMessageMatcher : AbstractMessageMatcher {
324 *new {|addr, func| ^super.new.init(addr, func);}
326 init {|argaddr, argfunc| addr = argaddr; func = argfunc; }
328 value {|msg, time, testAddr, recvPort|
329 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)}, {
330 func.value(msg, time, testAddr, recvPort)
335 // if you need to test for recvPort func gets wrapped in this
336 OSCFuncRecvPortMessageMatcher : AbstractMessageMatcher {
339 *new {|recvPort, func| ^super.new.init(recvPort, func);}
341 init {|argrecvPort, argfunc| recvPort = argrecvPort; func = argfunc; }
343 value {|msg, time, addr, testRecvPort|
344 if(testRecvPort == recvPort, {
345 func.value(msg, time, addr, testRecvPort)
350 OSCFuncBothMessageMatcher : AbstractMessageMatcher {
353 *new {|addr, recvPort, func| ^super.new.init(addr, recvPort, func);}
355 init {|argaddr, argrecvPort, argfunc| addr = argaddr; recvPort = argrecvPort; func = argfunc; }
357 value {|msg, time, testAddr, testRecvPort|
358 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)} and: {testRecvPort == recvPort}, {
359 func.value(msg, time, testAddr, testRecvPort)
364 OSCArgsMatcher : AbstractMessageMatcher {
367 *new{|argTemplate, func| ^super.new.init(argTemplate, func) }
369 init {|argArgTemplate, argFunc| argTemplate = argArgTemplate.asArray; func = argFunc; }
371 value {|testMsg, time, addr, recvPort|Ê
372 testMsg[1..].do({|item, i|
373 if(argTemplate[i].matchItem(item).not, {^this});
375 func.value(testMsg, time, addr, recvPort)
379 ///////////////////// MIDI
381 // for \noteOn, \noteOff, \control, \polytouch
382 MIDIMessageDispatcher : AbstractWrappingDispatcher {
385 *new {|messageType| ^super.new.messageType_(messageType) }
387 getKeysForFuncProxy {|funcProxy| ^(funcProxy.msgNum ? (0..127)).asArray;} // noteNum, etc.
389 value {|src, chan, num, val| active[num].value(val, num, chan, src);}
392 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).addFunc(this));
397 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).removeFunc(this));
401 // wrapper objects based on arg type and testing requirements
402 wrapFunc {|funcProxy|
403 var func, chan, srcID, argTemplate;
404 func = funcProxy.func;
405 chan = funcProxy.chan;
406 srcID = funcProxy.srcID;
407 argTemplate = funcProxy.argTemplate;
408 if(argTemplate.notNil, { func = MIDIValueMatcher(argTemplate, func)});
410 { srcID.notNil && chan.isArray }, {MIDIFuncBothCAMessageMatcher(chan, srcID, func)},
411 { srcID.notNil && chan.notNil }, {MIDIFuncBothMessageMatcher(chan, srcID, func)},
412 { srcID.notNil }, {MIDIFuncSrcMessageMatcher(srcID, func)},
413 { chan.isArray }, {MIDIFuncChanArrayMessageMatcher(chan, func)},
414 { chan.notNil }, {MIDIFuncChanMessageMatcher(chan, func)},
419 typeKey { ^('MIDI ' ++ messageType).asSymbol }
422 // for \touch, \program, \bend
423 MIDIMessageDispatcherNV : MIDIMessageDispatcher {
425 getKeysForFuncProxy {|funcProxy| ^(funcProxy.chan ? (0..15)).asArray;} // chan
427 value {|src, chan, val| active[chan].value(val, chan, src);}
429 // wrapper objects based on arg type and testing requirements
430 wrapFunc {|funcProxy|
431 var func, chan, srcID, argTemplate;
432 func = funcProxy.func;
433 chan = funcProxy.chan;
434 srcID = funcProxy.srcID;
435 argTemplate = funcProxy.argTemplate;
436 if(argTemplate.notNil, { func = MIDIValueMatcher(argTemplate, func)});
438 { srcID.notNil }, {MIDIFuncSrcMessageMatcherNV(srcID, func)},
445 MIDIFunc : AbstractResponderFunc {
446 classvar <>defaultDispatchers;
447 var <chan, <msgNum, <msgType, <argTemplate;
450 defaultDispatchers = IdentityDictionary.new;
451 [\noteOn, \noteOff, \control, \polytouch].do({|type|
452 defaultDispatchers[type] = MIDIMessageDispatcher(type);
454 [\touch, \program, \bend].do({|type|
455 defaultDispatchers[type] = MIDIMessageDispatcherNV(type);
459 *new { arg func, msgNum, chan, msgType, srcID, argTemplate, dispatcher;
460 ^super.new.init(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher ? defaultDispatchers[msgType]);
463 *cc { arg func, ccNum, chan, srcID, argTemplate, dispatcher;
464 ^this.new(func, ccNum, chan, \control, srcID, argTemplate, dispatcher);
467 *noteOn { arg func, noteNum, chan, srcID, argTemplate, dispatcher;
468 ^this.new(func, noteNum, chan, \noteOn, srcID, argTemplate, dispatcher);
471 *noteOff { arg func, noteNum, chan, srcID, argTemplate, dispatcher;
472 ^this.new(func, noteNum, chan, \noteOff, srcID, argTemplate, dispatcher);
475 *polytouch { arg func, noteNum, chan, srcID, argTemplate, dispatcher;
476 ^this.new(func, noteNum, chan, \polytouch, srcID, argTemplate, dispatcher);
479 *touch { arg func, chan, srcID, argTemplate, dispatcher;
480 ^this.new(func, nil, chan, \touch, srcID, argTemplate, dispatcher);
483 *bend { arg func, chan, srcID, argTemplate, dispatcher;
484 ^this.new(func, nil, chan, \bend, srcID, argTemplate, dispatcher);
487 *program { arg func, chan, srcID, argTemplate, dispatcher;
488 ^this.new(func, nil, chan, \program, srcID, argTemplate, dispatcher);
491 init {|argfunc, argmsgNum, argchan, argType, argsrcID, argtempl, argdisp|
492 msgNum = msgNum ? argmsgNum;
493 chan = chan ? argchan;
494 srcID = argsrcID ? srcID;
496 msgType = argType ? msgType;
497 dispatcher = argdisp ? dispatcher;
498 argTemplate = argtempl ? argTemplate;
500 allFuncProxies.add(this);
504 printOn { arg stream; stream << this.class.name << "(" <<* [msgType, msgNum, chan, argTemplate] << ")" }
509 classvar <>all; // same as other def classes, do we need a setter really?
513 all = IdentityDictionary.new;
516 *new { arg key, func, msgNum, chan, msgType, srcID, argTemplate, dispatcher;
517 var res = all.at(key);
519 ^super.new(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher).addToAll(key);
524 res.init(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher ? defaultDispatchers[msgType]);
525 }, { res.init(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher ? defaultDispatchers[msgType]).disable; });
531 *cc { arg key, func, ccNum, chan, srcID, argTemplate, dispatcher;
532 ^this.new(key, func, ccNum, chan, \control, srcID, argTemplate, dispatcher);
535 *noteOn { arg key, func, noteNum, chan, srcID, argTemplate, dispatcher;
536 ^this.new(key, func, noteNum, chan, \noteOn, srcID, argTemplate, dispatcher);
539 *noteOff { arg key, func, noteNum, chan, srcID, argTemplate, dispatcher;
540 ^this.new(key, func, noteNum, chan, \noteOff, srcID, argTemplate, dispatcher);
543 *polytouch { arg key, func, noteNum, chan, srcID, argTemplate, dispatcher;
544 ^this.new(key, func, noteNum, chan, \polytouch, srcID, argTemplate, dispatcher);
547 *touch { arg key, func, chan, srcID, argTemplate, dispatcher;
548 ^this.new(key, func, nil, chan, \touch, srcID, argTemplate, dispatcher);
551 *bend { arg key, func, chan, srcID, argTemplate, dispatcher;
552 ^this.new(key, func, nil, chan, \bend, srcID, argTemplate, dispatcher);
555 *program { arg key, func, chan, srcID, argTemplate, dispatcher;
556 ^this.new(key, func, nil, chan, \program, srcID, argTemplate, dispatcher);
559 addToAll {|argkey| key = argkey; all.put(key, this) }
561 free { all[key] = nil; super.free; }
564 printOn { arg stream; stream << this.class.name << "(" <<* [key, msgType, msgNum, chan, argTemplate] << ")" }
569 // if you need to test for srcID func gets wrapped in this
570 MIDIFuncSrcMessageMatcher : AbstractMessageMatcher {
573 *new {|srcID, func| ^super.new.init(srcID, func);}
575 init {|argsrcID, argfunc| srcID = argsrcID; func = argfunc; }
577 value {|value, num, chan, testSrc|
578 if(srcID == testSrc, {func.value(value, num, chan, testSrc)})
582 // if you need to test for srcID func gets wrapped in this
583 MIDIFuncChanMessageMatcher : AbstractMessageMatcher {
586 *new {|chan, func| ^super.new.init(chan, func);}
588 init {|argchan, argfunc| chan = argchan; func = argfunc; }
590 value {|value, num, testChan, srcID|
591 if(chan == testChan, {func.value(value, num, testChan, srcID)})
595 // if you need to test for chanArray func gets wrapped in this
596 MIDIFuncChanArrayMessageMatcher : AbstractMessageMatcher {
599 *new {|chanArray, func|
601 // lookup bool by index fastest, so construct an Array here
602 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
603 ^super.new.init(chanBools, func);
606 init {|argchanbools, argfunc| chanBools = argchanbools; func = argfunc; }
608 value {|value, num, testChan, srcID|
609 // lookup bool by index fastest
610 if(chanBools[testChan], {func.value(value, num, testChan, srcID)})
614 // version for message types which don't pass a val
615 MIDIFuncSrcMessageMatcherNV : MIDIFuncSrcMessageMatcher {
617 value {|num, chan, testSrc|
618 if(srcID == testSrc, {func.value(num, chan, testSrc)})
622 // if you need to test for chan and srcID func gets wrapped in this
623 MIDIFuncBothMessageMatcher : AbstractMessageMatcher {
626 *new {|chan, srcID, func| ^super.new.init(chan, srcID, func);}
628 init {|argchan, argsrcID, argfunc| chan = argchan; srcID = argsrcID; func = argfunc; }
630 value {|value, num, testChan, testSrc|
631 if(srcID == testSrc and: {chan == testChan}, {func.value(value, num, testChan, testSrc)})
636 // if you need to test for chanArray and srcID func gets wrapped in this
637 MIDIFuncBothCAMessageMatcher : AbstractMessageMatcher {
638 var chanBools, srcID;
640 *new {|chanArray, srcID, func|
642 // lookup bool by index fastest, so construct an Array here
643 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
644 ^super.new.init(chanBools, srcID, func);
647 init {|argbools, argsrcID, argfunc| chanBools = argbools; srcID = argsrcID; func = argfunc; }
649 value {|value, num, testChan, testSrc|
650 if(srcID == testSrc and: {chanBools[testChan]}, {func.value(value, num, testChan, testSrc)})
654 // if you want to test for the actual message value, the func gets wrapped in this
655 MIDIValueMatcher : AbstractMessageMatcher {
658 *new{|argTemplate, func| ^super.new.init(argTemplate, func) }
660 init {|argArgTemplate, argFunc| argTemplate = argArgTemplate; func = argFunc; }
663 if(argTemplate.matchItem(testMsg.first), {func.value(*testMsg)});