Patterns

Overview

Patterns are some of the most expressive parts of the SuperCollider language. They allow a composer/programmer/musician to express complex musical patterns within a robust network of classes. They are also some of the more challenging aspects for first-time users. In addition to the notes for this course, I encourage you to read through the documentation below to further your understanding.

Pattern Guide
The documentation on Pattern

Pbind

The simplest way to start organizing sound in time is with Pbind. Pbind is an example of a pattern. Pbind creates repeated Synths on the server based on key-value pairs provided by the user. In order to get going with a Pbind, we first need a SynthDef.

The SynthDef below creates a simple sine wave with an amplitude envelope. The final result is passed through Pan2 which places a sound in the stereo field. A pan position (i.e., pos) of -1 places the sound completely in the left speaker, 0 in the middle and 1 on the right. Any other value between -1 and 1 places the sound in some combination of the left and right speaker. Important, Pan2 takes a signal and returns an array of two signals: one for the left and one for the right. Therefore, we do not need to create an array using the duplication operator.

Let's create a really simple Pbind. A Pbind is simply a set of comma-separated key-value pairs. When you use the .play method on a Pbind you create a new object an EventStreamPlayer. This is an object you can create like an audio file player. You can pause, start, stop and resume the pattern as it plays.

Pbind can coordinate with a SynthDef's arguments and generate Synths with different arguments. You must ensure though that the key of the Pbind matches an argument of the SynthDef. Here we use the keys \amp and \freq to set the amplitude and frequency of each synth generated. Note that our SynthDef has matching parameters for those keys.

Your turn: Write a Pbind that plays the \sine_tone instrument from above with the following specifications:

Set the EventStreamPlayer returned by Pbind to a variable so you can stop it. Remember that the keys of the Pbind must match the arguments of the SynthDef.

Write a line of code to stop the player.

Events

Pbind generates Events at intervals specified by the \dur key. What exactly is an event though? An event is simply a dictionary of key-value pairs.

We can also play the event. When an event is played, a Synth is generated on the server using the arguments specified as keys.

A keen eye will notice though that once we play this Synth, the post window seems to show other key-value pairs. It turns out that events are a special kind of dictionary. Every Event has a series of default key-value pairs that can be overriden as well as added. For example, try to play the empty event below. It will make a sound because it is relying upon default key-value pairs.

Below is a snippet of code to unearth some of those default values that are passed everytime an Event is played on the server. There are quite a lot of them.

Most of the above keys are irrelevant unless they match a parameter to the SynthDef. For example, one of the default keys is \lag. Because our SynthDef does not have a parameter \lag, the \lag key-value pair will not impact the sound produced.

In summary, Pbind generates events and overwrites/adds key-value pairs from the default values of the event. For example in the Pbind above, the default key-value of \instrument was overwritten with the value \sine_tone. The only key that operates specially is the \dur key which specifies the time until the next event should play. dur itself has no impact on the sound generated by the synth. The dur key is only relevant if the event is part of a stream of events like the EventStreamPlayer generated from the Pbind method .play.

Your Turn: Play an event using our \sine_tone instrument from above by changing just the default attack and release to be 5 seconds each. You should hear a long swell.

Debugging Tip: Trace

Patterns can get complicated quickly. There is a helpful method that can be applied to any pattern called .trace.

Below shows an example of calling .trace on a Pbind.

Note that we can also trace a specific key as well.

Embedding Other Patterns in Pbind

The power of patterns lie in their ability to embed other patterns within themselves. Our pattern right now is relatively dull and boring. It just plays the same sine wave at the same regular interval. To introduce some more interest, let us embed a pattern. To do so, first let's introduce the pattern Pseq.

Pseq

The pattern Pseq generates a sequence of values from a list. Pseq takes two arguments: the list and the number of times to repeat the list. A pattern is a lazy evaluation. Elements from the pattern are meant to be "pulled" one at a time. A pattern by itself defines a sequence of values to be generated but needs to be converted into a stream to be usable.

Below we define a pattern using Pseq. Once converted to a stream, it will generate the number 100 followed by 200 twice. If we pull from the stream anymore, then we will simply generate nil.

Convert the pattern to a stream and pull from the stream.

Below write a Pseq and store it in a variable ~p that produces the following pattern: 101, 102, 103, 101, 102, 103, 101, 102, 103.

Many patterns that are intended to repeat including Pseq can repeat indefinitely if given the keyword inf.

Using Pseq in a Pbind

Pseq, or any pattern for that matter, can be used as a value within Pbind. When Pbind generates the next event, the next value in any embedded pattern is pulled and added as the value for the key. Here are the first 9 events generated from the pattern below:

( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.5, 'atk': 0.01, 
  'amp': 0.2, 'freq': 400 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.1, 'atk': 0.01, 
  'amp': 0.2, 'freq': 800 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.1, 'atk': 0.01, 
  'amp': 0.2, 'freq': 600 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.5, 'atk': 0.01, 
  'amp': 0.2, 'freq': 400 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.1, 'atk': 0.01, 
  'amp': 0.2, 'freq': 800 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.1, 'atk': 0.01, 
  'amp': 0.2, 'freq': 600 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.5, 'atk': 0.01, 
  'amp': 0.2, 'freq': 400 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.1, 'atk': 0.01, 
  'amp': 0.2, 'freq': 800 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.1, 'atk': 0.01, 
  'amp': 0.2, 'freq': 600 )
( 'instrument': sine_tone, 'sus': 0.05, 'dur': 0.5, 'atk': 0.01, 
  'amp': 0.2, 'freq': 400 )

Things can become more interesting when the patterns don't align perfectly.

Sidenote: Trace for embedded patterns

Recall that trace can be used to print out the values of pattern streams as they are pulled. We can also use trace for embedded patterns if we like. See below.

nil

Once a value in a key-value pair is nil, Pbind will automatically stop the pattern. For example, below the pattern for freq will generate four numbers and then generate nil. Therefore, only four events will be created.

Note that we don't need to stop the player because nil will do that for us once the pattern is exhausted.

Rests

Rests can be embedded in patterns to indicate silence just as a rest would in notated music. A rest can be the value for any key in a pattern and the silence will occur. To notate a rest, simply write Rest().

If the rest is used as a value in the \dur key, then one can pass an argument specifying the length to rest.

Pwhite: Adding Randomness

The pattern Pwhite can be used to generate random values between some lower value and upper value. If the arguments are integers, then the random value is an integer. If the arguments are floats, then the random value is float.

Pwhite draws numbers from a uniform distribution but there are other distributions you can draw from including exponential and gaussian. See the classes Pexprand, Pgauss, Phprand, Plprand and others.

Exercises

Swells

Write a Pbind that plays back the frequencies of an A major chord in infinite succession (440Hz, 550Hz, 660Hz, 880Hz). Make the attack and decay long (around 4 seconds) and randomize the duration from one note to the next using Pwhite. I kept the duration between 1-4 seconds.

Kids

Write a pattern that plays the following musical excerpts using the SynthDef \sine_tone above. It's helpful to think of these notes in terms of their MIDI number. In this system, every note is assigned a unique integer value. For example, the first G is assigned the integer 67. The G#/Ab above that is 68 and so on and so forth. Here, you can create a Pseq of MIDI note numbers that will be converted to frequency like so: Pseq([67, 69]).midicps. These are the first two notes of the melody below.

Frequency in Events

If we look at the previous examples, it is easy to see that the frequency \freq of the Pbind matches the frequency argument from the SynthDef sine_tone. The example below though should seem perplexing then if we switch the Pbind key from \freq to \midinote. Seemingly this should not work. sine_tone does not have an argument midinote. But it does work. So what is going on?

Is this Pbind producing a value for the key freq?

It turns out that there is a complex web of keys that work together to produce the frequency. See the bottom of the Event page for more details. Essentially, the default keys will work behind the scenes to produce the correct frequency. As a user, you can think about your pitches in different terms (MIDI notes, frequency, or scale degrees) depending upon the application. Behind the scenes each event will calculate the correct frequency. The image below shows the details:

pitch_keys.png

For all this to run correctly, you need to ensure that your SynthDef has the argument freq. In general it's best to work at one "level". Think about your notes in either frequency, midi note, or scale degrees. But don't mix and match. That will usually lead to unexpected outcomes as each level is derived from the values of the previous level. Notice, for example, how the key \freq comes from the key \midinote.

Another visualization of those levels: pitch_hierarchy.png

Clocks

Much music is governed by a consistent tempo. Traditional Western-European music adheres to a tempo defined in a score. But plenty of other cultures from Africa, the Carribean, Asia, and South America create music with a constant pulse.

In SuperCollider, we can use clocks to create a pulse and the dur key to define the rhythmic content. The class TempoClock defines a clock with a tempo measure in beats per second. Generally, we think of tempo in beats per minute. Therefore, I typically write a tempo in bpm and divide by 60 to convert to beats per second.

Now that we have defined a clock, we can play a Pbind using that clock. The dur key represents the elapsed time in beats relative to the clock.

Quantization

Clocks also come with the ability to quantize the pattern to a particular multiple of the beat. Quantization is an important concept in music technology that extends to various different concepts. In general quanitzation is the process of mapping a larger set of values to a smaller set.

When we run the code above we are executing it instantly on the clock. But that moment of execution could be anywhere in the clock's pulse. It could be on beat 3.714, for example. Generally, we like patterns to start at the beginning of a measure or on the beat, not on some fractional beat. To ensure that we can use quantization to enfore our patterns to align with a clock's beat or measure.

Quantization is useful when we need multiple patterns to align. Execute the cells below at your own leisure. The quantization will each pattern to align to a measure. Note that you may hear some delay between the time you execute the pattern and when you hear it. The pattern needs to wait for the beginning of the next measure.

Pkey

In many instances it is useful to reference the values from other keys. To do so, we use the pattern called Pkey. If we pass in a key from an earlier event variable, we can get its value.

IMPORTANT: Pkey must be used after the key referenced. Therefore, it matters that our \amp key is calculated after our \freq key.

Other Patterns

Prand

SuperCollider excels at creating algorithmic compositions. To give algorithmic music more interest, we often use randomness to add surprise and unpredictability. There are several patterns devoted to randomness. Prand is the most common.

Prand chooses randomly from a list. The second argument to Prand is the number of times to repeat.

Here is an example where notes are chosen randomly.

We can also add patterns together. Here is an example where the second Prand draws either 0, 12, or -12 to change the octave of the midinote.

Pseg

Pseg is a "time-based pattern". When it is embedded into a Pbind, it operates independently of when an event is created. Pseg is a great to tool to change aspects of a pattern dynamically over time for things like fade-ins and fade-outs or sweeping a filter.

Pseg takes a list of levels, a list of durations, a list of curves, and the number of times to repeat. Pseg is typically used as an embedded pattern within something like a Pbind that uses a clock. The list of durations state how long it takes in beats based on the clock's tempo to move from one level to another level. When a clock is not explicity provided to Pbind's play, there is actually a default clock used at 60bpm. Therefore, Pseg will still work. The beats then can be understood simply as seconds.

In the example below, the Pseg moves the amplitude from 0 to 0.3 linearly across 5 beats and then back down to 0 linearly across 5 seconds. The pattern repeats itself infinitely.

Pfunc

When SuperCollider's provided patterns do not meet your needs, Pfunc is the way to create any kind of pattern using a function. This function optionally takes in the current event as an argument. Therefore, you can access any of the keys in the dictionary declared prior to the instance of Pfunc. For example, below the Pfunc could not access the \pos key.

For each event generated from the Pbind, the function inside Pfunc will be called to pull the next value.

Pbindef

While patterns can wonderfully encapsulate repetition in musical structures, many patterns need to be updated over time. Unfortunately, a Pbind can not be changed dynamically. Fortunately, there is a corollary to Pbind that can be updated incrementally: Pbindef.

Pbindef works exactly like Pbind except it takes one extra argument at the beginning which is the name of the pattern. Note that Pbindef also creates an event stream player and stores it as one of its internal variables (sometimes called instance variables) but the object returned is actually the Pbindef and not the player. Functionally you can use it just the same as before.

While the pattern is running, we can make some incremental changes. Let's update the frequencies. Here we need to pass the name of the pattern and then the key/values we want to change.

An important thing to note is that SuperCollider keeps a global reference of all the patterns playing/stopped. It is typically a good idea to clear out all references to old Pbindefs if you intend to replay the pattern. In certain scenarios, this can lead to strange behavior if you are not careful.

Working with Audio Files and Busses

We can also use audio files and organize them in patterns. This example will uses samples from a drum kit. To get started, we first need to get the path of the folder that has the drum kit files.

If you are running from the SuperCollider IDE you can easily get the path to the folder by running the cell below.

Run the code below to add several audio files to buffers for our drum kit.

We will need a SynthDef to playback those buffers.

Let's make a simple bass drum (also sometimes called a kick drum on a drum set) pattern below.

Here we put a kick drum pattern together with a snare pattern and sync them using quant.

Below write a drum pattern using some of the other buffers. Create a clock and use the clock and quant to sync each pattern together like above.

Write some code to stop your drum pattern from above.

The End!