Manipulating pattern data

Merging (interleaving) independent streams

Suppose you wanted a pattern that generated pitches in a lower range 70% of the time, and a higher range the other 30%. For purely random patterns, this is simple because the pattern for each range has no memory (the next value does not depend on the previous value in any perceptible way). The random number generator patterns (Pwhite) return one element before yielding control back to the "selector" (Pwrand).

\degree, Pwrand([Pwhite(-7, 11, 1), Pwhite(7, 18, 1)], #[0.7, 0.3], inf)

This does not work if the ranges need to keep their own integrity. For that, Pnsym1 is ideal. We create a dictionary with named patterns, each of which maintain their own streams. Then we choose randomly between their names, picking one value from whichever stream is chosen this cycle.

This use of Pseries is essentially a random walk among scale degrees. It has more linear continuity than the equal distribution generated by Pwhite. Even though the higher range interrupts from time to time, the continuity should still be audible.

(

var melodies = (

lowMelody: Pseries(4, Prand(#[-2, -1, 1, 2], inf), inf).fold(-7, 11),

highMelody: Pseries(14, Prand(#[-3, -2, 2, 3], inf), inf).fold(7, 18)

);

p = Pbind(

\degree, Pnsym1(Pwrand(#[lowMelody, highMelody], [0.7, 0.3], inf), melodies),

\dur, Pwrand(#[0.25, 0.5], #[0.4, 0.6], inf)

).play;

)

p.stop;

Reading an array forward or backward arbitrarily

Here's an interesting one. We have an array of possible output values, and we want the pattern to move forward or backward through the array depending on some kind of user input.

There is actually a pattern that handles this already, based on the standard programming concept of a (random) walk. In a random walk, there is an "observer" who is at a position within an array. The observer moves randomly by some number of steps forward or backward. In the SuperCollider pattern implementation, Pwalk, the steps don't have to be random. So here, we determine the step size from a slider.

In general, GUI objects should not be used for data storage. The approach here is to save the step size into a variable, and then refer to that variable in the Pwalk pattern.

(

var pitches = (0..14), // replace with other pitches you want

move = 0,

window, slider;

window = Window.new("Mouse Transport", Rect(5, 100, 500, 50));

slider = Slider.new(window, Rect(5, 5, 490, 20))

.action_({ |view|

move = (view.value * 4 - 2).round;

})

.value_(0.5);

window.front;

p = Pbind(

// Pfunc is the direction to move through the array

// it could be anything

// - could read from MIDI or HID and convert it into a step

// - could be a GUI control, as it is here

\degree, Pwalk(pitches, Pfunc { move }, 1, 7),

\dur, 0.25

).play;

)

p.stop;

Third-party extension alert: The Pwalk pattern shown here moves forward and backward in a preset array. To do the same thing with the results of a pattern, see Pscratch in the ddwPatterns quark. An especially fun use of Pscratch is to use it on an event pattern like Pbind, skipping around in a series of fully realized note events.

Changing Pbind value patterns on the fly

Patterns are converted into streams to generate values (or events). By design, there is no way to access the internal state of the stream. This means, for Pbind and similar patterns, the streams producing values for the event keys are invisible. So, it isn't possible to reach inside the stream and change them while the pattern is playing.

What we can do instead is base the Pbind on pattern proxies -- objects that take the place of a pattern. The PatternProxy is a single object that creates a single stream within Pbind, but it looks for its values to the pattern and stream contained inside the proxy. Changing the proxy's pattern replaces the stream, without having to touch the Pbind's closed box.

In the first example, pattern proxies are held in environment variables, and they can be manipulated through those variables.

(

~degree = PatternProxy(Pn(Pseries(0, 1, 8), inf));

~dur = PatternProxy(Pn(0.25, inf));

p = Pbind(

\degree, ~degree,

\dur, ~dur

).play;

)

~degree.source = (Pexprand(1, 8, inf) - 1).round;

~dur.source = Pwrand(#[0.25, 0.5, 0.75], #[0.5, 0.3, 0.2], inf);

p.stop;

Another way is to use Pdefn, which is a global namespace of proxies for value patterns. (Because of the different requirements for handling values and event patterns, there are two namespaces: Pdef for event patterns like Pbind, and Pdefn for value patterns such as \degree and \dur here.) Storage is all taken care of for you, no need for variables of your own.

(

Pdefn(\degree, Pn(Pseries(0, 1, 8), inf));

Pdefn(\dur, Pn(0.25, inf));

p = Pbind(

\degree, Pdefn(\degree),

\dur, Pdefn(\dur)

).play;

)

Pdefn(\degree, (Pexprand(1, 8, inf) - 1).round);

Pdefn(\dur, Pwrand(#[0.25, 0.5, 0.75], #[0.5, 0.3, 0.2], inf));

p.stop;

Third-party extension alert: The ddwChucklib quark defines a third way of doing this, using object prototyping (based on Environments) to create objects that encapsulate all the information needed to perform a musical behavior. Patterns stored in the prototype's variables are automatically available as pattern proxies to the object's pattern, making it easier to create complex, malleable "processes" which can be replicated as separate objects that don't interfere with each other. It's a step toward object-oriented modeling of musical behaviors without requiring hardcoded classes that are specific to one piece or another.

Previous: PG_Cookbook01_Basic_Sequencing

Next: PG_Cookbook03_External_Control