An Approach to Custom Scales in SuperCollider

Posted on Posted in Tutorials

I often work with arbitrary pitch collections in Just Intonation that can change from place to place. As such, I usually deal with my pitches as harmonies or sets of ratios rather than scales or modes, but sometimes it’s nice to be able to do both.

SuperCollider has a built in mechanism for working with scales and tunings conveniently called Scale and Tuning respectively. These are particularly useful with Patterns as you can supply and change the scale and tuning you are using independent of the scale degree. Without this schema, you are stuck dealing directly with the exact pitches which, while straight forward and sometimes useful, can be limiting in certain situations.

Working with Scale and Tuning is pretty simple and well documented but there are a few quirks and redundant things to using it the way I need. My goal is to go straight from an array of ratios to something I can directly plug into a Pattern. For this, I made a simple function that handles all the subtitles:

// a function to convert an array or ratios to a Scale
~makeScale = {|ratios| // supply an array of ratios
Scale( // make a Scale
(0..ratios.size-1), // list the steps in the scale from 0 to the number of ratios
ratios.size, // how many steps we have in our scale
Tuning( // make a Tuning
ratios.ratiomidi // first we convert out ratios to midi
% 12 // then we mod 12 to put everything
).sort // lastly we sort it from low to high

// three JI scales, each with different root
a = ~makeScale.value([1,9/8,6/5,4/3,3/2,8/5,7/4]); // our initial scale
b = ~makeScale.value((5/4)*[1,9/8,6/5,4/3,3/2,8/5,7/4]); // same scale transposed a 5/4
c = ~makeScale.value((3/2)*[1,9/8,6/5,4/3,3/2,8/5,7/4]); // same scale transposed a 3/2

// test it out
\root, 0, // the pitch class of the scale's root
\octave, 5, // the octave of the root with a scale degree of 0
\scale, Pstep([a,b,c],2,inf), // play each scale/tuning every 2 seconds
\degree, Pseq((0..a.size),inf), // walk up the scale
\dur, 0.125 // each note is a 1/16th

Here’s what’s going on and why.

Scale first asks for an array of scale degrees based on the total number of degrees. For something like a major scale in 12TET this would be [0,2,4,5,7,9,11]. Since I want to deal with arbitrary scales sizes and the step sizes will be handled by the Tuning later, I just use (0..ratios.size-1) to make an array from 0 to one less than the number of ratios I supplied. If I give it 5 ratios, this gives [0,1,2,3,4].

Next Scale asks for the number of steps per octave. Since we want each ratio to be its own step, we just use ratios.size here. This effectively maps each ratio in the Tuning to each scale degree. At this point, Scale is just a wrapper around the Tuning, which is the real point of interest.

My way of dealing with the Tuning here is slightly more complicated than it absolutely needs to be, but I chose this approach because it offers the most flexibility.

All you really need is Tuning(ratios.ratiomidi), which would make a Tuning by taking our ratio array and converting it into the “midi number” version. This would be more than enough if we always ensure that our ratios are between 1/1 and 2/1. However, I decided I wanted to be able to be lazy and do things like transpose my ratios like so: (5/4) * [1,9/8,6/5,4/3,3/2,8/5,7/4]. This needed a little more stuff going on under the hood.

With ratios.ratiomidi, we get the the “midi number” array. Then we % 12 to force everything between 0 and 12. Finally we sort the result. This means that even if we supply ratios that are beyond one octave, this will automatically correct them and put put them in the right order for a scale.

Hopefully this is somewhat helpful. Once we have our Scale, we can do all the cool Patterny things exactly the same way as with any other standard scale. I also like to think of Scale in this case as also being usable as a chord. For instance, if you supply a three note tuning representing a chord, you could have your pattern play degrees [0,1,2] which would be the chord. Then if you change the Scale to be a different chord, the notes will change even if the degrees do not. Since the function we made will adjust octaves of out of range ratios, this can automatically handle inversions for us and thus give smooth voice leading basically for free (if that’s something you want). It’s fun to play around with at any rate.

One thought on “An Approach to Custom Scales in SuperCollider

  1. This is helpful. I’ve scratched my head about scales and tunings many times. Particularly because I’m not interested in ET or conventional, i.e, integer, MIDI sequencing.

    I’d be curious to see how you might modify this code to emulate the scale/tuning scheme Stockhausen developed in his early electronic piece, Studie II. To quote Wikipedia: “For the pitches, Stockhausen built a scale in which the interval between successive steps consists of the frequency proportion \sqrt[25]{5}—in other words, the interval of 5:1 (two octaves plus a just major third) is divided into 25 equal parts.” [That should be root-25 of 5, by the way; copy/paste doesn’t do well with math symbols.] Here’s the Wikipedia link:

    Thanks. …edN

Comments are closed.