An Approach to Custom Scales in SuperCollider

September 26, 2014 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
(
Pbind(
\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
).play
)

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.

Post to Twitter Post to Facebook Post to Google Buzz Send Gmail

SuperCollider Difference Tone Finder

June 25, 2013 in Music, Tutorials

Something that I need quite often is to find the difference tone between various pitches. For those unfamiliar with difference tones, it is a physical acoustic phenomenon where when any two frequencies are played simultaneously, you hear the difference between them (the higher minus the lower). If you’ve been around a pair of high instruments playing together you may have noticed a third, lower tone sounding below them. Or if you’ve heard power chords on a distorted electric guitar you actually hear a note an octave below the lowest note.

I’ve been working a lot with harmonies based on a set of just intonation ratios and amplifying their difference tone. When only dealing with two pitches at a time, it’s easy enough to just subtract them but once you have three or more, you must subtract every pair and find the lowest to know what the “primary” difference tone is. I’ve done this by hand before and it can be quite time consuming so I finally got around to making a little tool in SuperCollider to do it for me. It’s really simple: it loops through an array of ratios or frequencies, adds the difference of every pair to a list, sorts that list, and returns the lowest (first in the sorted list) difference tone. Here’s an exammple:

JustIntonationTools.differenceToneRatio([1,6/5,8/5,9/5]).asRational;

will return 8/5, which is the lowest ratio of all the difference tones in that set. Maybe someone out there will find a use for this in their work but it’s something I need constantly. The JustIntonationTools class currently only has this one method but I’m sure I’ll be adding more over time. It can be found on my github if you’d like to play with it. I should point out that it requires the TuningLib quark created by Charles Hutchins as he has a great method to adjust ratios to be within an octave.

Post to Twitter Post to Facebook Post to Google Buzz Send Gmail

ByteBeat Shell Script

April 28, 2013 in Tutorials

Recently I have been playing around with ByteBeat. You can play around with these in your browser here if you are interested. SuperCollider can also do this as of version 3.5 (although I’ve noticed some differences in the output) if you want more control. I however felt like being able to play with these in a more brute force way: directly from terminal.

Since the workflow for messing with these is always the same I decided to make a little shell script to speed up the process. I’m on a Mac so I have to use sox for playback. If you are on Linux you’d need to change the pipe to /dev/audio or /dev/dsp unless you wanted to use sox instead.


#!/bin/bash

# This script creates and plays a simple ByteBeat
# ARGS
# $1: a string with the ByteBeat algorithm e.g. "((t * 3) & (t >> 5))"
# $2: the name of the file to be creates (without an extension)
###
# make the c file
echo "main(t) {
for( t = 0;;t++)
putchar( $1 );
}" > "$2.c"
###
# compile the source
gcc "$2.c" -o "$2"
###
# play it with standard ByteBeat settings
./"$2" | sox -traw -r8000 -b8 -u - -tcoreaudio

Save that to a file called something like bytebeat.sh and make it executable with chmod 755. after that you can just run

./bytebeat.sh "((t * 3) & (t >> 5))" test

and automagically you get 8-bit goodness right from your terminal. There’s a slight delay between running the script and hearing the output because it needs to compile so I wouldn’t necessarily perform with this (unless you get really good a timing it). Now this could be modified to let you specify the arguments for sox or whatever… but meh. This is just intended as a quick and dirty way to look like you are more of a hacker than you really are.

Post to Twitter Post to Facebook Post to Google Buzz Send Gmail

“Chromatic” Just Intonation – fast version

January 26, 2013 in Music, Tutorials

About a month ago I talked here about the math required to take a set of ratios and generate a set of +/- 50 cent alterations for tuning a chromatic scale to any 12-tone just tuning. There were quite a number of steps and a lot of math as I wanted to show how to do it in a way that could be applied to any programming language (or done by hand if you are feeling masochistic). Now I’m going to show a shortcut if you are specifically using SuperCollider.

SuperCollider has a method called ratiomidi that takes a ratio and converts it to the float MIDI number version. This replaces the need to multiply the ratios by a fundamental and mod 12 it as, because all our ratios are within an octave, everything will be from 0.0 through 11.99. In other words: it gives us tuned versions of pitch classes. This one simple method removes 2 of the previous steps.

[1,17/16,9/8,6/5,5/4,4/3,10/7,3/2,8/5,12/7,7/4,15/8].ratiomidi;

gives us

[ 0, 1.0495540950041, 2.0391000173077, 3.1564128700055, 3.8631371386483, 4.9804499913461, 6.1748780739571, 7.0195500086539, 8.1368628613517, 9.3312909439626, 9.6882590646912, 10.882687147302 ]

Now that we have that, like before we need to get the cents in the +/- 50 cents, here in decimals. Once again we just need to subtract a series of integers from 0 to 11 like so:

( [1,17/16,9/8,6/5,5/4,4/3,10/7,3/2,8/5,12/7,7/4,15/8].ratiomidi – (0..11) ).round(0.01);

which gives us

[ 0, 0.05, 0.04, 0.16, -0.14, -0.02, 0.17, 0.02, 0.14, 0.33, -0.31, -0.12 ]

Great, that was easy! Now, you may notice by comparing it to the result we got from the other post that although the numbers are all the same, they aren’t in the same order. That is because before we based the tuning on A being the fundamental/tonic. Here C (array slot or pitch class 0) is the tonic. This is fine if you want C to be the tonic but what if you don’t? Simple! Arrays in SuperCollider also provide a method called rotate(numPlaces) which, as you might guess, shifts items left or right by a number of slots. So for our final version of this:

( [1,17/16,9/8,6/5,5/4,4/3,10/7,3/2,8/5,12/7,7/4,15/8].ratiomidi – (0..11) ).round(0.01).rotate(9);

gives us

[ 0.16, -0.14, -0.02, 0.17, 0.02, 0.14, 0.33, -0.31, -0.12, 0, 0.05, 0.04 ]

which is exactly what we got in the old example. Why are we using 9? If you prefer to think about this as a transposition operation then T9 = A. You could also put a -3 in there and you would get the exact same result. Totally depends on how you want to think about it.

Post to Twitter Post to Facebook Post to Google Buzz Send Gmail

Notes Between “Chromatic” Just Intonation

December 31, 2012 in Music, Tutorials

Yesterday I talked about how I’ve been taking MIDI note numbers and remapping them to different tunings here. If you only need to deal with discreet chromatic pitches this is enough; but what about if you are using pitch bends? Say you are bending a pitch up a half-step. If you simply add the bend amount to the initial note, you’ll get a sudden jump by the difference in tuning when you hit the next step. Let me illustrate this:

Starting Pitch: middle C + 16 cents (60.16)
Ending Pitch: C# – 14 cents (60.86)

If we have our pitch bend data so that it gives +1.0 is a half-step and add it to the MIDI note look what happens:

Starting Pitch + Bend up 0.25: 60.41
Starting Pitch + Bend up 0.5: 60.66
Starting Pitch + Bend up 0.75: 60.91 <- higher than our desired ending pitch
Starting Pitch + Bend up 1.0: 61.16 <- even higher

This is because here pitch bend is being treated as an alteration to the note rather than to the index of our tuning. The trick is that rather than linear bending across a linear range, we now want linear bending across a non-linear range (since each step is not a different distance from its neighbors). We need to take a few additional steps to make this work correctly. Here we go (in SuperCollider again):

First, to make our pitch bends map to floats where a delta of 1.0 = a half-step, you need something like this:

bend.linlin(8192,8874,0.0,1.0,nil).round(0.01) // .linlin maps a linear range to an other linear range

The first two arguments depend on what format your pitch bend data is in. In my case, it’s a 14-bit integer where 8192 is no bend and 8874 is up a half-step. The 0.0,1.0 is the new range these numbers will be. nil tells it to not clamp values (so this works with higher and lower values than those given). Finally .round(0.01) rounds the output to the nearest cent (you could skip that if you’d like).

Now for some magic! Arrays in SuperCollider have a method called blendAt() which takes a float as an index and spits out a linearly interpolated value between the neighbors around it. Here’s an example:

a = [ 2, 3];
a.blendAt(0.5); // returns 2.5 since the index 0.5 is half way between index 0 (2) and index 1 (3)

This behavior can be easily implemented in other languages as well of course but it’s nice that it’s just there in SuperCollider.

(
var tuning, note, bend, val, tunedNote;

tuning = [ 0.16, -0.14, -0.02, 0.17, 0.02, 0.14, 0.33, -0.31, -0.12, 0, 0.05, 0.04 ]; // our tuning
note = 60; // starting pitch
bend = 1.0; // bend amount

val = note + bend; // add the note and the bend

tunedNote = val + tuning.blendAt( val % 12, ‘wrapAt’ ).round(0.01); // magic!
)

Here’s what’s happening. We start by adding the pitch bend to the note like before (val). Next we convert that to an index by using mod 12. ‘wrapAt’ lets the last value in the array blend with the first in the array (so you can bend between 11 and 0). This spits out the correctly interpolated +/- cents for our new note. We then add this to val to get the real offset. Here’s some example output:

note = 60, bend = 0.0: 60.16 <- initial note
note = 60, bend = 0.25: 60.33
note = 60, bend = 0.5: 60.51
note = 60, bend = 0.75: 60.68
note = 60, bend = 1.0: 60.86 <- bend up a half-step
note = 61, bend = 0.0: 60.86 <- up a half-step with no bend

Now all bent values are scaled correctly and a bend of +1.0 gives the same result as just playing the next higher note with no bending.

Post to Twitter Post to Facebook Post to Google Buzz Send Gmail