2 // contains numerical / value patterns
4 PatternProxy : Pattern {
5 var <source, <pattern, <>envir;
6 var >clock, quant, <>condition=true, reset;
8 // quant new pattern insertion. can be [quant, phase, timingOffset]
9 // in EventPatternProxy it can be [quant, phase, timingOffset, onset]
11 classvar <>defaultQuant, defaultEnvir;
14 ^super.new.source_(source)
17 *default { ^1 } // safe for duration patterns
20 clock { ^clock ? TempoClock.default }
22 quant { ^quant ?? { this.class.defaultQuant } }
23 quant_ { arg val; quant = val }
25 constrainStream { arg stream;
26 if(quant.isNil or: { stream.isNil }) { ^pattern.asStream };
27 ^Pseq([PfinQuant(EmbedOnce(stream), quant, clock), pattern]).asStream
31 var pat = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
32 if (obj.isNil) { pat = this.class.default };
34 source = obj; // keep original here.
38 if(envir.isNil) { envir = this.class.event };
39 ^if(envir[\independent] === true) { (parent:envir) } { envir }
42 convertFunction { arg func;
44 var inval = func.def.prototypeFrame !? { inval = this.defaultEvent };
45 func.value( inval ).embedInStream(inval)
49 *parallelise { arg list; ^Ptuple(list) }
51 pattern_ { arg pat; this.source_(pat) }
53 timingOffset_ { arg val; quant = quant.instill(2, val) }
54 timingOffset { arg val; ^quant.obtain(2) }
55 phase_ { arg val; quant = quant.instill(1, val) }
56 phase { arg val; ^quant.obtain(1) }
57 quantBeat_ { arg val; quant = quant.instill(0, val) }
58 quantBeat { arg val; ^quant.obtain(0) }
61 if(envir.isNil) { this.envir = this.class.event };
62 args.pairsDo { arg key, val; envir.put(key, val) };
65 if(envir.notNil) { args.do { arg key; envir.removeAt(key) } };
69 ^if(envir.notNil) { envir[key] } { nil };
73 if(envir.isNil) { this.envir = this.class.event };
74 args.pairsDo { arg key, val; envir.place(key, val) }; // place is defined in the event.
77 isEventPattern { ^false }
82 ^if(val.notNil) { Pchain(this, val) } { this }.embedInStream
85 embedInStream { arg inval;
87 var outval, count = 0;
90 var resetTest = reset;
91 var stream = pattern.asStream;
94 this.receiveEvent(inval); // used in subclass
98 or: { pat !== pattern and: { test.value(outval, count) } }
104 stream = this.constrainStream(stream);
106 outval = stream.next(inval);
110 inval = outval.yield;
117 ^Proutine { arg inval;
119 var outval, count = 0;
121 var test = condition;
122 var resetTest = reset;
123 var stream = pattern.asStream;
124 var default = this.class.defaultValue;
127 this.receiveEvent(inval); // used in subclass
130 (reset !== resetTest)
131 or: { pat !== pattern and: { test.value(outval, count) } }
137 stream = this.constrainStream(stream);
139 outval = stream.next(inval) ? default;
141 inval = outval.yield;
148 condition = { |val,i| i % n == 0 }
151 reset { reset = reset ? 0 + 1 }
156 { this.clock.schedAbs(quant.nextTimeOnGrid(this.clock), { func.value; nil }) }
159 storeArgs { ^[pattern] }
162 // these following methods are factored out for the benefit of subclasses
163 // they only work for Pdef/Tdef/Pdefn.
172 this.all.do { arg pat; pat.clear }
182 this.class.all.removeAt(this.key);
186 // backward compatibility
187 *basicNew { arg source;
188 ^super.new.init(source)
197 repositoryArgs { ^[this.key, this.source] }
199 *postRepository { arg keys, stream;
200 keys = keys ?? { this.all.keys };
201 stream = stream ? Post;
204 item = this.all[key];
205 if(item.notNil and: { item.source !== this.default }) {
206 stream << item.class.name << "(" <<<* item.repositoryArgs << ")";
207 if(item.envir.notNil and: { item.envir.notEmpty }) {
208 stream << ".set(" <<<* item.envir.asKeyValuePairs << ")"
216 *event { arg proxyClass = PatternProxy;
218 if(defaultEnvir.isNil) {
219 defaultEnvir = Event.default;
220 defaultEnvir.parent = defaultEnvir.parent.copy.putAll(
223 proxyClass: proxyClass,
226 if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x); };
229 place: { |e, key, val|
231 if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x) };
237 event = defaultEnvir.copy;
238 // event[\self] = event; // this risks a crash on storeOn.
246 Pdefn : PatternProxy {
251 all = IdentityDictionary.new;
253 *new { arg key, item;
254 var res = this.at(key);
256 res = super.new(item).prAdd(key);
258 if(item.notNil) { res.source = item }
265 if(envir.isNil) { this.envir = () };
266 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
269 storeArgs { ^[key] } // assume it was created globally
273 all.put(argKey, this);
278 // contains time patterns (tasks)
280 TaskProxy : PatternProxy {
281 var <player, <>playQuant;
282 classvar <>defaultQuant=1.0;
284 storeArgs { ^[source] }
287 pattern = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
288 if (obj.isNil) { pattern = this.class.default; source = obj; };
293 convertFunction { arg func;
295 var inval = func.def.prototypeFrame !? { this.defaultEvent };
296 if(inevent.isNumber or: {inevent.isNil} or: { inval.isNil }) {
299 inevent.copy.parent_(inval);
306 isEventPattern { ^true }
308 *default { ^Pn(this.defaultValue,1) }
310 constrainStream { arg str;
311 ^if(this.quant.notNil and: { str.notNil }) {
313 EmbedOnce(Pconst(thisThread.clock.timeToNextBeat(this.quant), str, 0.001)),
316 } { pattern }.asStream
319 align { arg argQuant;
321 this.source = this.source.copy;
325 ////////// playing interface //////////
327 playOnce { arg argClock, doReset = (false), quant;
328 var clock = argClock ? this.clock;
329 ^PauseStream.new(this.asProtected.asStream).play(clock, doReset, quant ? this.quant)
332 play { arg argClock, doReset=false, quant;
333 playQuant = quant ? this.quant;
335 player = this.playOnce(argClock, doReset, playQuant);
337 // resets when stream has ended or after pause/cmd-period:
338 if (player.streamHasEnded or: { player.wasStopped }) { doReset = true };
340 if(player.isPlaying.not) {
341 player.play(argClock, doReset, playQuant);
343 if (doReset) { player.reset };
348 if(this.isPlaying) { this.play(quant:playQuant) } }
350 ^Pprotect(this, { if(this.player.notNil) { this.player.streamError } })
353 // check playing states:
354 isPlaying { ^player.notNil and: { player.wasStopped.not } }
355 isActive { ^this.isPlaying and: { player.streamHasEnded.not } }
356 hasSource { ^source.notNil }
357 hasEnvir { ^envir.notNil }
358 hasPlayer { ^player.notNil }
359 hasEnded { ^player.isNil or: { player.streamHasEnded } }
360 isPaused { ^player.isNil or: { player.wasStopped } }
361 canPause { ^player.notNil and: { player.canPause } }
363 fork { arg clock, quant, event;
364 ^Routine { this.embedInStream(event) }.play(clock ? thisThread.clock, quant)
367 stop { player.stop; player = nil; }
369 pause { if(player.notNil) { this.sched { player.pause } } }
370 resume { arg clock, quant;
371 player !? { player.resume(clock ? this.clock, quant ? this.quant) }
382 all = IdentityDictionary.new;
385 *new { arg key, item;
386 var res = this.at(key);
388 res = super.new(item).prAdd(key);
390 if(item.notNil) { res.source = item }
400 all.put(argKey, this);
407 // contains event patterns
409 EventPatternProxy : TaskProxy {
411 classvar <>defaultQuant=1.0;
413 storeArgs { ^[source] }
416 if(obj.isKindOf(Function)) // allow functions to be passed in
417 { pattern = PlazyEnvirN(obj) }
419 { pattern = this.class.default }
422 envir !? { pattern = pattern <> envir };
429 this.source = source;
432 *defaultValue { ^Event.silent }
434 embedInStream { arg inval, cleanup;
438 var test = condition;
439 var resetTest = reset;
440 var stream = pattern.asStream;
442 cleanup ?? { cleanup = EventStreamCleanup.new };
445 this.receiveEvent(inval);
447 (reset !== resetTest)
448 or: { pat !== pattern and: { test.value(outval, count) } }
454 // inval is the next event that will be yielded
455 // constrainStream may add some values to it so IT MUST BE YIELDED
456 stream = this.constrainStream(stream, inval, cleanup);
457 cleanup = EventStreamCleanup.new;
459 outval = stream.next(inval);
463 outval = cleanup.update(outval);
464 inval = outval.yield;
465 if(inval.isNil) { ^nil.alwaysYield }
473 ^Proutine { arg inval;
476 var cleanup = EventStreamCleanup.new;
479 var test = condition;
480 var resetTest = reset;
481 var stream = pattern.asStream;
482 var default = this.class.defaultValue;
485 this.receiveEvent(inval);
487 (reset !== resetTest)
488 or: { pat !== pattern and: { test.value(outval, count) } }
494 // inval is the next event that will be yielded
495 // constrainStream may add some values to it so IT MUST BE YIELDED
496 stream = this.constrainStream(stream, inval, cleanup);
497 cleanup = EventStreamCleanup.new;
499 outval = stream.next(inval) ? default;
501 outval = cleanup.update(outval);
502 inval = outval.yield;
509 constrainStream { arg str, inval, cleanup;
510 var delta, tolerance, new;
511 var quantBeat, catchUp, deltaTillCatchUp, forwardTime, quant = this.quant;
514 quantBeat = this.quantBeat ? 0;
515 catchUp = this.outset;
517 delta = thisThread.clock.timeToNextBeat(quant);
518 tolerance = quantBeat % delta % 0.125;
520 deltaTillCatchUp = thisThread.clock.timeToNextBeat(catchUp);
521 new = pattern.asStream;
522 forwardTime = quantBeat - delta + deltaTillCatchUp;
523 delta = new.fastForward(forwardTime, tolerance) + deltaTillCatchUp;
533 Pseq([EmbedOnce(Pfindur(delta, str, tolerance).asStream(cleanup)), new])
538 EmbedOnce(PfadeOut(str, fadeTime, delta, tolerance).asStream(cleanup)),
539 PfadeIn(new, fadeTime, delta, tolerance)
548 *parallelise { arg list; ^Ppar(list) }
550 outset_ { arg val; quant = quant.instill(3, val) }
551 outset { arg val; ^this.quant.obtain(3) }
554 // branching from another thread
556 fork { arg argClock, quant, protoEvent; // usual fork arg order: clock, quant, ...
557 argClock = argClock ? thisThread.clock;
558 ^EventStreamPlayer(this.asStream, protoEvent).play(argClock, true, quant)
561 // playing one instance //
563 playOnce { arg argClock, protoEvent, quant;
564 ^this.fork(argClock ? this.clock, quant ? this.quant, protoEvent)
568 ////////// playing interface //////////
572 play { arg argClock, protoEvent, quant, doReset=false;
573 playQuant = quant ? this.quant;
575 player = EventStreamPlayer(this.asProtected.asStream, protoEvent);
576 player.play(argClock, doReset, playQuant);
578 // resets when stream has ended or after pause/cmd-period:
579 if(player.streamHasEnded or: { player.wasStopped }) { doReset = true };
580 protoEvent !? { player.event = protoEvent };
581 if(player.isPlaying.not) {
582 player.play(argClock, doReset, playQuant);
584 if(doReset) { player.reset };
592 Pdef : EventPatternProxy {
599 *new { arg key, item;
600 var res = this.at(key);
602 res = super.new(item).prAdd(key);
604 if(item.notNil) { res.source = item }
611 if(envir.isNil) { this.envir = () };
612 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
617 all.put(argKey, this);
623 all = IdentityDictionary.new;
624 Class.initClassTree(Event);
627 var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest;
629 embeddingLevel = ~embeddingLevel ? 0; // infinite recursion catch
631 rest = freq.isKindOf(Symbol); // check for outer rests
632 if(rest) { ~freq = freq };
633 pat = (~repository ? all).at(~instrument);
635 if(pat.notNil and: { embeddingLevel < 8 })
637 pat = pat.pattern; // optimization. outer pattern takes care for replacement
638 // preserve information from outer pattern, but not delta.
640 recursionLevel = ~recursionLevel;
641 if(~transparency.isNil or:
642 { ~transparency > (recursionLevel ? 0) }
644 outerEvent = currentEnvironment.copy
646 outerEvent = Event.default;
649 ~recursionLevel = recursionLevel;
653 if(recursionLevel.notNil) {
654 if(recursionLevel > 0) {
655 // in recursion, some inner values have to be overridden
656 instrument = ~instrument;
657 pat = pat.collect { |inval|
659 ~instrument = instrument;
660 ~parent = outerEvent;
661 ~recursionLevel = recursionLevel - 1;
666 // play pattern in the ordinary way
669 } { // avoid recursion, if instrument not set.
670 outerEvent.put(\embeddingLevel, embeddingLevel + 1);
671 outerEvent.parent_(Event.parentEvents.default);
673 // maybe add a Pprotect here.
675 pat = Pfindur(~sustain.value, pat);
676 outerEvent.put(\delta, nil); // block delta modification by Ppar
677 outerEvent.put(\instrument, ~synthDef ? \default);
679 pat.play(thisThread.clock, outerEvent, 0.0);
687 Event.addEventType(\phrase, phraseEventFunc);
694 PbindProxy : Pattern {
695 var <>pairs, <source;
697 *new { arg ... pairs;
698 ^super.newCopyArgs(pairs).init
701 forBy(0, pairs.size-1, 2) { arg i;
702 pairs[i+1] = PatternProxy(pairs[i+1])
704 source = EventPatternProxy(Pbind(*pairs));
706 embedInStream { arg inval;
707 ^source.embedInStream(inval)
710 pairs.pairsDo { |u,x,i| if(u === key) { ^i } }; ^nil
713 pairs.pairsDo { arg key, item; item.quant = val }; // maybe use ref later
716 quant { ^source.quant }
717 envir { ^source.envir }
718 envir_ { arg envir; source.envir_(envir) }
720 at { arg key; var i; i = this.find(key); ^if(i.notNil) { pairs[i+1] } { nil } }
722 // does not yet work with adding arrayed keys/values
723 set { arg ... args; // key, val ...
724 var changedPairs=false, quant;
726 args.pairsDo { |key, val|
736 pairs[i+1].source = val
739 pairs = pairs ++ [key, PatternProxy(val).quant_(quant)];
744 if(changedPairs) { source.source = Pbind(*pairs) };
748 storeArgs { ^pairs.collect(_.source) }
753 *new { arg ... pairs;
755 key = pairs.removeAt(0);
756 pat = super.new(key);
758 if(pairs.isEmpty.not) {
759 if(src.class === PbindProxy) {
763 if(src.isKindOf(Pbind))
765 src.patternpairs.pairsDo { |key, pat|
766 if(pairs.includes(key).not) {
767 pairs = pairs.add(key);
768 pairs = pairs.add(pat);
773 src = PbindProxy.new(*pairs).quant_(pat.quant);
782 storeArgs { ^[key]++pattern.storeArgs }
783 repositoryArgs { ^this.storeArgs }
784 quant_ { arg val; super.quant = val; source.quant = val }
790 // general purpose lookup stream
793 var <>dict, <>which, <>repeats, <>default;
794 *new { arg dict, which, repeats=inf, default;
795 ^super.newCopyArgs(dict, which, repeats, default);
797 storeArgs { ^[dict,which,repeats,default ] }
798 embedInStream { arg inval;
800 keyStream = which.asStream;
802 key = keyStream.next(inval);
803 if(key.isNil) { ^inval };
804 inval = (dict.at(key) ? default).embedInStream(inval);