class library: Volume - remove debug message
[supercollider.git] / SCClassLibrary / Common / Streams / FilterPatterns.sc
blob1f3fba43e8158eead3067fbae710b4a473a75000
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, inval));
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, inval).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, inval);
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 = EventStreamCleanup.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 cleanup = EventStreamCleanup.new;
157                 var val, inevent, filteredEvent;
158                 var valStream = value.asStream;
159                 var evtStream = pattern.asStream;
161                 loop {
162                         val = valStream.next(event);
163                         if (val.isNil or: event.isNil) {
164                                 ^cleanup.exit(event)
165                         }{
166                                 event = event.copy;
167                                 filteredEvent = this.filterEvent(event, val);
168                         };
169                         inevent = evtStream.next(filteredEvent);
170                         if (inevent.isNil) { ^cleanup.exit(event) };
171                         cleanup.update(inevent);
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 cleanup = EventStreamCleanup.new;
201                 var val, inEvent;
202                 var valStream = value.asStream;
203                 var evtStream = pattern.asStream;
205                 loop {
206                         inEvent = evtStream.next(event);
207                         // if (event.isNil) { ^nil.yield };
208                         if (inEvent.isNil) { ^cleanup.exit(event) };
209                         val = valStream.next(inEvent);
210                         if (val.isNil) { ^cleanup.exit(event) };
212                         this.filterEvent(inEvent, val);
213                         cleanup.update(inEvent);
214                         event = inEvent.yield;
215                 }
216         }
220 Padd : Pset {
221         filterEvent { arg event, val;
222                 ^event[name] = event[name] + val;
223         }
226 Pmul : Pset {
227         filterEvent { arg event, val;
228                 ^event[name] = event[name] * val;
229         }
233 Psetp : Pset {
234         embedInStream { arg event;
235                 var evtStream, val, inevent;
236                 var valStream = value.iter;
238                 while {
239                         val = valStream.next(event);
240                         val.notNil
241                 }{
242                         evtStream = pattern.asStream;
243                         while {
244                                 inevent = evtStream.next(event);
245                                 if(event.isNil) { ^nil.yield };
246                                 inevent.notNil;
247                         } {
248                                 this.filterEvent(inevent, val);
249                                 event = inevent.yield;
250                         };
251                 };
252                 ^event;
253         }
256 Paddp : Psetp {
257         filterEvent { arg event, val;
258                 ^event[name] = event[name] + val;
259         }
262 Pmulp : Psetp {
263         filterEvent { arg event, val;
264                 ^event[name] = event[name] * val;
265         }
269 Pstretch : FilterPattern {
270         var <>value;
271         *new { arg value, pattern;
272                 ^super.new(pattern).value_(value)
273         }
274         storeArgs { ^[value,pattern] }
275         embedInStream {  arg event;
276                 var cleanup = EventStreamCleanup.new;
277                 var inevent;
278                 var val, delta;
279                 var valStream = value.asStream;
280                 var evtStream = pattern.asStream;
282                 loop {
283                         inevent = evtStream.next(event).asEvent;
284                         if (inevent.isNil) { ^cleanup.exit(event) };
285                         val = valStream.next(inevent);
286                         if (val.isNil) { ^cleanup.exit(event) };
288                         delta = event[\delta];
289                         if (delta.notNil) {
290                                 inevent[\delta] = delta * val;
291                         };
292                         inevent[\dur] = inevent[\dur] * val;
293                         cleanup.update(inevent);
294                         event = yield(inevent);
295                 }
296         }
301 Pstretchp : Pstretch {
303         embedInStream { arg event;
304                 var evtStream, val, inevent, delta;
305                 var valStream = value.asStream;
306                 while {
307                         val = valStream.next(event).asEvent;
308                         val.notNil
309                 } {
310                         evtStream = pattern.asStream;
311                         while {
312                                 inevent = evtStream.next(event);
313                                 // if(event.isNil) { ^nil.yield };
314                                 inevent.notNil
315                         } {
316                                 delta = inevent[\delta];
317                                 if (delta.notNil) {
318                                         inevent[\delta] = delta * val;
319                                 };
320                                 inevent[\dur] = inevent[\dur] * val;
321                                 event = inevent.yield;
322                         };
323                 };
324                 ^event;
325         }
329 // needs testing - hjh
330 Pplayer : FilterPattern {
331         var <>subPattern;
332         *new { arg playerPattern, subPattern;
333                 ^super.newCopyArgs(playerPattern, subPattern)
334         }
335         storeArgs { ^[ pattern, subPattern ] }
336         embedInStream { arg event;
337                 var player, inevent;
338                 var playerStream = pattern.asStream;
339                 var stream = subPattern.asStream;
340                 loop{
341                         inevent = stream.next(event);
342                         if (inevent.isNil) { ^event };
343                         player = playerStream.next(event);
344                         if (player.isNil) { ^event };
345                         inevent.parent = player.event;
346                         event = yield(inevent);
347                 }
348         }
349         // backward compatibility: unnecessary var playerPattern was removed
350         playerPattern { ^pattern }
351         playerPattern_ { |playerPattern| pattern = playerPattern }
356 Pdrop : FilterPattern {
357         var <>count;
358         *new { arg count, pattern;
359                 ^super.new(pattern).count_(count)
360         }
361         storeArgs { ^[count,pattern] }
362         embedInStream { arg event;
363                 var inevent;
364                 var stream = pattern.asStream;
366                 count.value(event).do {
367                         inevent = stream.next(event);
368                         if (inevent.isNil, { ^event });
369                 };
370                 loop {
371                         inevent = stream.next(event);
372                         if (inevent.isNil, { ^event });
373                         event = inevent.yield;
374                 };
375         }
378 Pfin : FilterPattern {
379         var <>count;
380         *new { arg count, pattern;
381                 ^super.new(pattern).count_(count)
382         }
383         storeArgs { ^[count,pattern] }
384         asStream { | cleanup| ^Routine({ arg inval; this.embedInStream(inval, cleanup) }) }
386         embedInStream { arg event, cleanup;
387                 var inevent;
388                 var stream = pattern.asStream;
389                 cleanup ?? { cleanup = EventStreamCleanup.new };
390                 count.value(event).do({
391                         inevent = stream.next(event) ?? { ^event };
392                         cleanup.update(inevent);
393                         event = inevent.yield;
394                 });
395                 ^cleanup.exit(event)
396         }
401 // it is not correct to call stream.next(nil) on a value stream
402 // but there is no good way to distinguish in Pfin so we need a subclass
403 // might be ok to deprecate this now
405 Pfinval : Pfin {
406         embedInStream { arg event;
407                 var inevent;
408                 var stream = pattern.asStream;
410                 count.value(event).do({
411                         inevent = stream.next(event);
412                         if (inevent.isNil, { ^event });
413                         event = inevent.yield;
414                 });
415                 ^event
416         }
419 Pfindur : FilterPattern {
420         var <>dur, <>tolerance;
421         *new { arg dur, pattern, tolerance = 0.001;
422                 ^super.new(pattern).dur_(dur).tolerance_(tolerance)
423         }
424         storeArgs { ^[dur,pattern,tolerance] }
425         asStream { | cleanup| ^Routine({ arg inval; this.embedInStream(inval, cleanup) }) }
427         embedInStream { arg event, cleanup;
428                 var item, delta, elapsed = 0.0, nextElapsed, inevent,
429                 localdur = dur.value(event);
430                 var stream = pattern.asStream;
432                 cleanup ?? { cleanup = EventStreamCleanup.new };
433                 loop {
434                         inevent = stream.next(event).asEvent ?? { ^event };
435                         cleanup.update(inevent);
436                         delta = inevent.delta;
437                         nextElapsed = elapsed + delta;
438                         if (nextElapsed.roundUp(tolerance) >= localdur) {
439                                 // must always copy an event before altering it.
440                                 // fix delta time and yield to play the event.
441                                 inevent = inevent.copy.put(\delta, localdur - elapsed).yield;
442                                 ^cleanup.exit(inevent);
443                         };
445                         elapsed = nextElapsed;
446                         event = inevent.yield;
448                 }
449         }
452 Psync : FilterPattern {
453         var <>quant, <>maxdur, <>tolerance;
454         *new { arg pattern, quant, maxdur, tolerance = 0.001;
455                 ^super.new(pattern).quant_(quant).maxdur_(maxdur).tolerance_(tolerance)
456         }
457         storeArgs { ^[pattern,quant,maxdur,tolerance] }
459         embedInStream { arg event;
460                 var item, stream, delta, elapsed = 0.0, nextElapsed, clock, inevent;
461                 var     localquant = quant.value(event), localmaxdur = maxdur.value(event);
462                 var cleanup = EventStreamCleanup.new;
464                 stream = pattern.asStream;
466                 loop {
467                         inevent = stream.next(event).asEvent;
468                         if(inevent.isNil) {
469                                 if(localquant.notNil) {
470                                         event = Event.silent(elapsed.roundUp(localquant) - elapsed, event);
471                                         ^cleanup.exit(event).yield;
472                                 };
473                         };
474                         cleanup.update(inevent);
476                         delta = inevent.delta;
477                         nextElapsed = elapsed + delta;
479                         if (localmaxdur.notNil and: { nextElapsed.round(tolerance) >= localmaxdur }) {
480                                 inevent = inevent.copy;
481                                 inevent.put(\delta, localmaxdur - elapsed);
482                                 event = inevent.yield;
483                                 ^cleanup.exit(event);
484                         } {
485                                 elapsed = nextElapsed;
486                                 event = inevent.yield;
487                         };
488                 };
489         }
493 Pconst : FilterPattern {
494         var <>sum, <>tolerance;
495         *new { arg sum, pattern, tolerance=0.001;
496                 ^super.new(pattern).sum_(sum).tolerance_(tolerance)
497         }
498         storeArgs { ^[sum,pattern,tolerance] }
500         embedInStream { arg inval;
501                 var delta, elapsed = 0.0, nextElapsed, str=pattern.asStream,
502                 localSum = sum.value(inval);
503                 loop ({
504                         delta = str.next(inval);
505                         if(delta.isNil) {
506                                 (localSum - elapsed).yield;
507                                 ^inval
508                         };
509                         nextElapsed = elapsed + delta;
510                         if (nextElapsed.round(tolerance) >= localSum) {
511                                 (localSum - elapsed).yield;
512                                 ^inval
513                         }{
514                                 elapsed = nextElapsed;
515                                 inval = delta.yield;
516                         };
517                 });
518         }
521 Plag : FilterPattern {
522         var <>lag;
523         *new { arg lag, pattern;
524                 ^super.new(pattern).lag_(lag)
525         }
526         storeArgs { ^[lag,pattern] }
527         embedInStream { arg event;
528                 var item;
529                 var stream = pattern.asStream;
530                 var inevent = event.copy;
532                 event = Event.silent(lag.value(event), event).yield;
534                 loop {
535                         inevent = stream.next(event);
536                         if (inevent.isNil) { ^event};
537                         event = inevent.yield;
538                 };
539         }
543 Pbindf : FilterPattern {
544         var <>patternpairs;
545         *new { arg pattern ... pairs;
546                 if (pairs.size.odd, { Error("Pbindf should have odd number of args.\n").throw });
547                 ^super.new(pattern ? Event.default).patternpairs_(pairs)
548         }
549         storeArgs { ^[pattern] ++ patternpairs }
550         embedInStream { arg event;
551                 var cleanup = EventStreamCleanup.new;
552                 var eventStream;
553                 var outevent;
554                 var streampairs = patternpairs.copy;
555                 var endval = streampairs.size - 1;
557                 forBy (1, endval, 2) { arg i;
558                         streampairs.put(i, streampairs[i].asStream);
559                 };
560                 eventStream = pattern.asStream;
562                 loop{
563                         outevent = eventStream.next(event);
564                         if (outevent.isNil) { ^cleanup.exit(event) };
566                         forBy (0, endval, 2) { arg i;
567                                 var name = streampairs[i];
568                                 var stream = streampairs[i+1];
569                                 var streamout = stream.next(outevent);
571                                 if (streamout.isNil) { ^cleanup.exit(event) };
572                                 if (name.isSequenceableCollection) {
573                                         if (name.size > streamout.size) {
574                                                 ("the pattern is not providing enough values to assign to the key set:" + name).warn;
575                                                 ^outevent
576                                         };
577                                         name.do { arg key, i;
578                                                 outevent.put(key, streamout[i]);
579                                         };
580                                 }{
581                                         outevent.put(name, streamout);
582                                 };
584                         };
585                         cleanup.update(outevent);
586                         event = yield(outevent);
587                 };
588         }
592 Pstutter : FilterPattern {
593         var <>n;
594         *new { arg n, pattern;
595                 ^super.new(pattern).n_(n)
596         }
597         storeArgs { ^[n,pattern] }
598         embedInStream { arg event;
599                 var inevent, nn;
601                 var stream = pattern.asStream;
602                 var nstream = n.asStream;
604                 while {
605                         (inevent = stream.next(event)).notNil
606                 } {
607                         if((nn = nstream.next(event)).notNil) {
608                                 nn.abs.do {
609                                         event = inevent.copy.yield;
610                                 };
611                         } { ^event };
612                 };
613                 ^event;
614         }
618 PdurStutter : Pstutter { // float streams
620         embedInStream { arg event;
621                 var dur, stut;
622                 var durs = pattern.asStream;
623                 var stutts = n.asStream;
624                 while({
625                         (dur = durs.next(event)).notNil
626                         and: {(stut = stutts.next(event)).notNil}
627                         },{
628                                 if(stut > 0,{ // 0 skips it
629                                         if(stut > 1,{
630                                                 dur = dur / stut;
631                                                 stut.do({
632                                                         event = dur.yield;
633                                                 })
634                                                 },{
635                                                         event = dur.yield
636                                         })
637                                 })
638                 })
639                 ^event;
640         }
643 Pclutch : FilterPattern {
644         var <>connected;
645         *new { arg pattern, connected = true;
646                 ^super.new(pattern).connected_(connected)
647         }
648         storeArgs { ^[ pattern, connected ] }
649         embedInStream { arg inval;
650                 var clutchStream = connected.asStream;
651                 var stream = pattern.asStream;
652                 var outval, clutch;
653                 while {
654                         clutch = clutchStream.next(inval);
655                         clutch.notNil
656                 } {
657                         if(clutch === true or: { clutch == 1 }) {
658                                 outval = stream.next(inval);
659                                 if(outval.isNil) { ^inval };
660                                 inval = outval.yield;
661                         } {
662                                 outval ?? { outval = stream.next(inval) };
663                                 inval = outval.copy.yield;
664                         };
665                 }
666         }
669 Pwhile : FuncFilterPattern {
670         embedInStream {arg event;
671                 while({ func.value(event) },{
672                         event = pattern.embedInStream(event);
673                 });
674                 ^event;
675         }
678 Pwrap : FilterPattern {
679         var <>lo, <>hi;
680         *new { arg pattern,lo,hi;
681                 ^super.new(pattern).lo_(lo).hi_(hi)
682         }
684         storeArgs { ^[pattern,lo,hi] }
686         embedInStream { arg event;
687                 var next;
688                 var stream = pattern.asStream;
689                 var loStr = lo.asStream;
690                 var hiStr = hi.asStream;
691                 var loVal, hiVal;
692                 while({
693                         loVal = loStr.next(event);
694                         hiVal = hiStr.next(event);
695                         next = stream.next(event);
696                         next.notNil and: { loVal.notNil } and: { hiVal.notNil }
697                         },{
698                                 event = next.wrap(loVal, hiVal).yield
699                 });
700                 ^event;
701         }
704 Ptrace : FilterPattern {
705         var <>key, printStream, prefix;
707         *new { arg pattern, key, printStream, prefix = "";
708                 ^super.newCopyArgs(pattern, key, printStream, prefix)
709         }
710         storeArgs { ^[ pattern, key, printStream, prefix ] }
712         embedInStream { arg inval;
713                 var func, collected;
714                 printStream = printStream ? Post;
715                 if(key.isNil) {
716                         collected = pattern.collect {|item| printStream << prefix << item << Char.nl; item }
717                 } {
718                         func = { |val, item, prefix|
719                                 if(val.isKindOf(Function) and: { item.isKindOf(Environment) })
720                                 {
721                                         val = item.use { val.value };
722                                         printStream << prefix << val << "\t(printed function value)\n";
723                                 } {
724                                         printStream << prefix << val << Char.nl;
725                                 };
726                         }.flop;
727                         collected = pattern.collect {|item|
728                                 var val = item.atAll(key.asArray).unbubble;
729                                 func.value(val, item, prefix);
730                                 item
731                         }
732                 };
733                 ^collected.embedInStream(inval)
734         }
737 Pclump : FilterPattern {
738         var <>n;
739         *new { arg n, pattern;
740                 ^super.new(pattern).n_(n)
741         }
742         embedInStream { arg event;
743                 var next, list, nval;
744                 var stream = pattern.asStream;
745                 var nstream = n.asStream;
746                 loop {
747                         list = [];
748                         nval = nstream.next(event);
749                         if (nval.isNil) { ^event };
750                         nval.do {
751                                 next = stream.next(event);
752                                 if (next.isNil) {
753                                         if (list.size > 0) { event = list.yield };
754                                         ^event
755                                 };
756                                 list = list.add(next);
757                         };
758                         event = list.yield;
759                 }
760         }
761         storeArgs { ^[ n, pattern ] }
764 Pflatten : Pclump {
765         embedInStream { arg event;
766                 var next, nval;
767                 var stream = pattern.asStream;
768                 var nstream = n.asStream;
769                 while {
770                         next = stream.next(event);
771                         nval = nstream.next(event);
772                         next.notNil and: { nval.notNil };
773                 }{
774                         if (next.isKindOf(SequenceableCollection)) {
775                                 next = next.flatten(nval);
776                                 next.do {|item| event = item.yield };
777                         }{
778                                 event = next.yield;
779                         }
780                 }
781                 ^event
782         }
785 Pdiff : FilterPattern {
786         embedInStream { arg event;
787                 var stream = pattern.asStream;
788                 var next, prev = stream.next(event);
789                 while {
790                         next = stream.next(event);
791                         next.notNil;
792                 }{
793                         event = (next - prev).yield;
794                         prev = next;
795                 }
796                 ^event
797         }
800 Prorate : FilterPattern {
801         var <>proportion;
803         *new { arg proportion, pattern=1;
804                 ^super.new(pattern).proportion_(proportion)
805         }
807         embedInStream { arg inval;
808                 var val, c;
809                 var str = pattern.asStream;
810                 var prop = proportion.asStream;
811                 loop {
812                         val = str.next(inval);
813                         c = prop.next(inval);
814                         if(val.isNil or: { c.isNil }) { ^inval };
815                         if(c.isSequenceableCollection) {
816                                 c.do { |el|
817                                         inval = yield(el * val)
818                                 }
819                         } {
820                                 inval = yield(c * val);
821                                 inval = yield(1 - c * val);
822                         }
823                 }
824         }
825         storeArgs { ^[proportion,pattern] }
828 Pavaroh : FilterPattern {
830         var <>aroh, <>avaroh, <>stepsPerOctave;
831         *new { arg pattern, aroh, avaroh, stepsPerOctave=12;
832                 ^super.newCopyArgs(pattern, aroh, avaroh, stepsPerOctave)
834         }
835         storeArgs { ^[pattern, aroh, avaroh, stepsPerOctave ] }
837         embedInStream { arg inval;
838                 var me, melast = 0, scale;
839                 var mestream = pattern.asStream;
840                 var stepsStr = stepsPerOctave.asStream, stepVal;
842                 while {
843                         stepVal = stepsStr.next(inval);
844                         me = mestream.next(inval);
845                         me.notNil and: { stepVal.notNil }
846                 } {
847                         scale = if(me >= melast) { aroh } { avaroh };
848                         melast = me;
849                         inval = me.degreeToKey(scale, stepVal).yield
850                 };
851                 ^inval
852         }