SuperCollider 7 – Scales and Degrees

audio, supercollider

Day 7 of 30 Days of SuperCollider

I guess by now it’s obvious that this whole 30 day plan isn’t going well. It’s not that I’m not into SuperCollider anymore. On the contrary, I’m super deep into it, as well as a lot of other related topics – synths, music theory, midi hardware, etc. I’m learning a whole lot and really enjoying it. But writing about it is the last thing on my mind. So I’m sure there will be more posts about SuperCollider, but I think this will be the last numbered one.

Describing the Problem (slowly)

But I did run into something recently that I thought was worth sharing. It’s got to do with scales and degrees and patterns. SC has a rich library of pattern classes/functions that give you powerful ways of composing sounds into … well, patterns. Pbind is often used for creating an overall composition of a single synth, like so:

(
Pbind(
  \freq, Prand([100, 200, 300, 400, 500], inf),
  \dur, 0.5,
).play;
)  

This will play a random note with one of those five frequencies, and choose another one 0.5 beats later (by default 0.5 seconds). And it will do this infinitely. (The audio samples here have been abridged to something less than infinite length to save on storage costs and upload time.)

The problem with this is that those frequency values are nice round numbers, but from a harmonic viewpoint, they are kind of random. It might be better if we could use the frequencies of the tones in, say, the C Major scale. My first attempt at this was brute force, looking up the frequencies and plugging them in.

(
Pbind(
  \freq, Prand([261.626, 293.665, 329.628, 349.228, 391.995, 440, 493.883], inf),
  \dur, 0.5,
).play;
)

Those are the frequency values for middle C through D, E, F, G, A, and B. This sounds better, but there’s no way I’m typing all those numbers very often. Luckily, that’s not necessary. As long as the synth you’re using has a \freq argument, you can use other types of notation and SC will convert it all to frequency. So this is equivalent to what we just did above:

(
Pbind(
  \midinote, Prand([60, 62, 64, 65, 67, 69, 71], inf),
  \dur, 0.5,
).play;
)

In fact, this is even more accurate because the frequencies are calculated out to something like ten decimal places. Here, 60 is the midi number for middle C. 62 is D, 64 is E, etc.

But what if I wanted to shift it down an octave? I could go through all those numbers and subtract 12. Or, with SC’s amazingly fluent array operators, I can just subtract 12 from the whole array:

(
Pbind(
  \midinote, Prand([60, 62, 64, 65, 67, 69, 71] - 12, inf),
  \dur, 0.5,
).play;
)

I could even put it into the key of D by adding 2:

(
Pbind(
  \midinote, Prand([60, 62, 64, 65, 67, 69, 71] - 12 + 2, inf),
  \dur, 0.5,
).play;
)

All that works great, but the code itself not super expressive. I’d like to just be able to put in the octave I want directly. Lo and behold, there is an \octave parameter! But it doesn’t affect midi notes because those are absolute. Midi note 60 is middle C, so it doesn’t make sense to say “middle C in octave 2”. But what it does affect is \degree. A degree is a more generic specification for a note that can be transposed to any octave. For example, both of the below Pbinds will do the same thing (play a middle C over and over).

(
Pbind(
  \midinote, 60,
  \dur, 0.5,
).play;
)

(
Pbind(
  \degree, 0,
  \octave, 5,
  \dur, 0.5,
).play;
)

Setting \octave to another value will move the C to that octave. Now I was all set, I thought, and did this:

(
Pbind(
  \degree, Prand([0, 2, 4, 5, 7, 9, 11], inf),
  \octave, 5,
  \dur, 0.5,
).play;
)

This assumption was validated by digging through SuperCollider’s Scale class. You can post the value for Scale.major.degrees and it gives you [0, 2, 4, 5, 7, 9, 11]. Exactly what I was using. These are both “degrees”, so it should be correct, right?

But the last sample doesn’t sound quite right, and winds up going much higher than I’d expect. Then I was playing a simple melody on the keyboard and trying to encode that into a sequence and the notes were all wrong. While trying to figure out what was going wrong, I changed the Prand to Pseq in the above example…

(
Pbind(
  \degree, Pseq([0, 2, 4, 5, 7, 9, 11, 12], inf),
  \octave, 5,
  \dur, 0.5,
).play;
)

This plays the notes in order, rather than randomly, so should give you the scale from C to B. (I threw on a 12 to finish out the scale the way we’re used to hearing it.) But it’s all wrong! Skipping notes and going way too high!

Looking around the net for examples, I finally realized that this works:

(
Pbind(
  \degree, Pseq([0, 1, 2, 3, 4, 5, 6, 7], inf),
  \octave, 5,
  \dur, 0.5,
).play;
)

Here the scale actually goes from 0 to 6 for C, D, E, F, G, A, B. I added on the 7 to finish it out on the next C.

The Aha Moment

OK, so it seems like what’s happening is that Pbind‘s \degrees are like indexes into the degrees defined by Scale.major.degrees. Yup, they’re using the same term to define two related but different concepts. Sigh. In other words, \degree sets the number of the note in the scale you are using. By default, that seems to be the C Major scale. This is validated looking at the source code for pitchEvent:

pitchEvent: (
  mtranspose: 0,
  gtranspose: 0.0,
  ctranspose: 0.0,

  octave: 5.0,
  root: 0.0,                      // root of the scale
  degree: 0,
  scale: #[0, 2, 4, 5, 7, 9, 11], // diatonic major scale
...

Here, I discovered that \scale is another Pbind parameter. So if I want to set the scale explicitly, I can say:

(
Pbind(
  \scale, Scale.major.degrees,
  // or...
  // \scale, [0, 2, 4, 5, 7, 9, 11],
  \degree, Pseq([0, 1, 2, 3, 4, 5, 6, 7], inf),
  \octave, 5,
  \dur, 0.5,
).play;
)

Or, you could use a minor scale, which is defined as [0, 2, 3, 5, 7, 8, 10]

(
Pbind(
  \scale, Scale.minor.degrees,
  \degree, Pseq([0, 1, 2, 3, 4, 5, 6, 7], inf),
  \octave, 5,
  \dur, 0.5,
).play;
)

Or you could use one of the more than one hundred scales built into Scale, or create your own. Here’s a small sample of what’s included:

Looking at the pitchEvent code again, I realized that it’s also now more expressive to express the key of the scale you want to use, by using the \root parameter. For example, to use a D Major scale, set \root to 2. This is a bit confusing maybe. You might think we want to set it to 1 since we just established that a degree of 1 is D. But this is operating on the scale definition level, so it’s operating on semitones, not the tones of the final scale.

(
Pbind(
  \scale, Scale.major.degrees,
  \degree, Pseq([0, 1, 2, 3, 4, 5, 6, 7], inf),
  \root, 2,
  \octave, 5,
  \dur, 0.5,
).play;
)

The last thing I’ll say is that if you really, really wanted to use semitones in the \degree parameter, you could set up your \scale to include all the degrees in an octave:

(
Pbind(
  \scale, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
  \degree, Pseq([0, 2, 4, 5, 7, 9, 11, 12], inf),
  \octave, 5,
  \dur, 0.5,
).play;
)

It works, but I don’t think this is the normal way of doing things.

Also, none of this takes into account custom stuff like more or less than 12 notes per octave, detuning, transposing, etc. But if you’re already into that, this post is probably telling you stuff you already know.

The Result

All this is to say that I can now do stuff like this:

(
Pbind(
  \scale, Scale.major.degrees,
  \degree, Prand([0, 1, 2, 3, 4, 5, 6, 7], inf),
  \octave, Pxrand([Pn(4, 8), Pn(5, 8), Pn(6, 8)], inf),
  \dur, 0.5,
).play;
)

Here I’m using a major scale and a random degree. Then I’m playing eight notes in one of the octaves 4, 5, or 6, randomly chosen, never repeating (\Pxrand), infinitely. Not amazing in itself, but opens some creative possibilities.

One thought on “SuperCollider 7 – Scales and Degrees

  1. I guess I should note that all those [0, 1, 2, 3, 4, 5, 6, 7]s could be replaced by (0..7). Not really the point of the post, but someone is bound to point it out! 🙂

Comments are closed.