PS (PStream, Pstream) Pattern that behaves like a Stream 


Part of: miSCellaneous


Inherits from: Plazy


See also: MemoRoutine, PSx stream patterns, PSdup, PSrecur



In general Patterns are stateless. But e.g. for counted embedding in other Patterns the exception of stream-like behaviour is practical. PS might also be used in cases where Streams must not be passed to certain Patterns.


NOTE: Name and implementation of former Pstream has changed with miSCellaneous_v0.9, in compliance with other PSx patterns it's been renamed to PS / PStream, however for backwards compatibility Pstream will still work by subclassing.



Creation / Class Methods


*new (srcPat, length, bufSize, copyItems, copySets)

Creates a new PS object.

srcPat - Source pattern, might also be event pattern.

length - Number of output items, may be pattern or stream, defaults to inf.

bufSize - Size of buffer to store last values, defaults to 1.

copyItems - Determines if and how to copy items, which are which are either non-Sets or member of Sets. 

Takes Integer 0 (or false or Symbol \false), 1 (or true or Symbol \true) or 2 (or Symbol \deep). 

Other values are interpreted as 0. Defaults to 0.

0: original item 

1: copy item

2: deepCopy item

copySets - Determines if to copy Sets (and hence Events). 

Takes Integer 0 (or false or Symbol \false), 1 (or true or Symbol \true). 

Other values are interpreted as 0. Defaults to 1.

0: original Set 

1: copy Set

NOTE 1: The distinction of copying items and sets makes sense in the case of event streams.

Per default Events are copied (copySets == 1), not their values (copyItems == 0). 

By playing Events those are used to store additional data (synth ids, msgFuncs …) 

which is mostly not of interest when refering to the event stream, e.g. with PSx patterns which use 

MemoRoutine - copied Events will not contain this additional data. 

If values of Events or values returned directly by the stream (being no kind of Sets) are unstructured 

then copying makes no sense, this is the normal case, so copyItems defaults to 0.

When going to alter the ouput, you might want to set copyItems to 1 for a PSx returning 

simple arrays or 2 for nested arrays (deepCopy). For deepCopying Events you'd have to set

copySets to 1 and copyItems to 2 (an option copySets == 2 doesn't exist as

it would be contradictory in combination with copyItems < 2).


NOTE 2: Copy options concern copying into PS's buffer as well as 

the output of a Stream derived from the PS. When such a Stream is outputting copies 

this prevents unintended altering of items from srcPat. On the other hand

storing copies in PS's buffer prevents these from being altered unintendedly.



Instance Methods


lastValue

Last value stored in memoRoutine


lastValues

Array of last values stored in memoRoutine, latest value is first in array.

at (i)

Returns ith item of array of last values stored in memoRoutine

(keep in mind reversed order: last value first)

bufSize

Size of array of last values.


srcPat, srcPat_(value)

Instance variable getter and setter methods. 


length, length_(value)

Instance variable getter and setter methods. 


lengthStream, lengthStream_(value)

Instance variable getter and setter methods. 

memoRoutine, memoRoutine_(value)

Instance variable getter and setter methods. 


count, count_(value)

Instance variable getter and setter methods.

Counts each call of next / value / resume / run on the memoRoutine. 

If several Streams are derived from one PS each call of next on a derived Stream

will be counted by the memoRoutine and thus by the PS.


Examples


(

s = Server.local;

Server.default = s;

s.boot;

)


// PS used to store a sequence of 6 events


(

p = Pbind(

\midinote, Pwhite(60, 90, 6),

\dur, Prand([0.2, 0.4], inf)

);


// As the sequence ends with nil, nil is also stored in the buffer of PStream's MemoRoutine.

// Hence to store the whole sequence we must increase the buffer size by 1.


q = PS(p, bufSize: 7);


q.play;

)



// values are shifted, so latest are first


q.lastValues



// play in reverse order


Pseq(q.lastValues.drop(1)).play



// repeat original sequence


Pseq(q.lastValues.reverse).play



// counted embedding of value patterns

// PLx variants default to repeats = inf


(

p = PLseq([ 

PS(PLseq((60..65)), 3), 

PS(PLseq((80..90)), Pwhite(2,5)) 

]); 


x = Pbind(

\midinote, p,

\dur, 0.1

).play;

)


x.stop;



// counted embedding of event patterns


(

p = Pbind(

\midinote, PLseq((55..70)) + Pfunc { [0, [4,5].choose] },

\dur, 0.2

);


q = Pbind(

\midinote, PLseq((80..100)),

\dur, 0.05

);


x = PLseq([

PS(p, Pwhite(2,6)), 

PS(q, Pwhite(2,6)) 

]).play;

)


x.stop;



Keep in mind that repeated streamifying of a PS is just like resuming a Stream (yes, PS behaves like ... a Stream).

For getting a Stream to start at the beginning as defined by the Pattern enclosed by the PS,

you'd have to generate a new PS, e.g. by reevaluating its definition or wrapping 

it into a Function.



p = PS(Pseries(), 5);


// evaluate more than once


p.asStream.all;




// compare


q = { PS(Pseries(), 5) };


// evaluate more than once


q.value.asStream.all;




For recursively generating data see PSrecur. Referring to buffered last values of a PS can 

easily be done with method .at.



// canonical brown movement

// define 3 voices refering to a PS

// use separate PS to collect data

// plot


(

p = PS(Pbrown(65, 90, 2.1), inf, 16);

p.iter.nextN(16);


q = Pfunc { p[5] - 7 };

r = Pfunc { p[10] - 14 };

t = Pfunc { p[15] - 21 };


u = PS(Ptuple([p,q,r,t]), bufSize: 100);


a = Plotter().superpose_(true).plotMode_(\plines);

a.value = u.iter.nextN(100).flop

)


// playback stored pitches


(

v = Pbind(

\midinote, Pseq(u.lastValues.reverse),

\dur, 0.2

).trace.play

)