Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / SCClassLibrary / Common / Streams / ListPatterns.sc
blobd4b7298709992e9ed0feaa2915cf51b967f7a8f2
1 Pindex : Pattern {
2         var listPat, indexPat, repeats;
3         *new { arg listPat, indexPat, repeats=1;
4                 ^super.newCopyArgs(listPat, indexPat, repeats)
5         }
6         storeArgs { ^[listPat,indexPat,repeats] }
7         embedInStream { arg inval;
8                 var indexStream, index, item, itemCount;
9                 var listStream = listPat.asStream;
10                 repeats.value(inval).do {
11                         var list = listStream.next(inval);
12                         if (list.isNil) { ^inval };
13                         indexStream = indexPat.asStream;
14                         itemCount = 0;
15                         while {
16                                 index = indexStream.next(inval);
17                                 index.notNil
18                         }{
19                                 itemCount = itemCount + 1;
20                                 item = list.wrapAt(index);
21                                 inval = item.embedInStream(inval);
22                         };
23                         if(itemCount == 0) { ^inval }
24                 };
25                 ^inval;
26         }
29 ListPattern : Pattern {
30         var <>list, <>repeats=1;
32         *new { arg list, repeats=1;
33                 if (list.size > 0) {
34                         ^super.new.list_(list).repeats_(repeats)
35                 }{
36                         Error("ListPattern (" ++ this.name ++ ") requires a non-empty collection; received "
37                                 ++ list ++ ".").throw;
38                 }
39         }
40         copy {
41                 ^super.copy.list_(list.copy)
42         }
43         storeArgs { ^[ list, repeats ] }
46 Pseq : ListPattern {
47         var <>offset;
48         *new { arg list, repeats=1, offset=0;
49                 ^super.new(list, repeats).offset_(offset)
50         }
51         embedInStream {  arg inval;
52                 var item, offsetValue;
53                 offsetValue = offset.value(inval);
54                 if (inval.eventAt('reverse') == true, {
55                         repeats.value(inval).do({ arg j;
56                                 list.size.reverseDo({ arg i;
57                                         item = list.wrapAt(i + offsetValue);
58                                         inval = item.embedInStream(inval);
59                                 });
60                         });
61                 },{
62                         repeats.value(inval).do({ arg j;
63                                 list.size.do({ arg i;
64                                         item = list.wrapAt(i + offsetValue);
65                                         inval = item.embedInStream(inval);
66                                 });
67                         });
68                 });
69                 ^inval;
70         }
71         storeArgs { ^[ list, repeats, offset ] }
74 Pser : Pseq {
75         embedInStream { arg inval;
76                 var item;
77                 var offsetValue = offset.value(inval);
78                 if (inval.eventAt('reverse') == true, {
79                         repeats.value(inval).reverseDo({ arg i;
80                                 item = list.wrapAt(i + offsetValue);
81                                 inval = item.embedInStream(inval);
82                         });
83                 },{
84                         repeats.value(inval).do({ arg i;
85                                 item = list.wrapAt(i + offsetValue);
86                                 inval = item.embedInStream(inval);
87                         });
88                 });
89                 ^inval;
90         }
93 Pshuf : ListPattern {
94         embedInStream { arg inval;
95                 var item, stream;
96                 var localList = list.copy.scramble;
98                 repeats.value(inval).do({ arg j;
99                         localList.size.do({ arg i;
100                                 item = localList.wrapAt(i);
101                                 inval = item.embedInStream(inval);
102                         });
103                 });
104                 ^inval;
105         }
108 Prand : ListPattern {
109         embedInStream { arg inval;
110                 var item;
112                 repeats.value(inval).do({ arg i;
113                         item = list.at(list.size.rand);
114                         inval = item.embedInStream(inval);
115                 });
116                 ^inval;
117         }
120 Pxrand : ListPattern {
121         embedInStream { arg inval;
122                 var item, size;
123                 var index = list.size.rand;
124                 repeats.value(inval).do({ arg i;
125                         size = list.size;
126                         index = (index + (size - 1).rand + 1) % size;
127                         item = list.at(index);
128                         inval = item.embedInStream(inval);
129                 });
130                 ^inval;
131         }
134 Pwrand : ListPattern {
135         var <>weights;
136         *new { arg list, weights, repeats=1;
137                 ^super.new(list, repeats).weights_(weights)
138         }
139         embedInStream {  arg inval;
140                 var item, wVal;
141                 var wStr = weights.asStream;
142                 repeats.value(inval).do({ arg i;
143                         wVal = wStr.next(inval);
144                         if(wVal.isNil) { ^inval };
145                         item = list.at(wVal.windex);
146                         inval = item.embedInStream(inval);
147                 });
148                 ^inval
149         }
150         storeArgs { ^[ list, weights, repeats ] }
154 Pfsm : ListPattern {
155         embedInStream {  arg inval;
156                 var item, index=0;
157                 var maxState = ((list.size - 1) div: 2) - 1;
158                 repeats.value(inval).do({
159                         index = 0;
160                         while({
161                                 index = list.at(index).choose.clip(0, maxState) * 2 + 2;
162                                 item = list.at(index - 1);
163                                 item.notNil
164                         },{
165                                 inval = item.embedInStream(inval);
166                         });
167                 });
168                 ^inval;
169         }
172 Pdfsm : ListPattern {
173         var <>startState;
174         *new { arg list, startState=0, repeats=1;
175                 ^super.new( list, repeats ).startState_(startState)
176         }
178         embedInStream { arg inval;
179                 var currState, sigStream;
180                 var sig, state, stream;
181                 var numStates = list.size - 1;
182                 repeats.value(inval).do({
184                         currState = startState;
185                         sigStream = list[0].asStream;
187                         while({
188                                 sig = sigStream.next(inval);
189                                 state = list[currState + 1];
190                                 if( sig.isNil, { false }, {
191                                         if( state.includesKey(sig), {
192                                                 #currState, stream = state[sig];
193                                         }, {
194                                                 #currState, stream = state[\default];
195                                         });
196                                         currState.notNil and: {currState < numStates};
197                                 })
198                         }, {
199                                 inval = stream.embedInStream(inval);
200                         })
201                 });
202                 ^inval;
203         }
206 Pswitch : Pattern {
207         var <>list, <>which=0;
208         *new { arg list, which=0;
209                 ^super.new.list_(list).which_(which)
210         }
211         embedInStream {  arg inval;
212                 var item, index;
214                 var indexStream = which.asStream;
215                 while ({
216                         (index = indexStream.next(inval)).notNil;
217                 },{
218                         inval = list.wrapAt(index.asInteger).embedInStream(inval);
219                 });
220                 ^inval;
221         }
222         storeArgs { ^[ list, which ]  }
225 Pswitch1 : Pswitch {
226         embedInStream { arg inval;
227                 var cleanup = EventStreamCleanup.new;
228                 var index, outval;
229                 var streamList = list.collect({ arg pattern; pattern.asStream; });
230                 var indexStream = which.asStream;
232                 loop {
233                         if ((index = indexStream.next(inval)).isNil) { ^cleanup.exit(inval) };
234                         outval = streamList.wrapAt(index.asInteger).next(inval);
235                         if (outval.isNil) { ^cleanup.exit(inval) };
236                         cleanup.update(outval);
237                         inval = outval.yield;
238                 };
239         }
242 Ptuple : ListPattern {
243         embedInStream {  arg inval;
244                 var item, streams, tuple, outval;
246                 repeats.value(inval).do({ arg j;
247                         var sawNil = false;
248                         streams = list.collect({ arg item; item.asStream });
250                         while ({
251                                 tuple = Array.new(streams.size);
252                                 streams.do({ arg stream;
253                                         outval = stream.next(inval);
254                                         if (outval.isNil, { sawNil = true; });
255                                         tuple.add(outval);
256                                 });
257                                 sawNil.not
258                         },{
259                                 inval = yield(tuple);
260                         });
261                 });
262                 ^inval;
263         }
266 Place : Pseq {
267         embedInStream {  arg inval;
268                 var item;
269                 var offsetValue = offset.value(inval);
271                 if (inval.eventAt('reverse') == true, {
272                         repeats.value(inval).do({ arg j;
273                                 list.size.reverseDo({ arg i;
274                                         item = list.wrapAt(i + offsetValue);
275                                         if (item.isSequenceableCollection, {
276                                                 item = item.wrapAt(j);
277                                         });
278                                         inval = item.embedInStream(inval);
279                                 });
280                         });
281                 },{
282                         repeats.value(inval).do({ arg j;
283                                 list.size.do({ arg i;
284                                         item = list.wrapAt(i + offsetValue);
285                                         if (item.isSequenceableCollection, {
286                                                 item = item.wrapAt(j);
287                                         });
288                                         inval = item.embedInStream(inval);
289                                 });
290                         });
291                 });
292                 ^inval;
293         }
296 // similar to Place, but the list is an array of Patterns or Streams
297 Ppatlace : Pseq {
298         embedInStream { |inval|
299                 var     consecutiveNils = 0, index, repeat, item;
300                 var streamList = list.collect({ |item| item.asStream });
301                 var offsetValue = offset.value(inval);
302                 var localRepeats = repeats.value(inval);
304                 index = repeat = 0;
305                 while { (repeat < localRepeats) and: { consecutiveNils < list.size } } {
306                         if(inval.eventAt(\reverse) == true) {
307                                 item = streamList.wrapAt(offsetValue - index - 1);
308                         } {
309                                 item = streamList.wrapAt(offsetValue + index);
310                         };
311                         if((item = item.next(inval)).notNil) {
312                                 consecutiveNils = 0;
313                                 inval = item.embedInStream(inval);
314                         } {
315                                 consecutiveNils = consecutiveNils + 1;
316                         };
317                         if((index = index + 1) == list.size) {
318                                 index = 0;
319                                 repeat = repeat + 1;
320                         };
321                 };
322                 ^inval;
323         }
326 Pslide : ListPattern {
327     // 'repeats' is the number of segments.
328     // 'len' is the length of each segment.
329     // 'step' is how far to step the start of each segment from previous.
330     // 'start' is what index to start at.
331     // indexing wraps around if goes past beginning or end.
332     // step can be negative.
334     var <>len, <>step, <>start, <>wrapAtEnd;
335     *new { arg list, repeats = 1, len = 3, step = 1, start = 0, wrapAtEnd = true;
336         ^super.new(list, repeats).len_(len).step_(step).start_(start)
337                         .wrapAtEnd_(wrapAtEnd);
338     }
339     embedInStream { arg inval;
340         var item;
341         var pos = start;
342         var stepStr = step.asStream, stepVal;
343         var lengthStr = len.asStream, lengthVal;
345                 repeats.value(inval).do {
346                         lengthVal = lengthStr.next(inval);
347                         if(lengthVal.isNil) { ^inval };
348                         if(wrapAtEnd) {
349                                 lengthVal.do { |j|
350                                         item = list.wrapAt(pos + j);
351                                         inval = item.embedInStream(inval);
352                                 }
354                         } {
355                                 lengthVal.do { |j|
356                                         item = list.at(pos + j);
357                                         if(item.notNil) {
358                                                 inval = item.embedInStream(inval);
359                                         } {
360                                                 ^inval
361                                         };
362                                 }
363                         };
364                 stepVal = stepStr.next(inval);
365                 if(stepVal.isNil) { ^inval };
366                 pos = pos + stepVal;
367                 };
369              ^inval;
370     }
373 Pwalk : ListPattern {
374                 // random walk pattern - hjh - jamshark70@gmail.com
376         var     <>startPos,     // starting index
377                 <>stepPattern,  // pattern for steps
378                 <>directionPattern;     // pattern should return a stream of:
379                                                         // 1 to move in direction of stepPattern
380                                                         // -1 to reverse the direction of stepPattern
381                                                         // a new direction will be chosen when the walker
382                                                         // reaches a boundary
384         *new { arg list, stepPattern, directionPattern = 1, startPos = 0;
385                 ^super.new(list).startPos_(startPos)
386                         .stepPattern_(stepPattern ?? { Prand([-1, 1], inf) })
387                         .directionPattern_(directionPattern ? 1);
388         }
390         storeArgs { ^[list, stepPattern, directionPattern, startPos] }
393         embedInStream { arg inval;
394                 var     step;
395                 var index = startPos;
396                 var stepStream = stepPattern.asStream;
397                 var directionStream = directionPattern.asStream;
398                 // 1 = use steps as is; -1 = reverse direction
399                 var direction = directionStream.next(inval) ? 1;                // start with first value
401                 while({
402                         // get step, stop when nil
403                         (step = stepStream.next(inval)).notNil
404                 },{
405                         inval = list[index].embedInStream(inval);  // get value/stream out
406                         step = step * direction;        // apply direction
407                                 // if next thing will be out of bounds
408                         if(((index + step) < 0) or: { (index + step) >= list.size }, {
409                                 direction = directionStream.next(inval) ? 1;  // next direction, or 1
410                                 step = step.abs * direction.sign;  // apply to this step
411                         });
412                         index = (index + step) % list.size;
413                 });
415                 ^inval;
416         }