class library: Reset has been renamed to OnError
[supercollider.git] / SCClassLibrary / JITLib / Patterns / Pdef.sc
blob427ce3f33e5caf518a4aeb760fad0706e799e05a
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;
12         *new { arg source;
13                 ^super.new.source_(source)
14         }
16         *default { ^1 } // safe for duration patterns
17         *defaultValue { ^1 }
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
27         }
29         source_ { arg obj;
30                 var pat = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
31                 if (obj.isNil) { pat = this.class.default };
32                 pattern = pat;
33                 source = obj; // keep original here.
34         }
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.
41         }
43         defaultEvent {
44                 if(envir.isNil) { envir = this.class.event };
45                 ^if(envir[\independent] === true) { (parent:envir) } { envir }
46         }
48         convertFunction { arg func;
49                         ^Prout {
50                                 var inval = func.def.prototypeFrame !? { inval = this.defaultEvent };
51                                 func.value( inval ).embedInStream(inval)
52                         };
53         }
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) }
66         set { arg ... args;
67                 if(envir.isNil) { this.envir = this.class.event };
68                 args.pairsDo { arg key, val; envir.put(key, val) };
69         }
71         unset { arg ... args;
72                 if(envir.notNil) { args.do { arg key; envir.removeAt(key) } };
73         }
75         get { arg key;
76                 ^if(envir.notNil) { envir[key] } { nil };
77         }
79         place { arg ... args;
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.
82         }
84         isEventPattern { ^false }
86         receiveEvent { ^nil }
88         embed { |val|
89                 ^if(val.notNil) { Pchain(this, val) } { this }.embedInStream
90         }
92         embedInStream { arg inval;
94                 var outval, count = 0;
95                 var pat = pattern;
96                 var test = condition;
97                 var resetTest = reset;
98                 var stream = pattern.asStream;
100                 while {
101                         this.receiveEvent(inval); // used in subclass
103                         if(
104                                 (reset !== resetTest)
105                                 or: { pat !== pattern and: { test.value(outval, count) } }
106                         ) {
107                                                 pat = pattern;
108                                                 test = condition;
109                                                 resetTest = reset;
110                                                 count = 0;
111                                                 stream = this.constrainStream(stream);
112                         };
113                         outval = stream.next(inval);
114                         count = count + 1;
115                         outval.notNil
116                 }{
117                         inval = outval.yield;
118                 };
119                 ^inval
120         }
122         endless {
124                 ^Prout { arg inval;
126                         var outval, count = 0;
127                         var pat = pattern;
128                         var test = condition;
129                         var resetTest = reset;
130                         var stream = pattern.asStream;
131                         var default = this.class.defaultValue;
133                         loop {
134                                 this.receiveEvent(inval); // used in subclass
136                                 if(
137                                         (reset !== resetTest)
138                                         or: { pat !== pattern and: { test.value(outval, count) } }
139                                 ) {
140                                                         pat = pattern;
141                                                         test = condition;
142                                                         resetTest = reset;
143                                                         count = 0;
144                                                         stream = this.constrainStream(stream);
145                                 };
146                                 outval = stream.next(inval) ? default;
147                                 count = count + 1;
148                                 inval = outval.yield;
149                         }
150                 }
152         }
154         count { arg n=1;
155                 condition = { |val,i| i % n == 0 }
156         }
158         reset { reset = reset ? 0 + 1 }
160         sched { arg func;
161                 if(quant.isNil)
162                         { func.value }
163                         { this.clock.schedAbs(quant.nextTimeOnGrid(this.clock), { func.value; nil }) }
164         }
166         storeArgs { ^[pattern] }
169         // these following methods are factored out for the benefit of subclasses
170         // they only work for Pdef/Tdef/Pdefn.
173         *removeAll {
174                 this.clear;
175                 this.all.makeEmpty;
176         }
178         *clear {
179                 this.all.do { arg pat; pat.clear }
180         }
182         clear {
183                 this.stop;
184                 this.source = nil;
185                 ^nil
186         }
188         remove {
189                 this.class.all.removeAt(this.key);
190                 this.clear;
191         }
193         // backward compatibility
194         *basicNew { arg source;
195                 ^super.new.init(source)
196         }
198         // global storage
200         *at { arg key;
201                 ^this.all.at(key)
202         }
204         repositoryArgs { ^[this.key, this.source] }
206         *postRepository { arg keys, stream;
207                 keys = keys ?? { this.all.keys };
208                 stream = stream ? Post;
209                 keys.do { arg key;
210                         var item;
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 << ")"
216                                 };
218                                 stream << ";\n"
219                         };
220                 };
221         }
223         *event { arg proxyClass = PatternProxy;
224                 var event, res;
225                 if(defaultEnvir.isNil) {
226                         defaultEnvir = Event.default;
227                         defaultEnvir.parent = defaultEnvir.parent.copy.putAll(
228                                 (
229                                 forward: #{ 1 },
230                                 proxyClass: proxyClass,
231                                 get: {|e, key|
232                                         var x = e.at(key);
233                                         if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x); };
234                                         x
235                                 },
236                                 place: { |e, key, val|
237                                         var x = e.at(key);
238                                         if(x.isKindOf(e.proxyClass).not) { x = e.proxyClass.new; e.put(key, x) };
239                                         x.source_(val)
240                                 }
241                                 )
242                         );
243                 };
244                 event = defaultEnvir.copy;
245         //      event[\self] = event; // this risks a crash on storeOn.
246                 ^event
247         }
249         ////////////////
253 Pdefn : PatternProxy {
254         var <key;
255         classvar <>all;
257         *initClass {
258                 all = IdentityDictionary.new;
259         }
260         *new { arg key, item;
261                 var res = this.at(key);
262                 if(res.isNil) {
263                         res = super.new(item).prAdd(key);
264                 } {
265                         if(item.notNil) { res.source = item }
266                 }
267                 ^res
269         }
271         map { arg ... args;
272                 if(envir.isNil) { this.envir = () };
273                 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
274         }
276         storeArgs { ^[key] } // assume it was created globally
278         prAdd { arg argKey;
279                 key = argKey;
280                 all.put(argKey, this);
281         }
285 // contains time patterns (tasks)
287 TaskProxy : PatternProxy {
288         var <player, <>playQuant;
289         classvar <>defaultQuant=1.0;
291         storeArgs { ^[source] }
293         source_ { arg obj;
294                         pattern = if(obj.isKindOf(Function)) { this.convertFunction(obj) }{ obj };
295                         if (obj.isNil) { pattern = this.class.default; source = obj; };
296                         this.wakeUp;
297                         source = obj;
298         }
300         convertFunction { arg func;
301                         ^Prout { |inevent|
302                                         var inval = func.def.prototypeFrame !? { this.defaultEvent };
303                                         if(inevent.isNumber or: {inevent.isNil} or: { inval.isNil }) {
304                                                 inevent = inval
305                                         } {
306                                                 inevent.copy.parent_(inval);
307                                         };
308                                         func.value(inevent)
309                         }
310         }
313         isEventPattern { ^true }
315         *default { ^Pn(this.defaultValue, 1) }
317         constrainStream { arg str;
318                 ^if(this.quant.notNil and: { str.notNil }) {
319                         Pseq([
320                                 EmbedOnce(Pconst(thisThread.clock.timeToNextBeat(this.quant), str, 0.001)),
321                                 pattern
322                         ])
323                 } { pattern }.asStream
324         }
326         align { arg argQuant;
327                 quant = argQuant;
328                 this.source = this.source.copy;
329         }
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)
337         }
339         play { arg argClock, doReset=false, quant;
340                 playQuant = quant ? this.quant;
341                 if(player.isNil) {
342                         player = this.playOnce(argClock, doReset, playQuant);
343                 } {
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);
349                         } {
350                                 if (doReset) { player.reset };
351                         }
352                 }
353         }
354         wakeUp {
355                         if(this.isPlaying) { this.play(quant:playQuant) }       }
356         asProtected {
357                 ^Pprotect(this, { if(this.player.notNil) { this.player.streamError } })
358         }
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)
372         }
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) }
379         }
383 Tdef : TaskProxy {
384         var <key;
385         classvar <>all;
388         *initClass {
389                 all = IdentityDictionary.new;
390         }
392         *new { arg key, item;
393                 var res = this.at(key);
394                 if(res.isNil) {
395                         res = super.new(item).prAdd(key);
396                 } {
397                         if(item.notNil) { res.source = item }
398                 }
399                 ^res
401         }
403         storeArgs { ^[key] }
405         prAdd { arg argKey;
406                 key = argKey;
407                 all.put(argKey, this);
408         }
414 // contains event patterns
416 EventPatternProxy : TaskProxy {
417         var <>fadeTime;
418         classvar <>defaultQuant=1.0;
420         storeArgs { ^[source] }
422         source_ { arg obj;
423                 if(obj.isKindOf(Function)) // allow functions to be passed in
424                         { pattern = PlazyEnvirN(obj) }
425                         { if (obj.isNil)
426                                 { pattern = this.class.default }
427                                 { pattern = obj }
428                         };
429                 envir !? { pattern = pattern <> envir };
430                 this.wakeUp;
431                 source = obj;
432         }
434         envir_ { arg dict;
435                 envir = dict;
436                 this.source = source;
437         }
439         *defaultValue { ^Event.silent }
441         embedInStream { arg inval, cleanup;
443                 var outval, count=0;
444                 var pat = pattern;
445                 var test = condition;
446                 var resetTest = reset;
447                 var stream = pattern.asStream;
449                 cleanup ?? { cleanup = EventStreamCleanup.new };
451                 while {
452                         this.receiveEvent(inval);
453                         if(
454                                 (reset !== resetTest)
455                                 or: { pat !== pattern and: { test.value(outval, count) } }
456                         ) {
457                                                 pat = pattern;
458                                                 test = condition;
459                                                 resetTest = reset;
460                                                 count = 0;
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;
465                         };
466                         outval = stream.next(inval);
467                         count = count + 1;
468                         outval.notNil
469                 }{
470                         outval = cleanup.update(outval);
471                         inval = outval.yield;
472                         if(inval.isNil) { ^nil.alwaysYield }
473                 };
474                 ^inval
476         }
478         endless {
480                 ^Prout { arg inval;
482                         var outval;
483                         var cleanup = EventStreamCleanup.new;
484                         var count = 0;
485                         var pat = pattern;
486                         var test = condition;
487                         var resetTest = reset;
488                         var stream = pattern.asStream;
489                         var default = this.class.defaultValue;
491                         loop {
492                                 this.receiveEvent(inval);
493                                 if(
494                                         (reset !== resetTest)
495                                         or: { pat !== pattern and: { test.value(outval, count) } }
496                                 ) {
497                                                         pat = pattern;
498                                                         test = condition;
499                                                         resetTest = reset;
500                                                         count = 0;
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;
505                                 };
506                                 outval = stream.next(inval) ? default;
507                                 count = count + 1;
508                                 outval = cleanup.update(outval);
509                                 inval = outval.yield;
510                         }
511                 }
513         }
516         constrainStream { arg str, inval, cleanup;
517                 var delta, tolerance, new;
518                 var quantBeat, catchUp, deltaTillCatchUp, forwardTime, quant = this.quant;
520                 ^if(quant.notNil) {
521                         quantBeat = this.quantBeat ? 0;
522                         catchUp = this.outset;
524                         delta = thisThread.clock.timeToNextBeat(quant);
525                         tolerance = quantBeat % delta % 0.125;
526                         if(catchUp.notNil) {
527                                 deltaTillCatchUp = thisThread.clock.timeToNextBeat(catchUp);
528                                 new = pattern.asStream;
529                                 forwardTime = quantBeat - delta + deltaTillCatchUp;
530                                 delta = new.fastForward(forwardTime, tolerance) + deltaTillCatchUp;
531                         } {
532                                 new = pattern
533                         };
535                         if(fadeTime.isNil) {
536                                 if(delta == 0) {
537                                         cleanup.exit(inval);
538                                         new
539                                 } {
540                                         Pseq([EmbedOnce(Pfindur(delta, str, tolerance).asStream(cleanup)), new])
541                                 }
542                         }{
544                                 Ppar([
545                                         EmbedOnce(PfadeOut(str, fadeTime, delta, tolerance).asStream(cleanup)),
546                                         PfadeIn(new, fadeTime, delta, tolerance)
547                                 ])
548                         }
549                 } {
550                         cleanup.exit(inval);
551                         pattern
552                 }.asStream
553         }
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)
566         }
568         // playing one instance //
570         playOnce { arg argClock, protoEvent, quant;
571                 ^this.fork(argClock ? this.clock, quant ? this.quant, protoEvent)
572         }
575         ////////// playing interface //////////
577         // start playing //
579         play { arg argClock, protoEvent, quant, doReset=false;
580                 playQuant = quant ? this.quant;
581                 if(player.isNil) {
582                         player = EventStreamPlayer(this.asProtected.asStream, protoEvent);
583                         player.play(argClock, doReset, playQuant);
584                 } {
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);
590                         } {
591                                 if(doReset) { player.reset };
592                         }
593                 }
594         }
599 Pdef : EventPatternProxy {
600         var <key;
602         classvar <>all;
604         storeArgs { ^[key] }
606         *new { arg key, item;
607                 var res = this.at(key);
608                 if(res.isNil) {
609                         res = super.new(item).prAdd(key);
610                 } {
611                         if(item.notNil) { res.source = item }
612                 }
613                 ^res
615         }
617         map { arg ... args;
618                 if(envir.isNil) { this.envir = () };
619                 args.pairsDo { |key, name| envir.put(key, Pdefn(name)) }
620         }
622         prAdd { arg argKey;
623                 key = argKey;
624                 all.put(argKey, this);
625         }
627         *initClass {
628                 var phraseEventFunc;
630                 all = IdentityDictionary.new;
631                 Class.initClassTree(Event);
633                 phraseEventFunc = {
634                         var pat, event, outerEvent, recursionLevel, instrument, embeddingLevel, freq, rest;
636                                 embeddingLevel = ~embeddingLevel ? 0; // infinite recursion catch
637                                 freq = ~freq.value;
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 })
643                                 {
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) }
650                                         ) {
651                                                 outerEvent = currentEnvironment.copy
652                                         } {
653                                                 outerEvent = Event.default;
654                                                 outerEvent.use {
655                                                         ~type = \phrase;
656                                                         ~recursionLevel = recursionLevel;
657                                                 }
658                                         };
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|
665                                                                         inval.use {
666                                                                                         ~instrument = instrument;
667                                                                                         ~parent = outerEvent;
668                                                                                         ~recursionLevel = recursionLevel - 1;
669                                                                                 };
670                                                                                 inval
671                                                         };
672                                                 } {
673                                                         // play pattern in the ordinary way
674                                                         ~type = \note;
675                                                 };
676                                         } {     // avoid recursion, if instrument not set.
677                                                 outerEvent.put(\embeddingLevel, embeddingLevel + 1);
678                                                 outerEvent.parent_(Event.parentEvents.default);
679                                         };
680                                         // maybe add a Pprotect here.
681                                         // pat.asProtected
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);
687                                 } {
688                                         ~type = \note;
689                                         ~play.value;
690                                 }
692                 };
694                 Event.addEventType(\phrase, phraseEventFunc);
695         }
701 PbindProxy : Pattern {
702         var <>pairs, <source;
704         *new { arg ... pairs;
705                 ^super.newCopyArgs(pairs).init
706         }
707         init {
708                 forBy(0, pairs.size-1, 2) { arg i;
709                         pairs[i+1] = PatternProxy(pairs[i+1])
710                 };
711                 source = EventPatternProxy(Pbind(*pairs));
712         }
713         embedInStream { arg inval;
714                 ^source.embedInStream(inval)
715         }
716         find { arg key;
717                 pairs.pairsDo { |u,x,i| if(u === key) { ^i } }; ^nil
718         }
719         quant_ { arg val;
720                 pairs.pairsDo { arg key, item; item.quant = val }; // maybe use ref later
721                 source.quant = val;
722         }
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;
732                 quant = this.quant;
733                 args.pairsDo { |key, val|
734                         var i, remove;
735                         i = this.find(key);
736                         if(i.notNil)
737                         {
738                                 if(val.isNil) {
739                                         pairs.removeAt(i);
740                                         pairs.removeAt(i);
741                                         changedPairs = true;
742                                 }{
743                                         pairs[i+1].setSourceLikeInPbind(val)
744                                 };
745                         }{
746                                 pairs = pairs ++ [key, PatternProxy.new.setSourceLikeInPbind(val).quant_(quant)];
747                                 // fin(inf) is a way to stream symbols endlessly
748                                 changedPairs = true;
749                         };
751                 };
752                 if(changedPairs) { source.source =  Pbind(*pairs) };
754         }
756         storeArgs { ^pairs.collect(_.source) }
760 Pbindef : Pdef {
761         *new { arg ... pairs;
762                 var key, pat, src;
763                 key = pairs.removeAt(0);
764                 pat = super.new(key);
765                 src = pat.source;
766                 if(pairs.isEmpty.not) {
767                         if(src.class === PbindProxy) {
768                                 src.set(*pairs);
769                                 pat.wakeUp;
770                         } {
771                                 if(src.isKindOf(Pbind))
772                                 {
773                                         src.patternpairs.pairsDo { |key, pat|
774                                                 if(pairs.includes(key).not) {
775                                                         pairs = pairs.add(key);
776                                                         pairs = pairs.add(pat);
777                                                 }
778                                         }
779                                 };
781                                 src = PbindProxy.new(*pairs).quant_(pat.quant);
782                                 pat.source = src
783                         };
784                 };
786                 ^pat
788         }
790         storeArgs { ^[key]++pattern.storeArgs }
791         repositoryArgs { ^this.storeArgs }
792         quant_ { arg val; super.quant = val; source.quant = val }
798 // general purpose lookup stream
800 Pdict : Pattern {
801         var <>dict, <>which, <>repeats, <>default;
802         *new { arg dict, which, repeats=inf, default;
803                 ^super.newCopyArgs(dict, which, repeats, default);
804         }
805         storeArgs { ^[dict,which,repeats,default ] }
806         embedInStream { arg inval;
807                 var keyStream, key;
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);
813                 });
814                 ^inval
815         }