1 Pattern : AbstractFunction {
4 // concatenate Patterns
6 ^Pseq.new([this, aPattern])
10 ^Pchain(this, aPattern)
13 play { arg clock, protoEvent, quant;
14 ^this.asEventStreamPlayer(protoEvent).play(clock, false, quant)
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);
26 embedInStream { arg inval;
27 ^this.asStream.embedInStream(inval);
30 this.asStream.do(function)
33 // filtering operations
34 collect { arg function;
35 ^Pcollect.new(function, this)
37 select { arg function;
38 ^Pselect.new(function, this)
40 reject { arg function;
41 ^Preject.new(function, this)
44 // function composition
45 composeUnaryOp { arg operator;
46 ^Punop.new(operator, this)
48 composeBinaryOp { arg operator, pattern, adverb;
49 ^Pbinop.new(operator, this, pattern, adverb)
51 reverseComposeBinaryOp { arg operator, pattern, adverb;
52 ^Pbinop.new(operator, pattern, this, adverb)
54 composeNAryOp { arg selector, argList;
55 ^Pnaryop.new(selector, this, argList);
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 //////////////////////
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 };
99 if(thisProcess.platform.name == \windows) {
100 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Main.elapsedTime.round(0.01) ++ "." ++ headerFormat;
102 path = thisProcess.platform.recordingsDir +/+ "SC_" ++ Date.localtime.stamp ++ "." ++ headerFormat;
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);
113 bus = Bus.audio(server, numChannels);
115 buf.write(path, headerFormat, sampleFormat, numFrames: 0, startFrame: 0, leaveOpen: true);
117 "Recording pattern into % at %\n".postf(path, thisThread.beats);
118 recsynth = server.nextNodeID;
120 // Pfset has a cleanupFunc, which executes even if pattern is stopped by cmd-.
123 Pfuncn { startTime = thisThread.beats; 0 },
124 (type: \on, instrument: defname, bufnum: buf, bus: bus, out: out, id: recsynth,
126 pattern <> (out: bus),
128 Pn((type: \rest, delta: (fadeTime ? 0)
129 .roundUp(buf.numFrames / server.sampleRate)),
133 { (type: \kill, id: recsynth).play }
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|
146 server.sendMsg(\d_free, defname);
147 "Finished recording % at %\n".postf(path, thisThread.beats);
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)
158 storeArgs { ^[nextFunc] ++ resetFunc }
160 ^FuncStream.new(nextFunc, resetFunc)
166 *new { arg routineFunc;
167 ^super.newCopyArgs(routineFunc)
169 storeArgs { ^[routineFunc] }
171 ^Routine.new(routineFunc)
173 embedInStream { arg inval; ^routineFunc.value(inval) }
178 "Proutine is deprecated. Use Prout instead.".postln;
184 var <>func, <>repeats;
185 *new { arg func, repeats = 1;
186 ^super.newCopyArgs(func, repeats)
188 storeArgs { ^[func,repeats] }
189 embedInStream { arg inval;
190 repeats.value(inval).do({
191 inval = func.value(inval).yield;
198 // Punop and Pbinop are used to create patterns in response to math operations
201 *new { arg operator, a;
202 ^super.newCopyArgs(operator, a)
205 storeOn { arg stream; stream <<< a << "." << operator }
207 embedInStream { arg inval;
211 outval = stream.next(inval);
212 if (outval.isNil) { ^inval };
213 inval = yield(outval.perform(operator));
218 ^UnaryOpStream.new(operator, a.asStream);
223 var <>operator, <>a, <>b, <>adverb;
224 *new { arg operator, a, b, adverb;
225 ^super.newCopyArgs(operator, a, b, adverb)
228 storeOn { arg stream;
229 stream << "(" <<< a << " " << operator.asBinOpString;
230 if(adverb.notNil) { stream << "." << adverb };
231 stream << " " <<< b << ")"
236 ^BinaryOpStream.new(operator, a.asStream, b.asStream);
239 ^BinaryOpXStream.new(operator, a.asStream, b.asStream);
246 var <>operator, <>a, <>arglist;
247 *new { arg operator, a, arglist;
248 ^super.newCopyArgs(operator, a, arglist)
250 storeOn { arg stream; stream <<< a << "." << operator << "(" <<<* arglist << ")" }
252 embedInStream { arg inval;
253 var streamA, streamlist, vala, values, isNumeric;
254 streamA = a.asStream;
256 isNumeric = arglist.every { arg item;
257 item.isNumber or: {item.class === Symbol}
262 vala = streamA.next(inval);
263 if (vala.isNil) { ^inval };
264 inval = yield(vala.performList(operator, arglist));
267 streamlist = arglist.collect({ arg item; item.asStream });
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 };
276 inval = yield(vala.performList(operator, values));
282 ^NAryOpStream.new(operator, a.asStream, arglist.collect({ arg item; item.asStream }));
286 PdegreeToKey : Pnaryop {
287 *new { arg pattern, scale, stepsPerOctave=12;
288 ^super.new('degreeToKey', pattern, [scale, stepsPerOctave])
290 // this is not reversible
291 // but it would save as something that played the same
292 //storeArgs { ^[ pattern, scale, stepsPerOctave ] }
297 *new { arg ... patterns;
298 ^super.newCopyArgs(patterns);
302 list = patterns.copy.add(aPattern);
303 ^this.class.new(*list)
305 embedInStream { arg inval;
306 var streams, inevent, cleanup = EventStreamCleanup.new;
307 streams = patterns.collect(_.asStream);
309 inevent = inval.copy;
310 streams.reverseDo { |str|
311 inevent = str.next(inevent);
312 if(inevent.isNil) { ^cleanup.exit(inval) };
314 cleanup.update(inevent);
315 inval = yield(inevent);
318 storeOn { arg stream;
320 patterns.do { |item,i| if(i != 0) { stream << " <> " }; stream <<< item; };
327 var <>pattern, <>event;
329 *new { arg pattern, event;
330 ^super.newCopyArgs(pattern, event ?? { Event.default });
332 storeArgs { ^[pattern, event] }
333 embedInStream { arg inval;
335 var stream = pattern.asStream;
337 outval = stream.next(event);
338 if (outval.isNil) { ^inval };
347 *new { arg ... pairs;
348 if (pairs.size.odd, { Error("Pbind should have even number of args.\n").throw; });
349 ^super.newCopyArgs(pairs)
352 storeArgs { ^patternpairs }
353 embedInStream { arg inevent;
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);
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;
377 name.do { arg key, i;
378 event.put(key, streamout[i]);
381 event.put(name, streamout);
385 inevent = event.yield;
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)
397 embedInStream { | inevent |
398 ^PmonoStream(this).embedInStream(inevent)
403 embedInStream { |inevent|
404 ^PmonoArticStream(this).embedInStream(inevent)
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)
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 };
426 counter = counter + 1;
427 inval = outval.yield;
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)
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 };
450 counter = counter + 1;
451 inval = outval.yield;
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)
465 storeArgs { ^[lo,hi,step,length] }
467 embedInStream { arg inval;
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);
491 calcNext { arg cur, step;
497 calcNext { arg cur, step;
498 ^cur * (1 + step.xrand2)
503 var <>lo, <>hi, <>length;
504 *new { arg lo=0.0, hi=1.0, length=inf;
505 ^super.newCopyArgs(lo, hi, length)
507 storeArgs { ^[lo,hi,length] }
508 embedInStream { arg inval;
509 var loStr = lo.asStream;
510 var hiStr = hi.asStream;
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;
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);
528 distribution_ { arg list;
529 var n = tableSize ?? { max(64, list.size) }; // resample, if too small
531 table = distribution.asRandomTable(n);
535 this.distribution_(distribution);
537 embedInStream { arg inval;
538 var loStr = lo.asStream;
539 var hiStr = hi.asStream;
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;
552 Pstep2add : Pattern {
553 var <>pattern1, <>pattern2;
554 *new { arg pattern1, pattern2;
555 ^super.newCopyArgs(pattern1, pattern2)
557 storeArgs { ^[pattern1,pattern2] }
558 embedInStream { arg inval;
559 var stream1, stream2, val1, val2;
561 stream1 = pattern1.asStream;
563 (val1 = stream1.next(inval)).notNil;
565 stream2 = pattern2.asStream;
567 (val2 = stream2.next(inval)).notNil;
569 inval = yield( val1 + val2 );
576 Pstep3add : Pattern {
577 var <>pattern1, <>pattern2, <>pattern3;
578 *new { arg pattern1, pattern2, pattern3;
579 ^super.newCopyArgs(pattern1, pattern2, pattern3)
581 storeArgs { ^[pattern1,pattern2,pattern3] }
583 embedInStream { arg inval;
584 var stream1, stream2, stream3, val1, val2, val3;
586 stream1 = pattern1.asStream;
588 (val1 = stream1.next(inval)).notNil;
590 stream2 = pattern2.asStream;
592 (val2 = stream2.next(inval)).notNil;
594 stream3 = pattern3.asStream;
596 (val3 = stream3.next(inval)).notNil;
598 inval = yield( val1 + val2 + val3 );
607 PstepNfunc : Pattern {
608 var <function, <>patterns;
609 *new { arg function, patterns;
610 ^super.newCopyArgs(function ? { |x| x }, patterns)
612 storeArgs { ^[function,patterns] }
613 embedInStream { arg inval;
615 var size = patterns.size;
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;
622 streams[level] = patterns[level].asStream;
624 vals[level] = val = streams[level].next(inval);
628 inval = f.value(inval, level + 1)
630 inval = yield(function.value(vals));
640 PstepNadd : PstepNfunc {
641 *new { arg ... patterns;
642 ^super.new({ arg vals; vals.sum }, patterns)
644 storeArgs { ^patterns }
647 // returns relative time (in beats) from moment of embedding
651 *new { arg repeats=inf;
652 ^super.newCopyArgs(repeats)
654 storeArgs { ^[repeats] }
655 embedInStream { arg inval;
656 var start = thisThread.beats;
657 repeats.value(inval).do { inval = (thisThread.beats - start).yield };
662 // if an error is thrown in the stream, func is evaluated
664 Pprotect : FilterPattern {
666 *new { arg pattern, func;
667 ^super.new(pattern).func_(func)
669 storeArgs { ^[ pattern, func ] }
671 var rout = Routine(pattern.embedInStream(_));
672 rout.exceptionHandler = { |error|
673 func.value(error, rout);
674 nil.handleError(error)
682 // access a key from the input event
684 var <>key, <>repeats;
686 ^super.newCopyArgs(key)
689 // avoid creating a routine
691 var keystream = key.asStream;
692 ^FuncStream({ |inevent|
693 inevent !? { inevent[keystream.next(inevent)] }
699 var <>condition, <>iftrue, <>iffalse, <>default;
700 *new { |condition, iftrue, iffalse, default|
701 ^super.newCopyArgs(condition, iftrue, iffalse, default)
703 storeArgs { ^[condition, iftrue, iffalse,default] }
705 var condStream = condition.asStream,
706 trueStream = iftrue.asStream,
707 falseStream = iffalse.asStream;
709 ^FuncStream({ |inval|
711 if((test = condStream.next(inval)).isNil) {
715 trueStream.next(inval) ? default
717 falseStream.next(inval) ? default