124 lines
6.1 KiB
Text
124 lines
6.1 KiB
Text
|
title:: Pattern Guide Cookbook 02: Manipulating Patterns
|
||
|
summary:: Manipulating pattern data
|
||
|
related:: Tutorials/A-Practical-Guide/PG_Cookbook01_Basic_Sequencing, Tutorials/A-Practical-Guide/PG_Cookbook03_External_Control
|
||
|
categories:: Streams-Patterns-Events>A-Practical-Guide
|
||
|
|
||
|
section::Manipulating pattern data
|
||
|
|
||
|
subsection::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 ( link::Classes/Pwhite:: ) return one element before yielding control back to the "selector" ( link::Classes/Pwrand:: ).
|
||
|
|
||
|
code::
|
||
|
\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, link::Classes/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 link::Classes/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.
|
||
|
|
||
|
code::
|
||
|
(
|
||
|
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;
|
||
|
::
|
||
|
|
||
|
subsection::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, link::Classes/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.
|
||
|
|
||
|
code::
|
||
|
(
|
||
|
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;
|
||
|
::
|
||
|
|
||
|
strong::Third-party extension alert:: : The link::Classes/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 strong::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.
|
||
|
|
||
|
subsection::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 strong::pattern proxies:: -- objects that take the place of a pattern. The link::Classes/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.
|
||
|
|
||
|
code::
|
||
|
(
|
||
|
~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 link::Classes/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: link::Classes/Pdef:: for event patterns like Pbind, and Pdefn for value patterns such as code::\degree:: and code::\dur:: here.) Storage is all taken care of for you, no need for variables of your own.
|
||
|
|
||
|
code::
|
||
|
(
|
||
|
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;
|
||
|
::
|
||
|
|
||
|
strong::Third-party extension alert:: : The strong::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: link::Tutorials/A-Practical-Guide/PG_Cookbook01_Basic_Sequencing::
|
||
|
|
||
|
Next: link::Tutorials/A-Practical-Guide/PG_Cookbook03_External_Control::
|