# Neo-Riemannian Theory for composition, analysis and math education in Sonic Pi

## Introduction

Hello there! I’m very excited to join the community to contribute some ideas and various future lines to consider.

This is the first idea: Although, Neo-Riemannian Theory is mainly used to analyze music, I think that an improvisational approach would be quite interesting in the Sonic Pi environment. For this reason, I was configuring three common functions from Neo-Riemannian Theory (P = parallel, R= relative, L = leading tone) to apply during live improvisations.

On the other hand, I have been working with these functions while teaching introductory classes to SP with Mathematical Music Theory; in this sense, it is also a good concept to intersect computer science, music and mathematics.

I share with you the functions and I propose a simple example with a loop that takes base notes and chords and transforms them according to the function P, R or L; also showing the name of the chord in the log.

## Code and example

``````define :p_neo_riemannian do |note, triad|
x = (chord note, :major)[0]
y = (chord note, :major)[1]
y2 = (y - 1)
z = (chord note, :major)[2]
puts [note_info(x), "minor"]
play  [x, y2, z].ring
x = (chord note, :minor)[0]
y = (chord note, :minor)[1]
y3 = (y + 1)
z = (chord note, :minor)[2]
puts [note_info(x), "major"]
play [x, y3, z].ring
else
puts "Wrong chord, enter a major or minor chord"
end
end

x = (chord note, :major)[0]
y = (chord note, :major)[1]
z = (chord note, :major)[2]
z2 = (z + 2)
puts [note_info(z2), "minor"]
play [x, y, z2].ring
x = (chord note, :minor)[0]
x2 = (x - 2)
y = (chord note, :minor)[1]
z = (chord note, :minor)[2]
puts [note_info(y), "major"]
play [x2, y, z].ring
else
puts "Wrong chord, enter a major or minor chord"
end
end

x = (chord note, :major)[0]
x2 = (x - 1)
y = (chord note, :major)[1]
z = (chord note, :major)[2]
puts [note_info(y), "minor"]
play [x2, y, z].ring
x = (chord note, :minor)[0]
y = (chord note, :minor)[1]
z = (chord note, :minor)[2]
z2 = (z + 1)
puts [note_info(z2), "major"]
play [x, y, z2].ring
else
puts "Wrong chord, enter a major or minor chord"
end
end
``````
``````# Example
4.times do
n = 4
density n / (n/2) do
n.times do
r_neo_riemannian [:a, :f, :d, :e ].tick,
[:minor, :major, :minor, :minor].look
sleep 1
end
n.times do
l_neo_riemannian [:c, :b, :d, :g ].tick,
[:major, :minor, :major, :minor].look
sleep 1
end
n.times do
p_neo_riemannian [:e, :g, :a, :b ].tick,
[:major, :major, :minor, :minor].look
sleep 1
end
end
end

``````

## Future works

1. It would be great to create functions that allow you to easily compose, in this way, you could create music with PLP, LRP and other functions from the nuclear functions P, L and R.

2. Another interesting functionality would be to create an analysis in “improvisational” time of the functions that occur in any chord progression. Thus, you could observe which functions contain a harmonic progression.

3. Some more generative music: take pseudorandomly generated Neo-Riemannian functions to apply them to a set of chords.

4 Likes

Fascinating stuff. I’ve been reading an article on this in wikipedia. Quite a lot to get your head around!

1 Like

This is great to see.

I use the Tonnetz on an iPad, using Lemur, to improvise with these transformations. Though, sadly, Lemur is now deprecated. One iPad is now ‘frozen’, so that I can carry on using Lemur.

I also have two video live streams using my Modular Synths and exploring NRTs (R, L, P) using the SIG (Stochastic Inspiration Generator)

Looking forward to playing with your code.

BTW: I see NRT’s are more than analytical. Certainly soundtracks for LOTR, Rings of Power, Dune etc., all make extensive use of NRT’s. Pat Metheny’s Roots of Coincidence is pretty much all NRT’s (won a grammy as well). Some of John Barry’s Bond cues from the 60s also follow NRT’s.

1 Like

Thanks for commenting and sharing your videos. I was observing on Lemur and Ipad the use of the Tonnetz. I also watched your videos with the modular synth. The code is intended for harmonic progressions, but also with a few changes to the loop it can be played note by note by chord.

On the other hand, there is currently an online tool that associates both the Tonnetz transformation and its dual (generalized to different simplicial complexes) together with the circle of fifths and chromatic in real time on the web. The Tonnetz – One Key, Many Representations
(note that it supports midi controller input)

Of course, NRT is not only for analysis, the study that Lehman does on this transformational perspective in film music, especially, is enriching.

References:

1. Lehman, F. 2014. Film music and NRT. Oxford Handbook.
2. Lehman, F. 2018. Hollywood Harmony: Musical Wonder and the Sound of Cinema. Oxford University Press.

`# @robin.newman : Thanks for the comment and interest in the post.`

@EdgarDelgadoVega well it’s not just about chords. If you do a harmonic reduction of single lines then you have harmony. The harmonic implication of this figure is a shift between Cm to Em and Cm to Abm as thirds based movement between roots which is a notable feature of NRT based music.

``````p = (ring :C4, :D4, :Eb4, :G4, :Ab4)

t = (ring 0, 4, 0, -4)

live_loop :nrt do
current_t = t.tick(:me)

20.times do
play p.tick + current_t
sleep 0.25
end
end
``````

NRT is really an attempt to rationalise non-diatonic movements that occurred in the music of Wagner and later practitioners etc. Riemann’s development of functional notation I, ii, iii, etc., could not adequately explain these types of shift, hence NRT’s.

I know Lehman well.

My point is that Lemur is a good way to improvise NRT’s in a very straightforward manner. Lemur ‘removes’ the instrumental barrier of keyboard competency, as well as ableism, thereby making it more accessible. If I’m playing a eurorack module, then a one finger chord press is a saviour given I’m using foot controllers as well to modulate parameters.

BTW: it was good to listen to your classical guitar performances and your Sonic Pi tracks.

1 Like

I don’t know about Eurorack hardware, but I guess it’s easier to run the NRT stuff simultaneously with Lemur. In this sense, the SP code also removes the barrier of pianistic competence. Following this line, below I attach other (composite) two functions based on Obluda’s thesis entitled:
Topics in Hollywood Scores: Using Topic Theory to Expand on Recent Neo-Riemannian Analyzes of Film Music (2021).

These are the F and N functions of NRT (one example):

``````define :f_transformation do |p, triad|
puts (chord p + 7, :minor)
return (chord p + 7, :minor)
puts (chord p - 7, :major)
return (chord p - 7, :major)
else
puts "Wrong chord, introduce a major o minor chord"
end
end

puts (chord p + 5, :minor)
return (chord p + 5, :minor)
puts (chord p - 5, :major)
return (chord p - 5, :major)
else
puts "Wrong chord, introduce a major o minor chord"
end
end

``````
``````##| Example

live_loop :obluda do
dur = 0.5
density 2 do
6.times do
play f_transformation(:c4, :minor).tick, release: dur
sleep dur
end
6.times do
play n_transformation(:ab4, :major).tick, release: dur
sleep dur
end
end
end
``````

Reference:

1. Obluda, D.C. (2021). Topics in Hollywood Scores: Using Topic Theory to Expand on Recent Neo-Riemannian Analyzes of Film Music (Doctoral dissertation, University of Colorado at Boulder).

Thank you for posting this! I love this stuff (it’s what drew me to study music theory in grad school, back in the day), and I’ve been thinking along similar lines. I was imagining a slightly different syntax and implementation, but I haven’t gotten around to coding any of it.

How does this sound?

1. In terms of syntax, Neo-Riemannian transformations could be chainable methods on `chord` objects. For example:
``````ch = chord(:c4, major)
# ch.p = c minor, root position
# ch.l = e minor, 2nd inversion
# ch.r = a minor, 1st inversion
# ch.l.r.p = g minor, 1st inversion
``````

This has the advantage of capturing the left-to-right direction of composition that most people seem to use. For example, if “LR” means L then R, then `ch.l.r` is easier to read than `r(l(ch))`.

1. The methods could make use of `chord`’s `invert:` parameter to put them in the expected position for smooth voice leading, as shown above. For in classical-ish styles (at least), this was the big advantage of neo-Riemannian theory. On the other hand, I’m open to the idea that it’s best for these functions to return root-position chords.

2. This might be overkill, but what about implementing them by means of Julian Hook’s “Uniform Triadic Transformations”? It’s basically the same as what you’ve done above: a scalar value for root transposition & a chord quality. And it provides a framework that could be easily extended to other transformations, chord types, and even other tuning systems!
Hook, Julian. “Uniform Triadic Transformations.” Journal of Music Theory 46, no. 1/2 (2002): 57–126. https://www.jstor.org/stable/4147678 (behind a paywall, sorry)

1 Like

Thanks for comment @pashultz. Actually, it could be implemented with other MaMuth algebraic groups: `PLR` group, `UTTs` group, `TI` group, `JQZ ` group; or, the group(field) of the affine parabola that works on all these mentioned groups.
As you mention, both the composition `P \circ L` with left or right notation is possible (in the mathematical sense first `L`, then `P`). A simpler syntax by concatenating `PLR` functions seems to me a better way around it than using `((()))`.
The point is to achieve the best implementation of these groups for live coding? Regarding the chord position to achieve a smooth voice leading it could be rearmed by extracting the elements by index `(chord :x, :triad)[ n ]` and regrouping them in the ideal position. The other way would be by chord inversion (as you mentioned). I’ll be watching if you come up with a new code in SP for PLR, or UTTs.

The implementation wasn’t intended to build `UTTs`, but as you mention, I didn’t realize that the way the code is written analogically looks very similar to the definition provided by Julian Hook with his group of `UTTs`. It could be thought that the implementation in SP described above proposes PLR as a subgroup of `UTTs`. Actually, it has made me think that when coding the function it is associated with a specific mathematical definition, even though the result is similar.

1 Like

Hello, a couple of months ago, without knowing anything about Riemann, I experimented on a harmony based on the succession of random triads, starting from one of the notes of the chord. For example, from a triad c,e,g (c major) a triad g,bb,d (g minor) could happen.

The triads could be in the fundamental position or in any of the other two inversions.
As for the melody that was generated while the chord was playing, it was major or minor pentatonic depending on the type of chord that was used at that moment.

I’m going to try to recover the code, it’s somewhere…

1 Like

I did some tonnetz experimentation with ziffers last year using roman numerals / numbered notation and Neo-Riemannian PLR operations. Could make some more generic Sonic Pi version as well at some point. Didn’t continue on working on that as transformations were limited only working for major/minor triads.

1 Like

@Raul We look forward to seeing its implementation.

I just looked at the Ziffersystem documentation. It abbreviates the SP syntax a lot and is great for such a creative and combinatorial purpose.
I just checked the Tonnetz section. Regarding your assertion about transformations limited to triads, indeed, the conventional Tonnetz `[3,4,5]` has only major and minor triads. But there are more possibilities: simplicial complexes with the same transformations but different harmonies. I took the liberty of using the Tonnetz found in the Chord Transformations section and adding one of those mentioned possibilities:

Image taken and modified from Transformations, Chord Transformations - Tonnetz by Miika Alonen

As you can see on the Tonnetz `K_TI[2,3,7]` (TI = Transposition/Inversion), the same Neo-Riemannian operations work on other types of triads. There are 10 other alternative Tonnetz. It will be great to know your opinion about it.

At the moment, over the Tonnetz `[2,3,7]`, I wrote the following Neo-Riemannian progression in Ziffers 2 manually:

``````zplay "R<0.25> 035 025 257 57T ", key: :c, scale: :chromatic, rhythm: "qe"
##| Progression: 035 - P - 025 - R - 257 - L - 57T
``````
``````# variables generales e inicio
use_bpm 90
notas = []
raiz = 51
48.times do |i|
notas[i]=raiz
raiz +=1
end
use_bpm 96
acordes = [[0,4,7],[4,7,12],[7,12,16],[0,3,7],[3,7,12],[7,12,15]] # acoredes maj y min e inversiones
raiz = 0
maj = [0,2,4,7,9,12,14,16,19] # pentatonica maj
min = [0,3,5,7,10,12,15,17,19] #pentatonica min
escala = 0 # escala por defecto maj
acorde_uso = rrand_i(0,5) # acorde uso
acorde_esc = acordes[acorde_uso] # notas acorde
raiz = acorde_esc[[0,1,2].choose] # eleccion una de las notas como raiz
t = 0
fl = "fluid_synth_(1856)_synth_input_port_(1856_0)_134_0" # puerto

# aura, gost whistle 1, bass analog 3, simple brass,Binary_piano2
define :canales do
midi_cc 0,25,channel: 1,port: zy
midi_cc 32,0,channel: 1,port: zy
midi_pc 50,channel: 1,port: zy
sleep 1
midi_cc 0,4,channel: 2,port: zy
midi_cc 32,0,channel: 2,port: zy
midi_pc 22,channel: 2,port: zy#
sleep 1
midi_cc 0,1,channel: 3,port: zy
midi_cc 32,0,channel: 3,port: zy
midi_pc 2,channel: 3,port: zy
sleep 1
midi_cc 0,2,channel: 4, port: zy
midi_cc 32,0,channel: 4,port: zy
midi_pc 64,channel: 4,port: zy
sleep 1
midi_cc 0,24,channel: 5, port: zy
midi_cc 32,0,channel: 5,port: zy
midi_pc 39,channel: 5,port: zy
sleep 1
midi_cc 10,32,channel: 1, port: zy # pan canal 1
sleep 1
midi_cc 10,110,channel: 2, port: zy # pan canal 2
sleep 1
midi_cc 10,74,channel: 3, port: zy # pan canal 3
sleep 1
midi_cc 10,54,channel: 4 , port: zy  # pan canal
sleep 1
midi_cc 10,96,channel: 5, port: zy  # pan canal
sleep 1
midi_cc 0,0,channel: 1,port: fl # banco 0
midi_cc 32,0,channel: 1,port: fl # va a cambiar el banco 25
midi_pc 25,channel: 1,port: fl # programa 25 banco 0
sleep 1

end

define :inicio do
# eleccion acordes
raiz = raiz+acorde_esc[[1].choose] # eleccion raiz
acorde_uso = rrand_i(0,5) # acorde uso
acorde_esc = acordes[acorde_uso] #  notas acorde uso
# limitar octava raiz
if raiz > 11
raiz -= 12
end
t = 8
end

define :bajo do
# notas bajo
midi_note_on notas[raiz]-24, channel: 3, port: zy,velocity: 60
midi_note_on notas[raiz]-12, channel: 4, port: zy,velocity: 60
end

3.times do |i|
# notas acorde
midi_note_on notas[raiz+acorde_esc[i]],channel: 1, velocity: 80, port: zy
midi_note_on notas[raiz+acorde_esc[i]],channel: 2, velocity: 80, port: zy

end
end

define :escala do
# eleccion escala por defecto maj
escala= maj
# los 3 primeros acordes son maj
if acorde_uso > 2
escala= min
end
end

define :melodia do
# notas que contendra melodia
z = rrand_i(5,9)

use_synth :pluck
11.times do
no = rrand_i(0,7)
n = raiz+escala[no]
m = notas[n]
midi_note_on m,channel: 1, port: fl,velocity: 90
play m,pan: -0.6,amp: 2
if no > 4 and no < 8
n2 = raiz+escala[[no-2,no-3].choose]
m2 = notas[n2]
midi_note_on m2,channel: 1,port: fl,velocity: 90
play m,pan: -0.6,amp: 2
end
end
sleep 0.5
midi_note_off m, channel: 1, port: fl
midi_note_off m2, channel: 1, port: fl

sleep 0.5
end
end
end

define :fx do
with_fx :reverb do
with_fx :ixi_techno,phase: (ring 0.5,0.25,0.125).choose do
with_fx :slicer,phase: (ring 0.5,0.25,0.125).choose do
live_loop :buc do
z = [0,1,2].choose
if z == 0 # off loop
stop
end
3.times do |i|
synth :mod_dsaw, note: notas[raiz+acorde_esc[i]],amp: 0.2,attack: 2,sustain: 4,release: 3,cutoff: 80,pan: rrand(-1,1)
end
sleep 2
end
end
end
end
end

define :drum do
live_loop :dr do
z = [0,1,2].choose
if z == 0 # off loop
stop
end
with_fx :echo,pahase: (ring 0.5,0.25).choose do
sample 25,pan: [-1,1].choose,amp: 2
sleep 1
end
end
end

canales()
sleep 1

live_loop :a do
midi_all_notes_off
stop
end
use_real_time
inicio()
bajo()
drum()
escala()
fx()