Composition Toolkit Blog - Generative Techniques

Hello friends. I have started writing a series of “Composition Toolkit” articles for my website. Each post is an in-depth look at a specific composition technique and provides a how-to guide to start experimenting with the techniques and add them to your bag of music tricks. The techniques I’m covering are not bound to any one piece of software, platform, or even genre, though I’m approaching each topic as a “classically-trained” composer. Whenever possible I’m including SuperCollider code that might be helpful for anyone interested in using that platform, but the articles and techniques discussed are not SuperCollider-specific by any means.

I have three posts up with new posts planned every two weeks:

Per Nørgård’s Infinity Series

Claude Viver’s “Couleurs”: Generating Pitch Structures Through Ring Modulation

Euclidean Rhythms Part 1: Maximum Evenness, Maximum Groove

I hope that this blog will be a useful resource for others like me who are interested in experimenting with generative processes, as well as a resource for teachers. A lot of the material for this blog comes from academic literature that focuses on one particular composer or piece; my goal is to generalize and distill those resources into techniques that musicians and composers can add to their creative toolbox. Please let me know if there are any particular topics you would like me to cover!

40 Likes

Great idea. It’s such a specific sound and certainly has potential beyond just imitating Pärt. I will add tintinabulli to the list!

PS – @ElectricaNada, I’d love to hear your experiments if you felt like posting them here.

These are really cool - and nice to see a proper blog format, too! - Thanks for posting.

2 Likes

@_greathorned, thank you for sharing this. A lazyweb question since you have already done a deep dive on this. Is Nørgård’s infinity series always based on a sequence in which the first two integers are 0, 1?

I’m asking because I am trying to think about different initial seeds and then whether one could still use something like the binary number method to jump ahead in the index. Maybe others with more experience with the lower level binary processing know about how to adjust such an algorithm.

1 Like

(as i understand it), the initial degree is always called 0 (so: yes), but the initial interval can be anything (so: no? and one could use different interval(s) in the iteration step as well.)

but poking around a bit i do see things like this paper which defines the norgard sequence has having first interval == +1. so maybe i’m just odd

and now that i’m thinking of it, it doesn’t really matter whether you call the initial value 0 or something else. (it’s an iterative sequence after all)

2 Likes

I’ve been playing around with the series on my norns ever since seeing this thread, and my layman’s take on it is that the first integer in the sequence is the point that everything kind of dances around, and the second integer determines the distance of the steps.

In my case I’ve been using the integer sequence to derive pitches. If I start with 0,1, that 0 is kind of the root of my sequence. The first 16 steps of 0,1 look like this:

0 1 -1 2 1 0 -2 3 -1 2 0 1 2 -1 -3 4

…if I change the first two integers to be 3, 1, the sequence “feels” the same, but the point everything dances around is now 3 (and the intervals are bigger). The first 16 steps of 3,1 look like this:

3 1 5 -1 1 3 7 -3 5 -1 3 1 -1 5 9 -5

Likewise for the second integer, if I start with 0, 12, I basically get a sequence of octave jumps:

0 12 -12 24 12 0 -24 36 -12 24 0 12 24 -12 -36 48

I can’t thank @_greathorned enough for posting this because it’s been really fascinating to play with!

ah you’re right, of course the initial value matters (it too being an interval.)

here’s a lua function if anyone else wants to try this in norns scripts.

-- @param t: a table containing at least 2 values
-- @param step: (optional, default=1) new interval step
-- @returns: nothing; new value is appended to the table in-place
function norgard_compute(t, step)
    step = step or 1
    local n = #t -- new index (zero-based)
    local y -- new value
    if n % 2 == 0 then
        -- even index
        y = -1 * t[n/2 + 1]
    else
        -- odd index
        y = t[((n-1)/2) + 1] + step
    end 
    table.insert(t, y)
end 

seed it with a table like t = {0, 1} (for the classic version)

8 Likes

Oh wow that is a very slick implementation! Thank you for sharing that.

I have to echo @unicity’s gratitude as this has been a load of fun. I have been staring at spreadsheets of the series with different seeding numbers like I’m sculpting mash potatoes and telling my family this means something. Here are some surprising observations. See the spreadsheet screenshot.

First I was surprised that no matter what the first two numbers are for a given series, if you define the first number as the root/center, it always returns to that root/center at the same indices (white columns in the screenshot).

Perhaps a little more surprising is that if you look at that spreadsheet and notice the way each color coded batch counts vertically, you see that index 0 counts by 0s, index 1 counts by 1s, index 2 counts by -1s, index 3 counts by 2s, index 4 counts by 1s.

Those count-by amounts are the infinity series itself for the series that starts 0,1.

This means that you can see a multiples pattern and therefore I think you can compute any index for any initial pair without having to generate the sequence up to that point. You just need to use the binary trick also. Here are the details as I have worked them out:

Let the norgard_integer of index n (indexing beginning 0, not 1) be the nth integer in the infinity series that begins 0,1. (I’m using norgard_integer here as that is what @_greathorned called it in the Super Collider code.)

Let the first germinal interval be the two numbers that start the series.

With the first germinal interval defined as a root and a step from the root, for any [root, first_step] pair, let seed_interval be defined as first_step - root.

The 0,1 seed pair is special because it defines the series in terms of the lowest whole numbers (I think this is true).

All infinity series numbers (for any initial pair) appear to be multiples of the 0,1 series where the multiple is based on the seed_interval as defined above.

Which means the nth integer in the series for any first pair is then:

root + (norgard_integer(n) * seed_interval)

In my screenshot’d spreadsheet, the selected cell is index 14 for the 1,12 pair.

root = 1
seed_interval = 12 - 1 = 11
norgard_integer(14) = -3 (index 14 of the 0,1 series, can be computed quickly with the binary trick)

1 + (-3 * 11) = -32

(Let me know if anyone sees a logic flaw here!)

p.s. And of course the most fun has actually been playing and hearing sequences based on these series.

2 Likes

yes, i think you’re right that, lets call it N_{(0, 1)} is a basis function for N_{(a, b)} with N_{(a, b)} == a + (b-a) * N_{(0, 1)}.

i think we can prove both this and the ‘binary trick’ equivalence pretty easily using induction… good little exercise

(oh, pff, they do exactly that with the binary string at the end of the paper i linked upthread.)


it’s funny that it seems non-trivial (/impossible?) to iteratively generate the “infinity” sequence for an arbitrary length of time… (without requiring an arbitrary anount of memory, that is)

3 Likes

Inspired by this thread, I went a little overboard and created a Nørgård infinity series generating library. Thanks @_greathorned and @zebra for your algorithms!

1 Like

This is great!

Sorry I’ve been slow getting new blog posts up. The schoolyear started and it’s been a lot with online teaching. For some reason everything feels like it takes 3 times longer in a virtual format.

1 Like