1 // contains numerical / value patterns
3 PatternProxy : Pattern {
4 var <source, <pattern, <>envir;
5 var >clock, quant, <>condition=true, reset;
7 // quant new pattern insertion. can be [quant, phase, timingOffset]
8 // in EventPatternProxy it can be [quant, phase, timingOffset, onset]
10 classvar <>defaultQuant, defaultEnvir;
13 ^super.new.source_(source)
16 *default { ^1 } // safe for duration patterns
19 clock { ^clock ? TempoClock.default }
21 quant { ^quant ?? { this.class.defaultQuant } }
22 quant_ { arg val; quant = val }
24 constrainStream { arg stream;
25 if(quant.isNil or: { stream.isNil }) { ^pattern.asStream };
26 ^Pseq([PfinQuant(EmbedOnce(stream), quant, clock), pattern]).asStream
30 var pat = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
31 if (obj.isNil) { pat = this.class.default };
33 source = obj; // keep original here.
36 setSourceLikeInPbind { arg obj;
37 var pat = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
38 if (obj.isNil) { pat = this.class.default };
39 pattern = pat.fin(inf);
40 source = obj; // keep original here.
44 if(envir.isNil) { envir = this.class.event };
45 ^if(envir[\independent] === true) { (parent:envir) } { envir }
48 convertFunction { arg func;
50 var inval = func.def.prototypeFrame !? { inval = this.defaultEvent };
51 func.value( inval ).embedInStream(inval)
55 *parallelise { arg list; ^Ptuple(list) }
57 pattern_ { arg pat; this.source_(pat) }
59 timingOffset_ { arg val; quant = quant.instill(2, val) }
60 timingOffset { arg val; ^quant.obtain(2) }
61 phase_ { arg val; quant = quant.instill(1, val) }
62 phase { arg val; ^quant.obtain(1) }
63 quantBeat_ { arg val; quant = quant.instill(0, val) }
64 quantBeat { arg val; ^quant.obtain(0) }
67 if(envir.isNil) { this.envir = this.class.event };
68 args.pairsDo { arg key, val; envir.put(key, val) };
72 if(envir.notNil) { args.do { arg key; envir.removeAt(key) } };
76 ^if(envir.notNil) { envir[key] } { nil };
80 if(envir.isNil) { this.envir = this.class.event };
81 args.pairsDo { arg key, val; envir.place(key, val) }; // place is defined in the event.
84 isEventPattern { ^false }
89 ^if(val.notNil) { Pchain(this, val) } { this }.embedInStream
92 embedInStream { arg inval;
94 var outval, count = 0;
97 var resetTest = reset;
98 var stream = pattern.asStream;
101 this.receiveEvent(inval); // used in subclass
104 (reset !== resetTest)
105 or: { pat !== pattern and: { test.value(outval, count) } }
111 stream = this.constrainStream(stream);
113 outval = stream.next(inval);
117 inval = outval.yield;
126 var outval, count = 0;
128 var test = condition;
129 var resetTest = reset;
130 var stream = pattern.asStream;
131 var default = this.class.defaultValue;
134 this.receiveEvent(inval); // used in subclass
137 (reset !== resetTest)
138 or: { pat !== pattern and: { test.value(outval, count) } }
144 stream = this.constrainStream(stream);
146 outval = stream.next(inval) ? default;
148 inval = outval.yield;
155 condition = { |val,i| i % n == 0 }
158 reset { reset = reset ? 0 + 1 }
163 { this.clock.schedAbs(quant.nextTimeOnGrid(this.clock), { func.value; nil }) }
166 storeArgs { ^[pattern] }
169 // these following methods are factored out for the benefit of subclasses
170 // they only work for Pdef/Tdef/Pdefn.
179 this.all.do { arg pat; pat.clear }
189 if(this.class.hasGlobalDictionary) { this.class.all.removeAt(this.key) };
193 // backward compatibility
194 *basicNew { arg source;
195 ^super.new.init(source)
201 ^if(this.hasGlobalDictionary) { this.all.at(key) } { nil }
204 repositoryArgs { ^[this.key, this.source] }
206 *postRepository { arg keys, stream;
207 if(this.hasGlobalDictionary.not) { Error("This class has no global repository.").throw };
208 keys = keys ?? { this.all.keys };
209 stream = stream ? Post;
212 item = this.all[key];
213 if(item.notNil and: { item.source !== this.default }) {
214 stream << item.class.name << "(" <<<* item.repositoryArgs << ")";
215 if(item.envir.notNil and: { item.envir.notEmpty }) {
216 stream << ".set(" <<<* item.envir.asKeyValuePairs << ")"
224 *event { arg proxyClass = PatternProxy;
226 if(defaultEnvir.isNil) {
227 defaultEnvir = Event.default;
228 defaultEnvir.parent = defaultEnvir.parent.copy.putAll(
231 proxyClass: proxyClass,
234 if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x); };
237 place: { |e, key, val|
239 if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x) };
245 event = defaultEnvir.copy;
246 // event[\self] = event; // this risks a crash on storeOn.
250 *hasGlobalDictionary { ^false }
254 Pdefn : PatternProxy {
259 all = IdentityDictionary.new;
261 *new { arg key, item;
262 var res = this.at(key);
264 res = super.new(item).prAdd(key);
266 if(item.notNil) { res.source = item }
273 if(envir.isNil) { this.envir = () };
274 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
277 storeArgs { ^[key] } // assume it was created globally
281 all.put(argKey, this);
284 *hasGlobalDictionary { ^true }
289 // contains time patterns (tasks)
291 TaskProxy : PatternProxy {
292 var <player, <>playQuant;
293 classvar <>defaultQuant=1.0;
295 storeArgs { ^[source] }
298 pattern = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
299 if (obj.isNil) { pattern = this.class.default; source = obj; };
304 convertFunction { arg func;
306 var inval = func.def.prototypeFrame !? { this.defaultEvent };
307 if(inevent.isNumber or: {inevent.isNil} or: { inval.isNil }) {
310 inevent.copy.parent_(inval);
317 isEventPattern { ^true }
319 *default { ^Pn(this.defaultValue, 1) }
321 constrainStream { arg str;
322 ^if(this.quant.notNil and: { str.notNil }) {
324 EmbedOnce(Pconst(thisThread.clock.timeToNextBeat(this.quant), str, 0.001)),
327 } { pattern }.asStream
330 align { arg argQuant;
332 this.source = this.source.copy;
336 ////////// playing interface //////////
338 playOnce { arg argClock, doReset = (false), quant;
339 var clock = argClock ? this.clock;
340 ^PauseStream.new(this.asProtected.asStream).play(clock, doReset, quant ? this.quant)
343 play { arg argClock, doReset=false, quant;
344 playQuant = quant ? this.quant;
346 player = this.playOnce(argClock, doReset, playQuant);
348 // resets when stream has ended or after pause/cmd-period:
349 if (player.streamHasEnded or: { player.wasStopped }) { doReset = true };
351 if(player.isPlaying.not) {
352 player.play(argClock, doReset, playQuant);
354 if (doReset) { player.reset };
359 if(this.isPlaying) { this.play(quant:playQuant) } }
361 ^Pprotect(this, { if(this.player.notNil) { this.player.streamError } })
364 // check playing states:
365 isPlaying { ^player.notNil and: { player.wasStopped.not } }
366 isActive { ^this.isPlaying and: { player.streamHasEnded.not } }
367 hasSource { ^source.notNil }
368 hasEnvir { ^envir.notNil }
369 hasPlayer { ^player.notNil }
370 hasEnded { ^player.isNil or: { player.streamHasEnded } }
371 isPaused { ^player.isNil or: { player.wasStopped } }
372 canPause { ^player.notNil and: { player.canPause } }
374 fork { arg clock, quant, event;
375 ^Routine { this.embedInStream(event) }.play(clock ? thisThread.clock, quant)
378 stop { player.stop; player = nil; }
380 pause { if(player.notNil) { this.sched { player.pause } } }
381 resume { arg clock, quant;
382 player !? { player.resume(clock ? this.clock, quant ? this.quant) }
393 all = IdentityDictionary.new;
396 *new { arg key, item;
397 var res = this.at(key);
399 res = super.new(item).prAdd(key);
401 if(item.notNil) { res.source = item }
411 all.put(argKey, this);
414 *hasGlobalDictionary { ^true }
421 // contains event patterns
423 EventPatternProxy : TaskProxy {
425 classvar <>defaultQuant=1.0;
427 storeArgs { ^[source] }
430 if(obj.isKindOf(Function)) // allow functions to be passed in
431 { pattern = PlazyEnvirN(obj) }
433 { pattern = this.class.default }
436 envir !? { pattern = pattern <> envir };
443 this.source = source;
446 *defaultValue { ^Event.silent }
448 embedInStream { arg inval, cleanup;
452 var test = condition;
453 var resetTest = reset;
454 var stream = pattern.asStream;
456 cleanup ?? { cleanup = EventStreamCleanup.new };
459 this.receiveEvent(inval);
461 (reset !== resetTest)
462 or: { pat !== pattern and: { test.value(outval, count) } }
468 // inval is the next event that will be yielded
469 // constrainStream may add some values to it so IT MUST BE YIELDED
470 stream = this.constrainStream(stream, inval, cleanup);
471 cleanup = EventStreamCleanup.new;
473 outval = stream.next(inval);
477 outval = cleanup.update(outval);
478 inval = outval.yield;
479 if(inval.isNil) { ^nil.alwaysYield }
490 var cleanup = EventStreamCleanup.new;
493 var test = condition;
494 var resetTest = reset;
495 var stream = pattern.asStream;
496 var default = this.class.defaultValue;
499 this.receiveEvent(inval);
501 (reset !== resetTest)
502 or: { pat !== pattern and: { test.value(outval, count) } }
508 // inval is the next event that will be yielded
509 // constrainStream may add some values to it so IT MUST BE YIELDED
510 stream = this.constrainStream(stream, inval, cleanup);
511 cleanup = EventStreamCleanup.new;
513 outval = stream.next(inval) ? default;
515 outval = cleanup.update(outval);
516 inval = outval.yield;
523 constrainStream { arg str, inval, cleanup;
524 var delta, tolerance, new;
525 var quantBeat, catchUp, deltaTillCatchUp, forwardTime, quant = this.quant;
528 quantBeat = this.quantBeat ? 0;
529 catchUp = this.outset;
531 delta = thisThread.clock.timeToNextBeat(quant);
532 tolerance = quantBeat % delta % 0.125;
534 deltaTillCatchUp = thisThread.clock.timeToNextBeat(catchUp);
535 new = pattern.asStream;
536 forwardTime = quantBeat - delta + deltaTillCatchUp;
537 delta = new.fastForward(forwardTime, tolerance) + deltaTillCatchUp;
547 Pseq([EmbedOnce(Pfindur(delta, str, tolerance).asStream(cleanup)), new])
552 EmbedOnce(PfadeOut(str, fadeTime, delta, tolerance).asStream(cleanup)),
553 PfadeIn(new, fadeTime, delta, tolerance)
562 *parallelise { arg list; ^Ppar(list) }
564 outset_ { arg val; quant = quant.instill(3, val) }
565 outset { arg val; ^this.quant.obtain(3) }
568 // branching from another thread
570 fork { arg argClock, quant, protoEvent; // usual fork arg order: clock, quant, ...
571 argClock = argClock ? thisThread.clock;
572 ^EventStreamPlayer(this.asStream, protoEvent).play(argClock, true, quant)
575 // playing one instance //
577 playOnce { arg argClock, protoEvent, quant;
578 ^this.fork(argClock ? this.clock, quant ? this.quant, protoEvent)
582 ////////// playing interface //////////
586 play { arg argClock, protoEvent, quant, doReset=false;
587 playQuant = quant ? this.quant;
589 player = EventStreamPlayer(this.asProtected.asStream, protoEvent);
590 player.play(argClock, doReset, playQuant);
592 // resets when stream has ended or after pause/cmd-period:
593 if(player.streamHasEnded or: { player.wasStopped }) { doReset = true };
594 protoEvent !? { player.event = protoEvent };
595 if(player.isPlaying.not) {
596 player.play(argClock, doReset, playQuant);
598 if(doReset) { player.reset };
606 Pdef : EventPatternProxy {
613 *new { arg key, item;
614 var res = this.at(key);
616 res = super.new(item).prAdd(key);
618 if(item.notNil) { res.source = item }
625 if(envir.isNil) { this.envir = () };
626 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
631 all.put(argKey, this);
634 *hasGlobalDictionary { ^true }
639 all = IdentityDictionary.new;
640 Class.initClassTree(Event);
643 var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest;
645 embeddingLevel = ~embeddingLevel ? 0; // infinite recursion catch
647 rest = freq.isKindOf(Symbol); // check for outer rests
648 if(rest) { ~freq = freq };
649 pat = (~repository ? all).at(~instrument);
651 if(pat.notNil and: { embeddingLevel < 8 })
653 pat = pat.pattern; // optimization. outer pattern takes care for replacement
654 // preserve information from outer pattern, but not delta.
656 recursionLevel = ~recursionLevel;
657 if(~transparency.isNil or:
658 { ~transparency > (recursionLevel ? 0) }
660 outerEvent = currentEnvironment.copy
662 outerEvent = Event.default;
665 ~recursionLevel = recursionLevel;
669 if(recursionLevel.notNil) {
670 if(recursionLevel > 0) {
671 // in recursion, some inner values have to be overridden
672 instrument = ~instrument;
673 pat = pat.collect { |inval|
675 ~instrument = instrument;
676 ~parent = outerEvent;
677 ~recursionLevel = recursionLevel - 1;
682 // play pattern in the ordinary way
685 } { // avoid recursion, if instrument not set.
686 outerEvent.put(\embeddingLevel, embeddingLevel + 1);
687 outerEvent.parent_(Event.parentEvents.default);
689 // maybe add a Pprotect here.
691 pat = Pfindur(~sustain.value, pat);
692 outerEvent.put(\delta, nil); // block delta modification by Ppar
693 outerEvent.put(\instrument, ~synthDef ? \default);
695 pat.play(thisThread.clock, outerEvent, 0.0);
703 Event.addEventType(\phrase, phraseEventFunc);
710 PbindProxy : Pattern {
711 var <>pairs, <source;
713 *new { arg ... pairs;
714 ^super.newCopyArgs(pairs).init
717 forBy(0, pairs.size-1, 2) { arg i;
718 pairs[i+1] = PatternProxy(pairs[i+1])
720 source = EventPatternProxy(Pbind(*pairs));
722 embedInStream { arg inval;
723 ^source.embedInStream(inval)
726 pairs.pairsDo { |u,x,i| if(u === key) { ^i } }; ^nil
729 pairs.pairsDo { arg key, item; item.quant = val }; // maybe use ref later
732 quant { ^source.quant }
733 envir { ^source.envir }
734 envir_ { arg envir; source.envir_(envir) }
736 at { arg key; var i; i = this.find(key); ^if(i.notNil) { pairs[i+1] } { nil } }
738 // does not yet work with adding arrayed keys/values
739 set { arg ... args; // key, val ...
740 var changedPairs=false, quant;
742 args.pairsDo { |key, val|
752 pairs[i+1].setSourceLikeInPbind(val)
755 pairs = pairs ++ [key, PatternProxy.new.setSourceLikeInPbind(val).quant_(quant)];
756 // fin(inf) is a way to stream symbols endlessly
761 if(changedPairs) { source.source = Pbind(*pairs) };
766 var result = Array(pairs.size);
767 pairs.pairsDo { |key, value|
768 result.add(key).add(value.source)
776 *new { arg ... pairs;
778 key = pairs.removeAt(0);
779 pat = super.new(key);
781 if(pairs.isEmpty.not) {
782 if(src.class === PbindProxy) {
786 if(src.isKindOf(Pbind))
788 src.patternpairs.pairsDo { |key, pat|
789 if(pairs.includes(key).not) {
790 pairs = pairs.add(key);
791 pairs = pairs.add(pat);
796 src = PbindProxy.new(*pairs).quant_(pat.quant);
805 storeArgs { ^[key]++pattern.storeArgs }
806 repositoryArgs { ^this.storeArgs }
807 quant_ { arg val; super.quant = val; source.quant = val }
809 *hasGlobalDictionary { ^true }
817 // general purpose lookup stream
820 var <>dict, <>which, <>repeats, <>default;
821 *new { arg dict, which, repeats=inf, default;
822 ^super.newCopyArgs(dict, which, repeats, default);
824 storeArgs { ^[dict,which,repeats,default ] }
825 embedInStream { arg inval;
827 keyStream = which.asStream;
828 repeats.value(inval).do({
829 key = keyStream.next(inval);
830 if(key.isNil) { ^inval };
831 inval = (dict.at(key) ? default).embedInStream(inval);