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 this.class.all.removeAt(this.key);
193 // backward compatibility
194 *basicNew { arg source;
195 ^super.new.init(source)
204 repositoryArgs { ^[this.key, this.source] }
206 *postRepository { arg keys, stream;
207 keys = keys ?? { this.all.keys };
208 stream = stream ? Post;
211 item = this.all[key];
212 if(item.notNil and: { item.source !== this.default }) {
213 stream << item.class.name << "(" <<<* item.repositoryArgs << ")";
214 if(item.envir.notNil and: { item.envir.notEmpty }) {
215 stream << ".set(" <<<* item.envir.asKeyValuePairs << ")"
223 *event { arg proxyClass = PatternProxy;
225 if(defaultEnvir.isNil) {
226 defaultEnvir = Event.default;
227 defaultEnvir.parent = defaultEnvir.parent.copy.putAll(
230 proxyClass: proxyClass,
233 if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x); };
236 place: { |e, key, val|
238 if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x) };
244 event = defaultEnvir.copy;
245 // event[\self] = event; // this risks a crash on storeOn.
253 Pdefn : PatternProxy {
258 all = IdentityDictionary.new;
260 *new { arg key, item;
261 var res = this.at(key);
263 res = super.new(item).prAdd(key);
265 if(item.notNil) { res.source = item }
272 if(envir.isNil) { this.envir = () };
273 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
276 storeArgs { ^[key] } // assume it was created globally
280 all.put(argKey, this);
285 // contains time patterns (tasks)
287 TaskProxy : PatternProxy {
288 var <player, <>playQuant;
289 classvar <>defaultQuant=1.0;
291 storeArgs { ^[source] }
294 pattern = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
295 if (obj.isNil) { pattern = this.class.default; source = obj; };
300 convertFunction { arg func;
302 var inval = func.def.prototypeFrame !? { this.defaultEvent };
303 if(inevent.isNumber or: {inevent.isNil} or: { inval.isNil }) {
306 inevent.copy.parent_(inval);
313 isEventPattern { ^true }
315 *default { ^Pn(this.defaultValue, 1) }
317 constrainStream { arg str;
318 ^if(this.quant.notNil and: { str.notNil }) {
320 EmbedOnce(Pconst(thisThread.clock.timeToNextBeat(this.quant), str, 0.001)),
323 } { pattern }.asStream
326 align { arg argQuant;
328 this.source = this.source.copy;
332 ////////// playing interface //////////
334 playOnce { arg argClock, doReset = (false), quant;
335 var clock = argClock ? this.clock;
336 ^PauseStream.new(this.asProtected.asStream).play(clock, doReset, quant ? this.quant)
339 play { arg argClock, doReset=false, quant;
340 playQuant = quant ? this.quant;
342 player = this.playOnce(argClock, doReset, playQuant);
344 // resets when stream has ended or after pause/cmd-period:
345 if (player.streamHasEnded or: { player.wasStopped }) { doReset = true };
347 if(player.isPlaying.not) {
348 player.play(argClock, doReset, playQuant);
350 if (doReset) { player.reset };
355 if(this.isPlaying) { this.play(quant:playQuant) } }
357 ^Pprotect(this, { if(this.player.notNil) { this.player.streamError } })
360 // check playing states:
361 isPlaying { ^player.notNil and: { player.wasStopped.not } }
362 isActive { ^this.isPlaying and: { player.streamHasEnded.not } }
363 hasSource { ^source.notNil }
364 hasEnvir { ^envir.notNil }
365 hasPlayer { ^player.notNil }
366 hasEnded { ^player.isNil or: { player.streamHasEnded } }
367 isPaused { ^player.isNil or: { player.wasStopped } }
368 canPause { ^player.notNil and: { player.canPause } }
370 fork { arg clock, quant, event;
371 ^Routine { this.embedInStream(event) }.play(clock ? thisThread.clock, quant)
374 stop { player.stop; player = nil; }
376 pause { if(player.notNil) { this.sched { player.pause } } }
377 resume { arg clock, quant;
378 player !? { player.resume(clock ? this.clock, quant ? this.quant) }
389 all = IdentityDictionary.new;
392 *new { arg key, item;
393 var res = this.at(key);
395 res = super.new(item).prAdd(key);
397 if(item.notNil) { res.source = item }
407 all.put(argKey, this);
414 // contains event patterns
416 EventPatternProxy : TaskProxy {
418 classvar <>defaultQuant=1.0;
420 storeArgs { ^[source] }
423 if(obj.isKindOf(Function)) // allow functions to be passed in
424 { pattern = PlazyEnvirN(obj) }
426 { pattern = this.class.default }
429 envir !? { pattern = pattern <> envir };
436 this.source = source;
439 *defaultValue { ^Event.silent }
441 embedInStream { arg inval, cleanup;
445 var test = condition;
446 var resetTest = reset;
447 var stream = pattern.asStream;
449 cleanup ?? { cleanup = EventStreamCleanup.new };
452 this.receiveEvent(inval);
454 (reset !== resetTest)
455 or: { pat !== pattern and: { test.value(outval, count) } }
461 // inval is the next event that will be yielded
462 // constrainStream may add some values to it so IT MUST BE YIELDED
463 stream = this.constrainStream(stream, inval, cleanup);
464 cleanup = EventStreamCleanup.new;
466 outval = stream.next(inval);
470 outval = cleanup.update(outval);
471 inval = outval.yield;
472 if(inval.isNil) { ^nil.alwaysYield }
483 var cleanup = EventStreamCleanup.new;
486 var test = condition;
487 var resetTest = reset;
488 var stream = pattern.asStream;
489 var default = this.class.defaultValue;
492 this.receiveEvent(inval);
494 (reset !== resetTest)
495 or: { pat !== pattern and: { test.value(outval, count) } }
501 // inval is the next event that will be yielded
502 // constrainStream may add some values to it so IT MUST BE YIELDED
503 stream = this.constrainStream(stream, inval, cleanup);
504 cleanup = EventStreamCleanup.new;
506 outval = stream.next(inval) ? default;
508 outval = cleanup.update(outval);
509 inval = outval.yield;
516 constrainStream { arg str, inval, cleanup;
517 var delta, tolerance, new;
518 var quantBeat, catchUp, deltaTillCatchUp, forwardTime, quant = this.quant;
521 quantBeat = this.quantBeat ? 0;
522 catchUp = this.outset;
524 delta = thisThread.clock.timeToNextBeat(quant);
525 tolerance = quantBeat % delta % 0.125;
527 deltaTillCatchUp = thisThread.clock.timeToNextBeat(catchUp);
528 new = pattern.asStream;
529 forwardTime = quantBeat - delta + deltaTillCatchUp;
530 delta = new.fastForward(forwardTime, tolerance) + deltaTillCatchUp;
540 Pseq([EmbedOnce(Pfindur(delta, str, tolerance).asStream(cleanup)), new])
545 EmbedOnce(PfadeOut(str, fadeTime, delta, tolerance).asStream(cleanup)),
546 PfadeIn(new, fadeTime, delta, tolerance)
555 *parallelise { arg list; ^Ppar(list) }
557 outset_ { arg val; quant = quant.instill(3, val) }
558 outset { arg val; ^this.quant.obtain(3) }
561 // branching from another thread
563 fork { arg argClock, quant, protoEvent; // usual fork arg order: clock, quant, ...
564 argClock = argClock ? thisThread.clock;
565 ^EventStreamPlayer(this.asStream, protoEvent).play(argClock, true, quant)
568 // playing one instance //
570 playOnce { arg argClock, protoEvent, quant;
571 ^this.fork(argClock ? this.clock, quant ? this.quant, protoEvent)
575 ////////// playing interface //////////
579 play { arg argClock, protoEvent, quant, doReset=false;
580 playQuant = quant ? this.quant;
582 player = EventStreamPlayer(this.asProtected.asStream, protoEvent);
583 player.play(argClock, doReset, playQuant);
585 // resets when stream has ended or after pause/cmd-period:
586 if(player.streamHasEnded or: { player.wasStopped }) { doReset = true };
587 protoEvent !? { player.event = protoEvent };
588 if(player.isPlaying.not) {
589 player.play(argClock, doReset, playQuant);
591 if(doReset) { player.reset };
599 Pdef : EventPatternProxy {
606 *new { arg key, item;
607 var res = this.at(key);
609 res = super.new(item).prAdd(key);
611 if(item.notNil) { res.source = item }
618 if(envir.isNil) { this.envir = () };
619 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
624 all.put(argKey, this);
630 all = IdentityDictionary.new;
631 Class.initClassTree(Event);
634 var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest;
636 embeddingLevel = ~embeddingLevel ? 0; // infinite recursion catch
638 rest = freq.isKindOf(Symbol); // check for outer rests
639 if(rest) { ~freq = freq };
640 pat = (~repository ? all).at(~instrument);
642 if(pat.notNil and: { embeddingLevel < 8 })
644 pat = pat.pattern; // optimization. outer pattern takes care for replacement
645 // preserve information from outer pattern, but not delta.
647 recursionLevel = ~recursionLevel;
648 if(~transparency.isNil or:
649 { ~transparency > (recursionLevel ? 0) }
651 outerEvent = currentEnvironment.copy
653 outerEvent = Event.default;
656 ~recursionLevel = recursionLevel;
660 if(recursionLevel.notNil) {
661 if(recursionLevel > 0) {
662 // in recursion, some inner values have to be overridden
663 instrument = ~instrument;
664 pat = pat.collect { |inval|
666 ~instrument = instrument;
667 ~parent = outerEvent;
668 ~recursionLevel = recursionLevel - 1;
673 // play pattern in the ordinary way
676 } { // avoid recursion, if instrument not set.
677 outerEvent.put(\embeddingLevel, embeddingLevel + 1);
678 outerEvent.parent_(Event.parentEvents.default);
680 // maybe add a Pprotect here.
682 pat = Pfindur(~sustain.value, pat);
683 outerEvent.put(\delta, nil); // block delta modification by Ppar
684 outerEvent.put(\instrument, ~synthDef ? \default);
686 pat.play(thisThread.clock, outerEvent, 0.0);
694 Event.addEventType(\phrase, phraseEventFunc);
701 PbindProxy : Pattern {
702 var <>pairs, <source;
704 *new { arg ... pairs;
705 ^super.newCopyArgs(pairs).init
708 forBy(0, pairs.size-1, 2) { arg i;
709 pairs[i+1] = PatternProxy(pairs[i+1])
711 source = EventPatternProxy(Pbind(*pairs));
713 embedInStream { arg inval;
714 ^source.embedInStream(inval)
717 pairs.pairsDo { |u,x,i| if(u === key) { ^i } }; ^nil
720 pairs.pairsDo { arg key, item; item.quant = val }; // maybe use ref later
723 quant { ^source.quant }
724 envir { ^source.envir }
725 envir_ { arg envir; source.envir_(envir) }
727 at { arg key; var i; i = this.find(key); ^if(i.notNil) { pairs[i+1] } { nil } }
729 // does not yet work with adding arrayed keys/values
730 set { arg ... args; // key, val ...
731 var changedPairs=false, quant;
733 args.pairsDo { |key, val|
743 pairs[i+1].setSourceLikeInPbind(val)
746 pairs = pairs ++ [key, PatternProxy.new.setSourceLikeInPbind(val).quant_(quant)];
747 // fin(inf) is a way to stream symbols endlessly
752 if(changedPairs) { source.source = Pbind(*pairs) };
756 storeArgs { ^pairs.collect(_.source) }
761 *new { arg ... pairs;
763 key = pairs.removeAt(0);
764 pat = super.new(key);
766 if(pairs.isEmpty.not) {
767 if(src.class === PbindProxy) {
771 if(src.isKindOf(Pbind))
773 src.patternpairs.pairsDo { |key, pat|
774 if(pairs.includes(key).not) {
775 pairs = pairs.add(key);
776 pairs = pairs.add(pat);
781 src = PbindProxy.new(*pairs).quant_(pat.quant);
790 storeArgs { ^[key]++pattern.storeArgs }
791 repositoryArgs { ^this.storeArgs }
792 quant_ { arg val; super.quant = val; source.quant = val }
798 // general purpose lookup stream
801 var <>dict, <>which, <>repeats, <>default;
802 *new { arg dict, which, repeats=inf, default;
803 ^super.newCopyArgs(dict, which, repeats, default);
805 storeArgs { ^[dict,which,repeats,default ] }
806 embedInStream { arg inval;
808 keyStream = which.asStream;
809 repeats.value(inval).do({
810 key = keyStream.next(inval);
811 if(key.isNil) { ^inval };
812 inval = (dict.at(key) ? default).embedInStream(inval);