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 if(recvPort.notNil and: {thisProcess.openUDPPort(recvPort).not}, {
271 Error("Could not open UDP port"+recvPort).throw;
273 argtemplate = argtemplate.collect({|oscArg|
274 if(oscArg.isKindOf(String), {oscArg.asSymbol}, {oscArg}); // match Symbols not Strings
276 argTemplate = argtemplate ? argTemplate;
277 func = argfunc ? func;
278 dispatcher = argdisp ? dispatcher;
280 allFuncProxies.add(this);
283 printOn { arg stream; stream << this.class.name << "(" <<* [path, srcID, argTemplate] << ")" }
292 all = IdentityDictionary.new;
295 *new { arg key, func, path, srcID, recvPort, argTemplate, dispatcher;
296 var res = all.at(key), wasDisabled;
298 ^super.new(func, path, srcID, recvPort, argTemplate, dispatcher).addToAll(key);
301 wasDisabled = res.enabled.not;
304 res.init(func, path, srcID, recvPort, argTemplate, dispatcher);
305 if(wasDisabled, { res.disable; });
315 *newMatching { arg key, func, path, srcID, argTemplate, recvPort;
316 ^this.new(key, func, path, srcID, recvPort, argTemplate, defaultMatchingDispatcher);
319 addToAll {|argkey| key = argkey; all.put(key, this) }
321 free { all[key] = nil; super.free; }
323 printOn { arg stream; stream << this.class.name << "(" <<* [key, path, srcID, argTemplate] << ")" }
326 var objs = all.shallowCopy;
332 // if you need to test for address func gets wrapped in this
333 OSCFuncAddrMessageMatcher : AbstractMessageMatcher {
336 *new {|addr, func| ^super.new.init(addr, func);}
338 init {|argaddr, argfunc| addr = argaddr; func = argfunc; }
340 value {|msg, time, testAddr, recvPort|
341 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)}, {
342 func.value(msg, time, testAddr, recvPort)
347 // if you need to test for recvPort func gets wrapped in this
348 OSCFuncRecvPortMessageMatcher : AbstractMessageMatcher {
351 *new {|recvPort, func| ^super.new.init(recvPort, func);}
353 init {|argrecvPort, argfunc| recvPort = argrecvPort; func = argfunc; }
355 value {|msg, time, addr, testRecvPort|
356 if(testRecvPort == recvPort, {
357 func.value(msg, time, addr, testRecvPort)
362 OSCFuncBothMessageMatcher : AbstractMessageMatcher {
365 *new {|addr, recvPort, func| ^super.new.init(addr, recvPort, func);}
367 init {|argaddr, argrecvPort, argfunc| addr = argaddr; recvPort = argrecvPort; func = argfunc; }
369 value {|msg, time, testAddr, testRecvPort|
370 if(testAddr.addr == addr.addr and: {addr.port.matchItem(testAddr.port)} and: {testRecvPort == recvPort}, {
371 func.value(msg, time, testAddr, testRecvPort)
376 OSCArgsMatcher : AbstractMessageMatcher {
379 *new{|argTemplate, func| ^super.new.init(argTemplate, func) }
381 init {|argArgTemplate, argFunc| argTemplate = argArgTemplate.asArray; func = argFunc; }
383 value {|testMsg, time, addr, recvPort|
384 testMsg[1..].do({|item, i|
385 if(argTemplate[i].matchItem(item).not, {^this});
387 func.value(testMsg, time, addr, recvPort)
391 ///////////////////// MIDI
393 // for \noteOn, \noteOff, \control, \polytouch
394 MIDIMessageDispatcher : AbstractWrappingDispatcher {
397 *new {|messageType| ^super.new.messageType_(messageType) }
399 getKeysForFuncProxy {|funcProxy| ^(funcProxy.msgNum ? (0..127)).asArray;} // noteNum, etc.
401 value {|src, chan, num, val| active[num].value(val, num, chan, src);}
404 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).addFunc(this));
409 MIDIIn.perform(messageType.asSetter, MIDIIn.perform(messageType.asGetter).removeFunc(this));
413 // wrapper objects based on arg type and testing requirements
414 wrapFunc {|funcProxy|
415 var func, chan, srcID, argTemplate;
416 func = funcProxy.func;
417 chan = funcProxy.chan;
418 srcID = funcProxy.srcID;
419 argTemplate = funcProxy.argTemplate;
420 if(argTemplate.notNil, { func = MIDIValueMatcher(argTemplate, func)});
422 { srcID.notNil && chan.isArray }, {MIDIFuncBothCAMessageMatcher(chan, srcID, func)},
423 { srcID.notNil && chan.notNil }, {MIDIFuncBothMessageMatcher(chan, srcID, func)},
424 { srcID.notNil }, {MIDIFuncSrcMessageMatcher(srcID, func)},
425 { chan.isArray }, {MIDIFuncChanArrayMessageMatcher(chan, func)},
426 { chan.notNil }, {MIDIFuncChanMessageMatcher(chan, func)},
431 typeKey { ^('MIDI ' ++ messageType).asSymbol }
434 // for \touch, \program, \bend
435 MIDIMessageDispatcherNV : MIDIMessageDispatcher {
437 getKeysForFuncProxy {|funcProxy| ^(funcProxy.chan ? (0..15)).asArray;} // chan
439 value {|src, chan, val| active[chan].value(val, chan, src);}
441 // wrapper objects based on arg type and testing requirements
442 wrapFunc {|funcProxy|
443 var func, chan, srcID, argTemplate;
444 func = funcProxy.func;
445 chan = funcProxy.chan;
446 srcID = funcProxy.srcID;
447 argTemplate = funcProxy.argTemplate;
448 if(argTemplate.notNil, { func = MIDIValueMatcher(argTemplate, func)});
450 { srcID.notNil }, {MIDIFuncSrcMessageMatcherNV(srcID, func)},
457 MIDIFunc : AbstractResponderFunc {
458 classvar <>defaultDispatchers;
459 var <chan, <msgNum, <msgType, <argTemplate;
462 defaultDispatchers = IdentityDictionary.new;
463 [\noteOn, \noteOff, \control, \polytouch].do({|type|
464 defaultDispatchers[type] = MIDIMessageDispatcher(type);
466 [\touch, \program, \bend].do({|type|
467 defaultDispatchers[type] = MIDIMessageDispatcherNV(type);
471 *new { arg func, msgNum, chan, msgType, srcID, argTemplate, dispatcher;
472 ^super.new.init(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher ? defaultDispatchers[msgType]);
475 *cc { arg func, ccNum, chan, srcID, argTemplate, dispatcher;
476 ^this.new(func, ccNum, chan, \control, srcID, argTemplate, dispatcher);
479 *noteOn { arg func, noteNum, chan, srcID, argTemplate, dispatcher;
480 ^this.new(func, noteNum, chan, \noteOn, srcID, argTemplate, dispatcher);
483 *noteOff { arg func, noteNum, chan, srcID, argTemplate, dispatcher;
484 ^this.new(func, noteNum, chan, \noteOff, srcID, argTemplate, dispatcher);
487 *polytouch { arg func, noteNum, chan, srcID, argTemplate, dispatcher;
488 ^this.new(func, noteNum, chan, \polytouch, srcID, argTemplate, dispatcher);
491 *touch { arg func, chan, srcID, argTemplate, dispatcher;
492 ^this.new(func, nil, chan, \touch, srcID, argTemplate, dispatcher);
495 *bend { arg func, chan, srcID, argTemplate, dispatcher;
496 ^this.new(func, nil, chan, \bend, srcID, argTemplate, dispatcher);
499 *program { arg func, chan, srcID, argTemplate, dispatcher;
500 ^this.new(func, nil, chan, \program, srcID, argTemplate, dispatcher);
503 init {|argfunc, argmsgNum, argchan, argType, argsrcID, argtempl, argdisp|
504 msgNum = argmsgNum ? msgNum;
505 chan = argchan ? chan;
506 srcID = argsrcID ? srcID;
507 func = argfunc ? func;
508 msgType = argType ? msgType;
509 dispatcher = argdisp ? dispatcher;
510 argTemplate = argtempl ? argTemplate;
512 allFuncProxies.add(this);
516 printOn { arg stream; stream << this.class.name << "(" <<* [msgType, msgNum, chan, argTemplate] << ")" }
521 classvar <>all; // same as other def classes, do we need a setter really?
525 all = IdentityDictionary.new;
528 *new { arg key, func, msgNum, chan, msgType, srcID, argTemplate, dispatcher;
529 var res = all.at(key);
531 ^super.new(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher).addToAll(key);
536 res.init(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher ? defaultDispatchers[msgType]);
537 }, { res.init(func, msgNum, chan, msgType, srcID, argTemplate, dispatcher ? defaultDispatchers[msgType]).disable; });
543 *cc { arg key, func, ccNum, chan, srcID, argTemplate, dispatcher;
544 ^this.new(key, func, ccNum, chan, \control, srcID, argTemplate, dispatcher);
547 *noteOn { arg key, func, noteNum, chan, srcID, argTemplate, dispatcher;
548 ^this.new(key, func, noteNum, chan, \noteOn, srcID, argTemplate, dispatcher);
551 *noteOff { arg key, func, noteNum, chan, srcID, argTemplate, dispatcher;
552 ^this.new(key, func, noteNum, chan, \noteOff, srcID, argTemplate, dispatcher);
555 *polytouch { arg key, func, noteNum, chan, srcID, argTemplate, dispatcher;
556 ^this.new(key, func, noteNum, chan, \polytouch, srcID, argTemplate, dispatcher);
559 *touch { arg key, func, chan, srcID, argTemplate, dispatcher;
560 ^this.new(key, func, nil, chan, \touch, srcID, argTemplate, dispatcher);
563 *bend { arg key, func, chan, srcID, argTemplate, dispatcher;
564 ^this.new(key, func, nil, chan, \bend, srcID, argTemplate, dispatcher);
567 *program { arg key, func, chan, srcID, argTemplate, dispatcher;
568 ^this.new(key, func, nil, chan, \program, srcID, argTemplate, dispatcher);
571 addToAll {|argkey| key = argkey; all.put(key, this) }
573 free { all[key] = nil; super.free; }
576 printOn { arg stream; stream << this.class.name << "(" <<* [key, msgType, msgNum, chan, argTemplate] << ")" }
579 var objs = all.shallowCopy;
585 // if you need to test for srcID func gets wrapped in this
586 MIDIFuncSrcMessageMatcher : AbstractMessageMatcher {
589 *new {|srcID, func| ^super.new.init(srcID, func);}
591 init {|argsrcID, argfunc| srcID = argsrcID; func = argfunc; }
593 value {|value, num, chan, testSrc|
594 if(srcID == testSrc, {func.value(value, num, chan, testSrc)})
598 // if you need to test for srcID func gets wrapped in this
599 MIDIFuncChanMessageMatcher : AbstractMessageMatcher {
602 *new {|chan, func| ^super.new.init(chan, func);}
604 init {|argchan, argfunc| chan = argchan; func = argfunc; }
606 value {|value, num, testChan, srcID|
607 if(chan == testChan, {func.value(value, num, testChan, srcID)})
611 // if you need to test for chanArray func gets wrapped in this
612 MIDIFuncChanArrayMessageMatcher : AbstractMessageMatcher {
615 *new {|chanArray, func|
617 // lookup bool by index fastest, so construct an Array here
618 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
619 ^super.new.init(chanBools, func);
622 init {|argchanbools, argfunc| chanBools = argchanbools; func = argfunc; }
624 value {|value, num, testChan, srcID|
625 // lookup bool by index fastest
626 if(chanBools[testChan], {func.value(value, num, testChan, srcID)})
630 // version for message types which don't pass a val
631 MIDIFuncSrcMessageMatcherNV : MIDIFuncSrcMessageMatcher {
633 value {|num, chan, testSrc|
634 if(srcID == testSrc, {func.value(num, chan, testSrc)})
638 // if you need to test for chan and srcID func gets wrapped in this
639 MIDIFuncBothMessageMatcher : AbstractMessageMatcher {
642 *new {|chan, srcID, func| ^super.new.init(chan, srcID, func);}
644 init {|argchan, argsrcID, argfunc| chan = argchan; srcID = argsrcID; func = argfunc; }
646 value {|value, num, testChan, testSrc|
647 if(srcID == testSrc and: {chan == testChan}, {func.value(value, num, testChan, testSrc)})
652 // if you need to test for chanArray and srcID func gets wrapped in this
653 MIDIFuncBothCAMessageMatcher : AbstractMessageMatcher {
654 var chanBools, srcID;
656 *new {|chanArray, srcID, func|
658 // lookup bool by index fastest, so construct an Array here
659 chanBools = Array.fill(16, {|i| chanArray.includes(i) });
660 ^super.new.init(chanBools, srcID, func);
663 init {|argbools, argsrcID, argfunc| chanBools = argbools; srcID = argsrcID; func = argfunc; }
665 value {|value, num, testChan, testSrc|
666 if(srcID == testSrc and: {chanBools[testChan]}, {func.value(value, num, testChan, testSrc)})
670 // if you want to test for the actual message value, the func gets wrapped in this
671 MIDIValueMatcher : AbstractMessageMatcher {
674 *new{|argTemplate, func| ^super.new.init(argTemplate, func) }
676 init {|argArgTemplate, argFunc| argTemplate = argArgTemplate; func = argFunc; }
679 if(argTemplate.matchItem(testMsg.first), {func.value(*testMsg)});