deprecate SCViewHolder-layRight
[supercollider.git] / SCClassLibrary / Common / Streams / Patterns.sc
blob9b0455a35191bb6c7557479f9b616081039f68ea
1 Pattern : AbstractFunction {
4         // concatenate Patterns
5         ++ { arg aPattern;
6                 ^Pseq.new([this, aPattern])
7         }
8         // compose Patterns
9         <> { arg aPattern;
10                 ^Pchain(this, aPattern)
11         }
13         play { arg clock, protoEvent, quant;
14                 ^this.asEventStreamPlayer(protoEvent).play(clock, false, quant)
15         }
16         // phase causes pattern to start somewhere in the current measure rather than on a downbeat
17         // offset allows pattern to compute ahead a bit to allow negative lags for strummed chords
18         // and to ensure one pattern computes ahead of another
20         asStream { ^Routine({ arg inval; this.embedInStream(inval) }) }
21         iter { ^this.asStream }
23         asEventStreamPlayer { arg protoEvent;
24                 ^EventStreamPlayer(this.asStream, protoEvent);
25         }
26         embedInStream { arg inval;
27                 ^this.asStream.embedInStream(inval);
28         }
29         do { arg function;
30                 this.asStream.do(function)
31         }
33         // filtering operations
34         collect { arg function;
35                 ^Pcollect.new(function, this)
36         }
37         select { arg function;
38                 ^Pselect.new(function, this)
39         }
40         reject { arg function;
41                 ^Preject.new(function, this)
42         }
44         // function composition
45         composeUnaryOp { arg operator;
46                 ^Punop.new(operator, this)
47         }
48         composeBinaryOp { arg operator, pattern, adverb;
49                 ^Pbinop.new(operator, this, pattern, adverb)
50         }
51         reverseComposeBinaryOp { arg operator, pattern, adverb;
52                 ^Pbinop.new(operator, pattern, this, adverb)
53         }
54         composeNAryOp { arg selector, argList;
55                 ^Pnaryop.new(selector, this, argList);
56         }
58         //////////////////////
60         mtranspose { arg n; ^Paddp(\mtranspose, n, this) }
61         ctranspose { arg n; ^Paddp(\ctranspose, n, this) }
62         gtranspose { arg n; ^Paddp(\gtranspose, n, this) }
63         detune { arg n; ^Paddp(\detune, n, this) }
65         scaleDur { arg x; ^Pmulp(\dur, x, this) }
66         addDur { arg x; ^Paddp(\dur, x, this) }
67         stretch { arg x; ^Pmulp(\stretch, x, this) }
68         lag { arg t; ^Plag(t, this) }
70         legato { arg x; ^Pmulp(\legato, x, this) }
72         db { arg db; ^Paddp(\db, db, this) }
74         clump { arg n; ^Pclump(n, this) }
75         flatten { arg n = 1; ^Pflatten(n, this) }
76         repeat { arg n=inf; ^Pn(this, n) }
77         keep { arg n; ^Pfin(n, this) }
78         drop { arg n; ^Pdrop(n, this) }
79         stutter { arg n; ^Pstutter(n, this) }
80         finDur { arg dur, tolerance = 0.001; ^Pfindur(dur, this, tolerance) }
81         fin { arg n; ^Pfin(n, this) }
83         trace { arg key, printStream, prefix=""; ^Ptrace(this, key, printStream, prefix) }
84         differentiate { ^Pdiff(this) }
85         integrate { ^Plazy { var sum = 0; this.collect { |x| sum = sum + x } } }
87         //////////////////////
89         // realtime recording
90         // for NRT see Pattern:asScore
92         // path: if nil, auto-generate path
93         // dur: if nil, record until pattern stops (infinite pattern = problem)
94         // fadeTime: allow extra time after last Event for nodes to become silent
95         record { |path, headerFormat = "AIFF", sampleFormat = "float", numChannels = 2, dur = nil, fadeTime = 0.2, clock(TempoClock.default), protoEvent(Event.default), server(Server.default), out = 0|
96                 var     buf, bus, recsynth, pattern, defname, cond, startTime;
97                 if(dur.notNil) { pattern = Pfindur(dur, this) } { pattern = this };
98                 path ?? {
99                         if(thisProcess.platform.name == \windows) {
100                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Main.elapsedTime.round(0.01) ++ "." ++ headerFormat;
101                         } {
102                                 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Date.localtime.stamp ++ "." ++ headerFormat;
103                         };
104                 };
105                 fork {
106                         cond = Condition.new;
107                         buf = Buffer.alloc(server, 65536, numChannels);
108                         SynthDef(defname = ("patrec"++numChannels).asSymbol, { |bufnum, bus, out|
109                                 var     sig = In.ar(bus, numChannels);
110                                 DiskOut.ar(bufnum, sig);
111                                 Out.ar(out, sig);
112                         }).add;
113                         bus = Bus.audio(server, numChannels);
114                         server.sync(cond);
115                         buf.write(path, headerFormat, sampleFormat, numFrames: 0, startFrame: 0, leaveOpen: true);
116                         server.sync(cond);
117                         "Recording pattern into % at %\n".postf(path, thisThread.beats);
118                         recsynth = server.nextNodeID;
119                         Pprotect(
120                                 // Pfset has a cleanupFunc, which executes even if pattern is stopped by cmd-.
121                                 Pfset(nil,
122                                         Pseq([
123                                                 Pfuncn { startTime = thisThread.beats; 0 },
124                                                 (type: \on, instrument: defname, bufnum: buf, bus: bus, out: out, id: recsynth,
125                                                         delta: 0),
126                                                 pattern <> (out: bus),
127                                                 Plazy {
128                                                         Pn((type: \rest, delta: (fadeTime ? 0)
129                                                                 .roundUp(buf.numFrames / server.sampleRate)),
130                                                                 1)
131                                                 }
132                                         ], 1),
133                                         { (type: \kill, id: recsynth).play }
134                                 ),
135                                 // on error, killing the recsynth triggers rest of cleanup below
136                                 { (type: \kill, id: recsynth).play }
137                         ).play(clock, protoEvent, quant: 0);
138                                 // clean up after recording synth stops
139                         OSCpathResponder(server.addr, ['/n_end', recsynth], { |time, resp, msg|
140                                 resp.remove;
141                                 cond.unhang;
142                         }).add;
143                         cond.hang;
144                         buf.close.free;
145                         bus.free;
146                         server.sendMsg(\d_free, defname);
147                         "Finished recording % at %\n".postf(path, thisThread.beats);
148                 }
149         }
152 Pfunc : Pattern {
153         var <>nextFunc; // Func is evaluated for each next state
154         var <>resetFunc; // Func is evaluated for each next state
155         *new { arg nextFunc, resetFunc;
156                 ^super.newCopyArgs(nextFunc, resetFunc)
157         }
158         storeArgs { ^[nextFunc] ++ resetFunc }
159         asStream {
160                 ^FuncStream.new(nextFunc, resetFunc)
161         }
164 Prout : Pattern {
165         var <>routineFunc;
166         *new { arg routineFunc;
167                 ^super.newCopyArgs(routineFunc)
168         }
169         storeArgs { ^[routineFunc] }
170         asStream {
171                 ^Routine.new(routineFunc)
172         }
173         embedInStream { arg inval; ^routineFunc.value(inval) }
176 Proutine : Prout {
177         *new { |routineFunc|
178                 "Proutine is deprecated. Use Prout instead.".postln;
179                 ^Prout(routineFunc)
180         }
183 Pfuncn : Pattern {
184         var <>func, <>repeats;
185         *new { arg func, repeats = 1;
186                 ^super.newCopyArgs(func, repeats)
187         }
188         storeArgs { ^[func,repeats] }
189         embedInStream {  arg inval;
190                 repeats.value(inval).do({
191                         inval = func.value(inval).yield;
192                 });
193                 ^inval
194         }
198 // Punop and Pbinop are used to create patterns in response to math operations
199 Punop : Pattern {
200         var <>operator, <>a;
201         *new { arg operator, a;
202                 ^super.newCopyArgs(operator, a)
203         }
205         storeOn { arg stream; stream <<< a << "." << operator }
207         embedInStream { arg inval;
208                 var stream, outval;
209                 stream = a.asStream;
210                 loop {
211                         outval = stream.next(inval);
212                         if (outval.isNil) { ^inval };
213                         inval = yield(outval.perform(operator));
214                 }
215         }
217         asStream {
218                 ^UnaryOpStream.new(operator, a.asStream);
219         }
222 Pbinop : Pattern {
223         var <>operator, <>a, <>b, <>adverb;
224         *new { arg operator, a, b, adverb;
225                 ^super.newCopyArgs(operator, a, b, adverb)
226         }
228         storeOn { arg stream;
229                         stream << "(" <<< a << " " << operator.asBinOpString;
230                         if(adverb.notNil) { stream << "." << adverb };
231                         stream << " " <<< b << ")"
232         }
234         asStream {
235                 if (adverb.isNil) {
236                         ^BinaryOpStream.new(operator, a.asStream, b.asStream);
237                 };
238                 if (adverb == 'x') {
239                         ^BinaryOpXStream.new(operator, a.asStream, b.asStream);
240                 };
241                 ^nil
242         }
245 Pnaryop : Pattern {
246         var <>operator, <>a, <>arglist;
247         *new { arg operator, a, arglist;
248                 ^super.newCopyArgs(operator, a, arglist)
249         }
250         storeOn { arg stream; stream <<< a << "." << operator << "(" <<<* arglist << ")" }
252         embedInStream { arg inval;
253                 var streamA, streamlist, vala, values, isNumeric;
254                 streamA = a.asStream;
255                          // optimization
256                 isNumeric = arglist.every { arg item;
257                         item.isNumber or: {item.class === Symbol}
258                 };
260                 if (isNumeric) {
261                         loop {
262                                 vala = streamA.next(inval);
263                                 if (vala.isNil) { ^inval };
264                                 inval = yield(vala.performList(operator, arglist));
265                         }
266                 }{
267                         streamlist = arglist.collect({ arg item; item.asStream });
268                         loop {
269                                 vala = streamA.next(inval);
270                                 if (vala.isNil) { ^inval };
271                                 values = streamlist.collect({ arg item;
272                                         var result = item.next(inval);
273                                         if (result.isNil) { ^inval };
274                                         result
275                                 });
276                                 inval = yield(vala.performList(operator, values));
277                         }
278                 };
279         }
281         asStream {
282                 ^NAryOpStream.new(operator, a.asStream, arglist.collect({ arg item; item.asStream }));
283         }
286 PdegreeToKey : Pnaryop {
287         *new { arg pattern, scale, stepsPerOctave=12;
288                 ^super.new('degreeToKey', pattern, [scale, stepsPerOctave])
289         }
290         // this is not reversible
291         // but it would save as something that played the same
292         //storeArgs { ^[ pattern, scale, stepsPerOctave ] }
295 Pchain : Pattern {
296         var <>patterns;
297         *new { arg ... patterns;
298                 ^super.newCopyArgs(patterns);
299         }
300         <> { arg aPattern;
301                 var list;
302                 list = patterns.copy.add(aPattern);
303                 ^this.class.new(*list)
304         }
305         embedInStream { arg inval;
306                 var streams, inevent, cleanup = EventStreamCleanup.new;
307                 streams = patterns.collect(_.asStream);
308                 loop {
309                         inevent = inval.copy;
310                         streams.reverseDo { |str|
311                                 inevent = str.next(inevent);
312                                 if(inevent.isNil) { ^cleanup.exit(inval) };
313                         };
314                         cleanup.update(inevent);
315                         inval = yield(inevent);
316                 };
317         }
318         storeOn { arg stream;
319                         stream << "(";
320                         patterns.do { |item,i|  if(i != 0) { stream << " <> " }; stream <<< item; };
321                         stream << ")"
322         }
326 Pevent : Pattern {
327         var <>pattern, <>event;
329         *new { arg pattern, event;
330                 ^super.newCopyArgs(pattern, event ?? { Event.default });
331         }
332         storeArgs { ^[pattern, event] }
333         embedInStream { arg inval;
334                 var outval;
335                 var stream = pattern.asStream;
336                 loop {
337                         outval = stream.next(event);
338                         if (outval.isNil) { ^inval };
339                         inval = outval.yield
340                  }
341         }
345 Pbind : Pattern {
346         var <>patternpairs;
347         *new { arg ... pairs;
348                 if (pairs.size.odd, { Error("Pbind should have even number of args.\n").throw; });
349                 ^super.newCopyArgs(pairs)
350         }
352         storeArgs { ^patternpairs }
353         embedInStream { arg inevent;
354                 var event;
355                 var sawNil = false;
356                 var streampairs = patternpairs.copy;
357                 var endval = streampairs.size - 1;
359                 forBy (1, endval, 2) { arg i;
360                         streampairs.put(i, streampairs[i].asStream);
361                 };
363                 loop {
364                         if (inevent.isNil) { ^nil.yield };
365                         event = inevent.copy;
366                         forBy (0, endval, 2) { arg i;
367                                 var name = streampairs[i];
368                                 var stream = streampairs[i+1];
369                                 var streamout = stream.next(event);
370                                 if (streamout.isNil) { ^inevent };
372                                 if (name.isSequenceableCollection) {
373                                         if (name.size > streamout.size) {
374                                                 ("the pattern is not providing enough values to assign to the key set:" + name).warn;
375                                                 ^inevent
376                                         };
377                                         name.do { arg key, i;
378                                                 event.put(key, streamout[i]);
379                                         };
380                                 }{
381                                         event.put(name, streamout);
382                                 };
384                         };
385                         inevent = event.yield;
386                 }
387         }
390 Pmono : Pattern {
391         var <>synthName, <>patternpairs;
392         *new { arg name ... pairs;
393                 if (pairs.size.odd, { Error("Pmono should have odd number of args.\n").throw; });
394                 ^super.newCopyArgs(name.asSymbol, pairs)
395         }
397         embedInStream { | inevent |
398                 ^PmonoStream(this).embedInStream(inevent)
399         }
402 PmonoArtic : Pmono {
403         embedInStream { |inevent|
404                 ^PmonoArticStream(this).embedInStream(inevent)
405         }
409 Pseries : Pattern {     // arithmetic series
410         var <>start=0, <>step=1, <>length=inf;
411         *new { arg start = 0, step = 1, length=inf;
412                 ^super.newCopyArgs(start, step, length)
413         }
414         storeArgs { ^[start,step,length] }
416         embedInStream { arg inval;
417                 var outval, counter = 0;
418                 var cur = start.value(inval);
419                 var len = length.value(inval);
420                 var stepStr = step.asStream, stepVal;
421                 while { counter < len } {
422                         stepVal = stepStr.next(inval);
423                         if(stepVal.isNil) { ^inval };
424                         outval = cur;
425                         cur = cur + stepVal;
426                         counter = counter + 1;
427                         inval = outval.yield;
428                 };
429                 ^inval;
430         }
433 Pgeom : Pattern {       // geometric series
434         var <>start=1.0, <>grow=1.0, <>length=inf;
435         *new { arg start = 0, grow = 1, length=inf;
436                 ^super.newCopyArgs(start, grow, length)
437         }
438         storeArgs { ^[start,grow,length] }
439         embedInStream { arg inval;
440                 var outval, counter = 0;
441                 var cur = start.value(inval);
442                 var len = length.value(inval);
443                 var growStr = grow.asStream, growVal;
445                 while { counter < len } {
446                         growVal = growStr.next(inval);
447                         if(growVal.isNil) { ^inval };
448                         outval = cur;
449                         cur = cur * growVal;
450                         counter = counter + 1;
451                         inval = outval.yield;
452                 };
453                 ^inval;
454         }
458 Pbrown : Pattern {
459         var <>lo, <>hi, <>step, <>length;
461         *new { arg lo=0.0, hi=1.0, step=0.125, length=inf;
462                 ^super.newCopyArgs(lo, hi, step, length)
463         }
465         storeArgs { ^[lo,hi,step,length] }
467         embedInStream { arg inval;
468                 var cur;
469                 var loStr = lo.asStream, loVal;
470                 var hiStr = hi.asStream, hiVal;
471                 var stepStr = step.asStream, stepVal;
473                 loVal = loStr.next(inval);
474                 hiVal = hiStr.next(inval);
475                 stepVal = stepStr.next(inval);
476                 cur = rrand(loVal, hiVal);
477                 if(loVal.isNil or: { hiVal.isNil } or: { stepVal.isNil }) { ^inval };
479                 length.value(inval).do {
480                         loVal = loStr.next(inval);
481                         hiVal = hiStr.next(inval);
482                         stepVal = stepStr.next(inval);
483                         if(loVal.isNil or: { hiVal.isNil } or: { stepVal.isNil }) { ^inval };
484                         cur = this.calcNext(cur, stepVal).fold(loVal, hiVal);
485                         inval = cur.yield;
486                 };
488                 ^inval;
489         }
491         calcNext { arg cur, step;
492                 ^cur + step.xrand2
493         }
496 Pgbrown : Pbrown {
497         calcNext { arg cur, step;
498                 ^cur * (1 + step.xrand2)
499         }
502 Pwhite : Pattern {
503         var <>lo, <>hi, <>length;
504         *new { arg lo=0.0, hi=1.0, length=inf;
505                 ^super.newCopyArgs(lo, hi, length)
506         }
507         storeArgs { ^[lo,hi,length] }
508         embedInStream { arg inval;
509                 var loStr = lo.asStream;
510                 var hiStr = hi.asStream;
511                 var hiVal, loVal;
512                 length.value(inval).do({
513                         hiVal = hiStr.next(inval);
514                         loVal = loStr.next(inval);
515                         if(hiVal.isNil or: { loVal.isNil }) { ^inval };
516                         inval = rrand(loVal, hiVal).yield;
517                 });
518                 ^inval;
519         }
522 Pprob : Pattern {
523         var <>hi, <>lo, <>length, <tableSize, <distribution, <table;
525         *new { arg distribution, lo=0.0, hi=1.0, length=inf, tableSize;
526                 ^super.newCopyArgs(hi, lo, length, tableSize).distribution_(distribution);
527         }
528         distribution_ { arg list;
529                 var n = tableSize ?? { max(64, list.size) }; // resample, if too small
530                 distribution = list;
531                 table = distribution.asRandomTable(n);
532         }
533         tableSize_ { arg n;
534                 tableSize = n;
535                 this.distribution_(distribution);
536         }
537         embedInStream { arg inval;
538                 var loStr = lo.asStream;
539                 var hiStr = hi.asStream;
540                 var hiVal, loVal;
542                 length.value(inval).do {
543                         loVal = loStr.next(inval);
544                         hiVal = hiStr.next(inval);
545                         if(hiVal.isNil or: { loVal.isNil }) { ^inval };
546                         inval = ((table.tableRand * (hiVal - loVal)) + loVal).yield;
547                 };
548                 ^inval;
549         }
552 Pstep2add : Pattern {
553         var <>pattern1, <>pattern2;
554         *new { arg pattern1, pattern2;
555                 ^super.newCopyArgs(pattern1, pattern2)
556         }
557         storeArgs { ^[pattern1,pattern2] }
558         embedInStream { arg inval;
559                 var stream1, stream2, val1, val2;
561                 stream1 = pattern1.asStream;
562                 while {
563                         (val1 = stream1.next(inval)).notNil;
564                 }{
565                         stream2 = pattern2.asStream;
566                         while {
567                                 (val2 = stream2.next(inval)).notNil;
568                         }{
569                                 inval = yield( val1 + val2 );
570                         };
571                 };
572                 ^inval
573         }
576 Pstep3add : Pattern {
577         var <>pattern1, <>pattern2, <>pattern3;
578         *new { arg pattern1, pattern2, pattern3;
579                 ^super.newCopyArgs(pattern1, pattern2, pattern3)
580         }
581         storeArgs { ^[pattern1,pattern2,pattern3] }
583         embedInStream { arg inval;
584                 var stream1, stream2, stream3, val1, val2, val3;
586                 stream1 = pattern1.asStream;
587                 while {
588                         (val1 = stream1.next(inval)).notNil;
589                 }{
590                         stream2 = pattern2.asStream;
591                         while {
592                                 (val2 = stream2.next(inval)).notNil;
593                         }{
594                                 stream3 = pattern3.asStream;
595                                 while {
596                                         (val3 = stream3.next(inval)).notNil;
597                                 }{
598                                         inval = yield( val1 + val2 + val3 );
599                                 };
600                         };
601                 };
602                 ^inval;
603         }
607 PstepNfunc : Pattern {
608         var <function, <>patterns;
609         *new { arg function, patterns;
610                 ^super.newCopyArgs(function ? { |x| x }, patterns)
611         }
612         storeArgs { ^[function,patterns] }
613         embedInStream { arg inval;
614                 var val;
615                 var size = patterns.size;
616                 var max = size - 1;
617                 var streams = Array.newClear(size);
618                 var vals = Array.newClear(size);
619                         // this variable is needed because of recursion
620                 var f = { arg inval, level=0;
621                         var val;
622                         streams[level] = patterns[level].asStream;
623                         while{
624                                 vals[level] = val = streams[level].next(inval);
625                                 val.notNil;
626                         }{
627                                 if(level < max) {
628                                         inval = f.value(inval, level + 1)
629                                 }{
630                                         inval = yield(function.value(vals));
631                                 }
632                         };
633                         inval;
634                 };
635                 ^f.value(inval);
636         }
640 PstepNadd : PstepNfunc {
641         *new { arg ... patterns;
642                 ^super.new({ arg vals; vals.sum }, patterns)
643         }
644         storeArgs { ^patterns }
647 // returns relative time (in beats) from moment of embedding
649 Ptime : Pattern {
650         var <>repeats;
651         *new { arg repeats=inf;
652                 ^super.newCopyArgs(repeats)
653         }
654         storeArgs { ^[repeats] }
655         embedInStream { arg inval;
656                 var start = thisThread.beats;
657                 repeats.value(inval).do { inval = (thisThread.beats - start).yield };
658                 ^inval
659         }
662 // if an error is thrown in the stream, func is evaluated
664 Pprotect : FilterPattern {
665         var <>func;
666         *new { arg pattern, func;
667                 ^super.new(pattern).func_(func)
668         }
669         storeArgs { ^[ pattern, func ] }
670         asStream {
671                 var rout = Routine(pattern.embedInStream(_));
672                 rout.exceptionHandler = { |error|
673                         func.value(error, rout);
674                         nil.handleError(error)
675                 };
676                 ^rout
677         }
682 // access a key from the input event
683 Pkey : Pattern {
684         var     <>key, <>repeats;
685         *new { |key|
686                 ^super.newCopyArgs(key)
687         }
688         storeArgs { ^[key] }
689                 // avoid creating a routine
690         asStream {
691                 var     keystream = key.asStream;
692                 ^FuncStream({ |inevent|
693                         inevent !? { inevent[keystream.next(inevent)] }
694                 });
695         }
698 Pif : Pattern {
699         var     <>condition, <>iftrue, <>iffalse, <>default;
700         *new { |condition, iftrue, iffalse, default|
701                 ^super.newCopyArgs(condition, iftrue, iffalse, default)
702         }
703         storeArgs { ^[condition, iftrue, iffalse,default] }
704         asStream {
705                 var     condStream = condition.asStream,
706                         trueStream = iftrue.asStream,
707                         falseStream = iffalse.asStream;
709                 ^FuncStream({ |inval|
710                         var test;
711                         if((test = condStream.next(inval)).isNil) {
712                                 nil
713                         } {
714                                 if(test) {
715                                         trueStream.next(inval) ? default
716                                 } {
717                                         falseStream.next(inval) ? default
718                                 };
719                         };
720                 }, {            // reset func
721                         condStream.reset;
722                         trueStream.reset;
723                         falseStream.reset;
724                 })
725         }