deprecate SCViewHolder-layRight
[supercollider.git] / SCClassLibrary / Common / Streams / FilterPatterns.sc
blob7580b3aef7cfa449c14fe16135afef99f77e8af5
1 FilterPattern : Pattern {
2         var <>pattern;
4         *new { arg pattern;
5                 ^super.new.pattern_(pattern)
6         }
10 Pn : FilterPattern {
11         var <>repeats, <>key;
12         *new { arg pattern, repeats=inf, key;
13                 ^super.newCopyArgs(pattern, repeats, key )
14         }
15         storeArgs { ^[pattern,repeats, key] }
16         embedInStream { | event |
17                 if(key.isNil) {
18                         repeats.value(event).do { event = pattern.embedInStream(event) };
19                 } {
20                         repeats.value(event).do {
21                                 event = pattern.embedInStream(event);
22                                 event[key] = true;
23                         };
24                         event[key] = false;
25                 };
26                 ^event;
27         }
30 Pgate  : Pn {
31         *new { arg pattern, repeats=inf,  key   ;
32                 ^super.new(pattern).repeats_(repeats).key_(key)
33         }
34         storeArgs { ^[pattern,repeats, key] }
35         embedInStream { | event |
36                 var stream, output;
37                 repeats.do {
38                         stream = pattern.asStream;
39                         output = stream.next(event);
40                         while {
41                                 if (event[key] == true) { output = stream.next(event) };
42                                 output.notNil;
43                         } {
44                                 event = output.copy.embedInStream(event)
45                         }
46                 };
47                 ^event;
48         }
51 FuncFilterPattern : FilterPattern {
52         var <>func;
54         *new { arg func, pattern;
55                 ^super.new(pattern).func_(func)
56         }
57         storeArgs { ^[func,pattern] }
60 Pcollect : FuncFilterPattern {
61         embedInStream { arg inval;
62                 var stream, outval;
63                 stream = pattern.asStream;
64                 loop {
65                         outval = stream.next(inval);
66                         if (outval.isNil) { ^inval };
67                         inval = yield(func.value(outval));
68                 }
69         }
70         asStream {
71                 ^pattern.asStream.collect(func);
72         }
75 Pselect : FuncFilterPattern {
76         embedInStream { arg inval;
77                 var stream, outval;
78                 stream = pattern.asStream;
79                 loop {
80                         while ({
81                                 outval = stream.next(inval);
82                                 if (outval.isNil) { ^inval };
83                                 func.value(outval).not
84                         });
85                         inval = yield(outval);
86                 }
87         }
88         asStream {
89                 ^pattern.asStream.select(func);
90         }
93 Preject : FuncFilterPattern {
94         embedInStream { arg inval;
95                 var stream, outval;
96                 stream = pattern.asStream;
97                 loop {
98                         while ({
99                                 outval = stream.next(inval);
100                                 if (outval.isNil) { ^inval };
101                                 func.value(outval);
102                         });
103                         inval = yield(outval);
104                 }
105         }
106         asStream {
107                 ^pattern.asStream.reject(func);
108         }
111 Pfset : FuncFilterPattern {
112         var     <>cleanupFunc;
113         *new { |func, pattern, cleanupFunc|
114                 ^super.new(func, pattern).cleanupFunc_(cleanupFunc)
115         }
116         embedInStream { arg inevent;
117                 var event, cleanup = IneventStreamCleanup.new;
118                         // cleanup should actually not be passed in
119                         // but retaining (temporarily) for backward compatibility
120                 var envir = Event.make({ func.value(cleanup) });
121                 var stream = pattern.asStream;
122                 var once = true;
124                 loop {
125                         inevent = inevent.copy;
126                         inevent.putAll(envir);
127                         event = stream.next(inevent);
128                         if(once) {
129                                 cleanup.addFunction(event, { |flag|
130                                         envir.use({ cleanupFunc.value(flag) });
131                                 });
132                                 once = false;
133                         };
134                         if (event.isNil) {
135                                 ^cleanup.exit(inevent)
136                         } {
137                                 cleanup.update(event);
138                         };
139                         inevent = yield(event);
140                         if(inevent.isNil) { ^cleanup.exit(event) }
141                 };
142         }
146 Psetpre : FilterPattern {
147         var <>name, <>value;
148         *new { arg name, value, pattern;
149                 ^super.new(pattern).name_(name).value_(value)
150         }
151         storeArgs { ^[name,value,pattern] }
152         filterEvent { arg event, val;
153                 ^event[name] = val;
154         }
155         embedInStream { arg event;
156                 var val, inevent, filteredEvent;
157                 var valStream = value.asStream;
158                 var evtStream = pattern.asStream;
160                 loop {
161                         val = valStream.next(event);
162 //                      if (val.isNil or: event.isNil) { ^event };
163 //                      event = this.filterEvent(event, val);
164                         if (val.isNil or: event.isNil) {
165                                 filteredEvent = nil;
166                         }{
167                                 event = event.copy;
168                                 filteredEvent = this.filterEvent(event, val);
169                         };
170                         inevent = evtStream.next(filteredEvent);
171                         if (inevent.isNil) { ^event };
172                         event = yield(inevent);
173                         if(event.isNil) { nil.yield; ^inevent }
174                 }
175         }
178 Paddpre : Psetpre {
179         filterEvent { arg event, val;
180                 ^event[name] = event[name] + val;
181         }
184 Pmulpre : Psetpre {
185         filterEvent { arg event, val;
186                 ^event[name] = event[name] * val;
187         }
190 Pset : FilterPattern {
191         var <>name, <>value;
192         *new { arg name, value, pattern;
193                 ^super.new(pattern).name_(name).value_(value)
194         }
195         storeArgs { ^[name,value,pattern] }
196         filterEvent { arg event, val;
197                 ^event[name] = val;
198         }
199         embedInStream { arg event;
200                 var val, inEvent;
201                 var valStream = value.asStream;
202                 var evtStream = pattern.asStream;
204                 loop {
205                         inEvent = evtStream.next(event);
206                         if (event.isNil) { ^nil.yield };
207                         if (inEvent.isNil) { ^event };
208                         val = valStream.next(inEvent);
209                         if (val.isNil) { ^event };
211                         this.filterEvent(inEvent, val);
212                         event = inEvent.yield;
213                 }
214         }
218 Padd : Pset {
219         filterEvent { arg event, val;
220                 ^event[name] = event[name] + val;
221         }
224 Pmul : Pset {
225         filterEvent { arg event, val;
226                 ^event[name] = event[name] * val;
227         }
231 Psetp : Pset {
232         embedInStream { arg event;
233                 var evtStream, val, inevent;
234                 var valStream = value.iter;
236                 while {
237                         val = valStream.next(event);
238                         val.notNil
239                 }{
240                         evtStream = pattern.asStream;
241                         while {
242                                 inevent = evtStream.next(event);
243                                 if(event.isNil) { ^nil.yield };
244                                 inevent.notNil;
245                         } {
246                                 this.filterEvent(inevent, val);
247                                 event = inevent.yield;
248                         };
249                 };
250                 ^event;
251         }
254 Paddp : Psetp {
255         filterEvent { arg event, val;
256                 ^event[name] = event[name] + val;
257         }
260 Pmulp : Psetp {
261         filterEvent { arg event, val;
262                 ^event[name] = event[name] * val;
263         }
267 Pstretch : FilterPattern {
268         var <>value;
269         *new { arg value, pattern;
270                 ^super.new(pattern).value_(value)
271         }
272         storeArgs { ^[value,pattern] }
273         embedInStream {  arg event;
274                 var inevent;
275                 var val, delta;
276                 var valStream = value.asStream;
277                 var evtStream = pattern.asStream;
279                 loop {
280                         inevent = evtStream.next(event);
281                         if (inevent.isNil) { ^event };
282                         val = valStream.next(inevent);
283                         if (val.isNil) { ^event };
285                         delta = event[\delta];
286                         if (delta.notNil) {
287                                 inevent[\delta] = delta * val;
288                         };
289                         inevent[\dur] = inevent[\dur] * val;
290                         event = yield(inevent);
291                 }
292         }
297 Pstretchp : Pstretch {
299         embedInStream { arg event;
300                 var evtStream, val, inevent, delta;
301                 var valStream = value.asStream;
302                 while {
303                         val = valStream.next(event);
304                         val.notNil
305                 } {
306                         evtStream = pattern.asStream;
307                         while {
308                                 inevent = evtStream.next(event);
309                                 if(event.isNil) { ^nil.yield };
310                                 inevent.notNil
311                         } {
312                                 delta = inevent[\delta];
313                                 if (delta.notNil) {
314                                         inevent[\delta] = delta * val;
315                                 };
316                                 inevent[\dur] = inevent[\dur] * val;
317                                 event = inevent.yield;
318                         };
319                 };
320                 ^event;
321         }
325 // needs testing - hjh
326 Pplayer : FilterPattern {
327         var <>subPattern;
328         *new { arg playerPattern, subPattern;
329                 ^super.newCopyArgs(playerPattern, subPattern)
330         }
331         storeArgs { ^[ pattern, subPattern ] }
332         embedInStream { arg event;
333                 var player, inevent;
334                 var playerStream = pattern.asStream;
335                 var stream = subPattern.asStream;
336                 loop{
337                         inevent = stream.next(event);
338                         if (inevent.isNil) { ^event };
339                         player = playerStream.next(event);
340                         if (player.isNil) { ^event };
341                         inevent.parent = player.event;
342                         event = yield(inevent);
343                 }
344         }
345                 // backward compatibility: unnecessary var playerPattern was removed
346         playerPattern { ^pattern }
347         playerPattern_ { |playerPattern| pattern = playerPattern }
352 Pdrop : FilterPattern {
353         var <>count;
354         *new { arg count, pattern;
355                 ^super.new(pattern).count_(count)
356         }
357         storeArgs { ^[count,pattern] }
358         embedInStream { arg event;
359                 var inevent;
360                 var stream = pattern.asStream;
362                 count.value(event).do {
363                         inevent = stream.next(event);
364                         if (inevent.isNil, { ^event });
365                 };
366                 loop {
367                         inevent = stream.next(event);
368                         if (inevent.isNil, { ^event });
369                         event = inevent.yield;
370                 };
371         }
374 Pfin : FilterPattern {
375         var <>count;
376         *new { arg count, pattern;
377                 ^super.new(pattern).count_(count)
378         }
379         storeArgs { ^[count,pattern] }
380         asStream { | cleanup| ^Routine({ arg inval; this.embedInStream(inval, cleanup) }) }
382         embedInStream { arg event, cleanup;
383                 var inevent;
384                 var stream = pattern.asStream;
385                 cleanup ?? { cleanup = EventStreamCleanup.new };
386                 count.value(event).do({
387                         inevent = stream.next(event) ?? { ^event };
388                         cleanup.update(inevent);
389                         event = inevent.yield;
390                 });
391                 ^cleanup.exit(event)
392         }
397 // it is not correct to call stream.next(nil) on a value stream
398 // but there is no good way to distinguish in Pfin so we need a subclass
399 // might be ok to deprecate this now
401 Pfinval : Pfin {
402         embedInStream { arg event;
403                 var inevent;
404                 var stream = pattern.asStream;
406                 count.value(event).do({
407                         inevent = stream.next(event);
408                         if (inevent.isNil, { ^event });
409                         event = inevent.yield;
410                 });
411                 ^event
412         }
415 Pfindur : FilterPattern {
416         var <>dur, <>tolerance;
417         *new { arg dur, pattern, tolerance = 0.001;
418                 ^super.new(pattern).dur_(dur).tolerance_(tolerance)
419         }
420         storeArgs { ^[dur,pattern,tolerance] }
421         asStream { | cleanup| ^Routine({ arg inval; this.embedInStream(inval, cleanup) }) }
423         embedInStream { arg event, cleanup;
424                 var item, delta, elapsed = 0.0, nextElapsed, inevent,
425                         localdur = dur.value(event);
426                 var stream = pattern.asStream;
428                 cleanup ?? { cleanup = EventStreamCleanup.new };
429                 loop {
430                         inevent = stream.next(event) ?? { ^event };
431                         cleanup.update(inevent);
432                         delta = inevent.delta;
433                         nextElapsed = elapsed + delta;
434                         if (nextElapsed.roundUp(tolerance) >= localdur) {
435                                 // must always copy an event before altering it.
436                                 // fix delta time and yield to play the event.
437                                 inevent = inevent.copy.put(\delta, localdur - elapsed).yield;
438                                 ^cleanup.exit(inevent);
439                         };
441                         elapsed = nextElapsed;
442                         event = inevent.yield;
444                 }
445         }
448 Psync : FilterPattern {
449         var <>quant, <>maxdur, <>tolerance;
450         *new { arg pattern, quant, maxdur, tolerance = 0.001;
451                 ^super.new(pattern).quant_(quant).maxdur_(maxdur).tolerance_(tolerance)
452         }
453         storeArgs { ^[pattern,quant,maxdur,tolerance] }
455         embedInStream { arg event;
456                 var item, stream, delta, elapsed = 0.0, nextElapsed, clock, inevent;
457                 var     localquant = quant.value(event), localmaxdur = maxdur.value(event);
458                 var cleanup = EventStreamCleanup.new;
460                 stream = pattern.asStream;
462                 loop {
463                         inevent = stream.next(event);
464                         if(inevent.isNil) {
465                                 if(localquant.notNil) {
466                                         event = Event.silent(elapsed.roundUp(localquant) - elapsed, event);
467                                         ^cleanup.exit(event).yield;
468                                 };
469                         };
470                         cleanup.update(inevent);
472                         delta = inevent.delta;
473                         nextElapsed = elapsed + delta;
475                         if (localmaxdur.notNil and: { nextElapsed.round(tolerance) >= localmaxdur }) {
476                                 inevent = inevent.copy;
477                                 inevent.put(\delta, localmaxdur - elapsed);
478                                 event = inevent.yield;
479                                 ^cleanup.exit(event);
480                         } {
481                                 elapsed = nextElapsed;
482                                 event = inevent.yield;
483                         };
484                 };
485         }
489 Pconst : FilterPattern {
490         var <>sum, <>tolerance;
491         *new { arg sum, pattern, tolerance=0.001;
492                 ^super.new(pattern).sum_(sum).tolerance_(tolerance)
493         }
494         storeArgs { ^[sum,pattern,tolerance] }
496         embedInStream { arg inval;
497                 var delta, elapsed = 0.0, nextElapsed, str=pattern.asStream,
498                         localSum = sum.value(inval);
499                 loop ({
500                         delta = str.next(inval);
501                         if(delta.isNil) {
502                                 (localSum - elapsed).yield;
503                                 ^inval
504                         };
505                         nextElapsed = elapsed + delta;
506                         if (nextElapsed.round(tolerance) >= localSum) {
507                                 (localSum - elapsed).yield;
508                                 ^inval
509                         }{
510                                 elapsed = nextElapsed;
511                                 inval = delta.yield;
512                         };
513                 });
514         }
517 Plag : FilterPattern {
518         var <>lag;
519         *new { arg lag, pattern;
520                 ^super.new(pattern).lag_(lag)
521         }
522         storeArgs { ^[lag,pattern] }
523         embedInStream { arg event;
524                 var item;
525                 var stream = pattern.asStream;
526                 var inevent = event.copy;
528                 event = Event.silent(lag.value(event), event).yield;
530                 loop {
531                         inevent = stream.next(event);
532                         if (inevent.isNil) { ^event};
533                         event = inevent.yield;
534                 };
535         }
539 Pbindf : FilterPattern {
540         var <>patternpairs;
541         *new { arg pattern ... pairs;
542                 if (pairs.size.odd, { Error("Pbindf should have odd number of args.\n").throw });
543                 ^super.new(pattern ? Event.default).patternpairs_(pairs)
544         }
545         storeArgs { ^[pattern] ++ patternpairs }
546         embedInStream { arg event;
547                 var eventStream;
548                 var inevent;
549                 var streampairs = patternpairs.copy;
550                 var endval = streampairs.size - 1;
552                 forBy (1, endval, 2) { arg i;
553                         streampairs.put(i, streampairs[i].asStream);
554                 };
555                 eventStream = pattern.asStream;
557                 loop{
558                         if(event.isNil) {
559                                 eventStream.next(nil);
560                                 ^nil.yield
561                         };
562                         inevent = eventStream.next(event);
564                         if (inevent.isNil) { ^event };
565                         forBy (0, endval, 2) { arg i;
566                                 var name = streampairs[i];
567                                 var stream = streampairs[i+1];
568                                 var streamout = stream.next(inevent);
570                                 if (streamout.isNil) { ^event };
571                                 if (name.isSequenceableCollection) {
572                                 if (name.size > streamout.size) {
573                                                 ("the pattern is not providing enough values to assign to the key set:" + name).warn;
574                                                 ^inevent
575                                         };
576                                         name.do { arg key, i;
577                                                 inevent.put(key, streamout[i]);
578                                         };
579                                 }{
580                                         inevent.put(name, streamout);
581                                 };
583                         };
584                         event = yield(inevent);
585                 };
586         }
590 Pstutter : FilterPattern {
591         var <>n;
592         *new { arg n, pattern;
593                 ^super.new(pattern).n_(n)
594         }
595         storeArgs { ^[n,pattern] }
596         embedInStream { arg event;
597                 var inevent, nn;
599                 var stream = pattern.asStream;
600                 var nstream = n.asStream;
602                 while {
603                         (inevent = stream.next(event)).notNil
604                 } {
605                         if((nn = nstream.next(event)).notNil) {
606                                 nn.abs.do {
607                                         event = inevent.copy.yield;
608                                 };
609                         } { ^event };
610                 };
611                 ^event;
612         }
616 PdurStutter : Pstutter { // float streams
618         embedInStream { arg event;
619                 var dur, stut;
620                 var durs = pattern.asStream;
621                 var stutts = n.asStream;
622                 while({
623                         (dur = durs.next(event)).notNil
624                         and: {(stut = stutts.next(event)).notNil}
625                 },{
626                         if(stut > 0,{ // 0 skips it
627                                 if(stut > 1,{
628                                         dur = dur / stut;
629                                         stut.do({
630                                                 event = dur.yield;
631                                         })
632                                 },{
633                                         event = dur.yield
634                                 })
635                         })
636                 })
637                 ^event;
638         }
641 Pclutch : FilterPattern {
642         var <>connected;
643         *new { arg pattern, connected = true;
644                 ^super.new(pattern).connected_(connected)
645         }
646         storeArgs { ^[ pattern, connected ] }
647         embedInStream { arg inval;
648                 var clutchStream = connected.asStream;
649                 var stream = pattern.asStream;
650                 var outval, clutch;
651                 while {
652                         clutch = clutchStream.next(inval);
653                         clutch.notNil
654                 } {
655                         if(clutch === true or: { clutch == 1 }) {
656                                 outval = stream.next(inval);
657                                 if(outval.isNil) { ^inval };
658                                 inval = outval.yield;
659                         } {
660                                 outval ?? { outval = stream.next(inval) };
661                                 inval = outval.copy.yield;
662                         };
663                 }
664         }
667 Pwhile : FuncFilterPattern {
668         embedInStream {arg event;
669                 while({ func.value(event) },{
670                         event = pattern.embedInStream(event);
671                 });
672                 ^event;
673         }
676 Pwrap : FilterPattern {
677         var <>lo, <>hi;
678         *new { arg pattern,lo,hi;
679                 ^super.new(pattern).lo_(lo).hi_(hi)
680         }
682         storeArgs { ^[pattern,lo,hi] }
684         embedInStream { arg event;
685                 var next;
686                 var stream = pattern.asStream;
687                 var loStr = lo.asStream;
688                 var hiStr = hi.asStream;
689                 var loVal, hiVal;
690                 while({
691                         loVal = loStr.next(event);
692                         hiVal = hiStr.next(event);
693                         next = stream.next(event);
694                         next.notNil and: { loVal.notNil } and: { hiVal.notNil }
695                 },{
696                         event = next.wrap(loVal, hiVal).yield
697                 });
698                 ^event;
699         }
702 Ptrace : FilterPattern {
703         var <>key, printStream, prefix;
705         *new { arg pattern, key, printStream, prefix = "";
706                 ^super.newCopyArgs(pattern, key, printStream, prefix)
707         }
708         storeArgs { ^[ pattern, key, printStream, prefix ] }
710         embedInStream { arg inval;
711                 var func, collected;
712                 printStream = printStream ? Post;
713                 if(key.isNil) {
714                         collected = pattern.collect {|item| printStream << prefix << item << Char.nl; item }
715                 } {
716                         func = { |val, item, prefix|
717                                 if(val.isKindOf(Function) and: { item.isKindOf(Environment) })
718                                         {
719                                                 val = item.use { val.value };
720                                                 printStream << prefix << val << "\t(printed function value)\n";
721                                         } {
722                                                 printStream << prefix << val << Char.nl;
723                                         };
724                         }.flop;
725                         collected = pattern.collect {|item|
726                                 var val = item.atAll(key.asArray).unbubble;
727                                 func.value(val, item, prefix);
728                                 item
729                         }
730                 };
731                 ^collected.embedInStream(inval)
732         }
735 Pclump : FilterPattern {
736         var <>n;
737         *new { arg n, pattern;
738                 ^super.new(pattern).n_(n)
739         }
740         embedInStream { arg event;
741                 var next, list, nval;
742                 var stream = pattern.asStream;
743                 var nstream = n.asStream;
744                 loop {
745                         list = [];
746                         nval = nstream.next(event);
747                         if (nval.isNil) { ^event };
748                         nval.do {
749                                 next = stream.next(event);
750                                 if (next.isNil) {
751                                         if (list.size > 0) { event = list.yield };
752                                         ^event
753                                 };
754                                 list = list.add(next);
755                         };
756                         event = list.yield;
757                 }
758         }
759         storeArgs { ^[ n, pattern ] }
762 Pflatten : Pclump {
763         embedInStream { arg event;
764                 var next, nval;
765                 var stream = pattern.asStream;
766                 var nstream = n.asStream;
767                 while {
768                         next = stream.next(event);
769                         nval = nstream.next(event);
770                         next.notNil and: { nval.notNil };
771                 }{
772                         if (next.isKindOf(SequenceableCollection)) {
773                                 next = next.flatten(nval);
774                                 next.do {|item| event = item.yield };
775                         }{
776                                 event = next.yield;
777                         }
778                 }
779                 ^event
780         }
783 Pdiff : FilterPattern {
784         embedInStream { arg event;
785                 var stream = pattern.asStream;
786                 var next, prev = stream.next(event);
787                 while {
788                         next = stream.next(event);
789                         next.notNil;
790                 }{
791                         event = (next - prev).yield;
792                         prev = next;
793                 }
794                 ^event
795         }
798 Prorate : FilterPattern {
799         var <>proportion;
801         *new { arg proportion, pattern=1;
802                 ^super.new(pattern).proportion_(proportion)
803         }
805         embedInStream { arg inval;
806                 var val, c;
807                 var str = pattern.asStream;
808                 var prop = proportion.asStream;
809                 loop {
810                         val = str.next(inval);
811                         c = prop.next(inval);
812                         if(val.isNil or: { c.isNil }) { ^inval };
813                         if(c.isSequenceableCollection) {
814                                 c.do { |el|
815                                         inval = yield(el * val)
816                                 }
817                         } {
818                                 inval = yield(c * val);
819                                 inval = yield(1 - c * val);
820                         }
821                 }
822         }
823         storeArgs { ^[proportion,pattern] }
826 Pavaroh : FilterPattern {
828         var <>aroh, <>avaroh, <>stepsPerOctave;
829         *new { arg pattern, aroh, avaroh, stepsPerOctave=12;
830                 ^super.newCopyArgs(pattern, aroh, avaroh, stepsPerOctave)
832         }
833         storeArgs { ^[pattern, aroh, avaroh, stepsPerOctave ] }
835         embedInStream { arg inval;
836                 var me, melast = 0, scale;
837                 var mestream = pattern.asStream;
838                 var stepsStr = stepsPerOctave.asStream, stepVal;
840                 while {
841                         stepVal = stepsStr.next(inval);
842                         me = mestream.next(inval);
843                         me.notNil and: { stepVal.notNil }
844                 } {
845                         scale = if(me >= melast) { aroh } { avaroh };
846                         melast = me;
847                         inval = me.degreeToKey(scale, stepVal).yield
848                 };
849                 ^inval
850         }