Blog

Re-tuning and Orchestration of J.S.Bach Chorales from the Passion of St. John

These pieces can be described as a re-tuning and orchestrating the chorales from the St. John Passion into fantasias. All the orchestrations are done by aleatoric algorithms I write in python. I create several variations on each chorale, then listen to them and make modifications until I find one of each that sounds interesting. The process uses subtractive composition, which is where I create a vast array of notes, then judiciously remove over 99% of them, leaving varying degrees of density and sparseness.

The retuning is also done by algorithm. It seeks to chose pitches in each chord that minimize the numerator and denominator of the ratios between the notes. This treats all ratios with low numbers as preferable to those with higher numbers. Just for comparison purposes, each interval in the 12 tone equal temperament scale has the ratio to the next semi-tone in the scale of 2385698798484657/2251799813685248, although it is sometimes approximated as 196/185. Lower numbers make for a stronger sound. The highest ratios in these pieces is often 8/7 or 10/9, with an occasional 27/20, or 25/18 when the algorithm couldn’t find any alternative that preserved the sense of the note as the composer intended. For this I would have had to read the mind of J.S. Bach. Or give it my best guess. I chose the latter.

Here is a zip file of my current favorites:

Zip file of Fantasias on Bach Chorales from the St. John Passion for Large Ensembles

Here are the first two you can listen to here:

bwv245.15 :

# this one has some nice transitions between the instruments of the large ensemble of strings, woodwinds, brass, percussion, and electric guitars.
bwv245.3 :
# this one starts off with a nasty bee buzzing on Ernie Ball Super Slinky Guitar Strings.

All the code is available here: https://github.com/prentrodgers/Auto_Just_Intonation
I can’t post the samples, since I don’t own the rights to them all.

Adaptive Tuning of Herzliebster #27

I made this version after I’ve updated the algorithm to prevent moving the B♮ and F♯ from 1100 and 602 cents respectively. This avoids some of the strange jumps between chords. The solution is a bit crude, but I think I can improve on it. I may need to pay attention to some of the other notes, even though those two are the most common ones in the chorale. Next up is the D♮ at 216 cents.

I added some code to list out the cents and ratios of each chord as it passes by. Here are the first few chords in the chorale, and then two from later. The first four numbers are the cent values of the chords. 1100 cents is a B♮, 602 is an F♯, 216 is a D♮, and 1100 is another B♮. The next set are the intervals in the chord stated in ratios and cents. The algorithm favors low integer numbered ratios, and is willing to go pretty far away from 12 TET to find them. For example the second to the last one includes an 8/7, which is far from 12 TET.

([1100, 602, 216, 1100]) [('1/1', 0), ('4/3', 498), ('5/4', 386), ('5/3', 884)]
([1100, 714, 398, 398]) [('3/2', 702), ('5/4', 386), ('6/5', 316), ('1/1', 0)]
([1100, 714, 398, 602]) [('4/3', 498), ('5/4', 386), ('6/5', 316), ('9/8', 204)]
([1100, 216, 1100, 714]) [('5/4', 386), ('5/3', 884), ('5/3', 884), ('5/4', 386)]
...
([699, 930, 348, 117]) [('7/5', 582), ('8/7', 231), ('7/5', 582), ('8/7', 231)]
([602, 918, 420, 216]) [('5/4', 386), ('6/5', 316), ('4/3', 498), ('9/8', 204)]

The goal of this exercise was to create some code that could automatically turn any 12 TET chorale, real or synthetic, into a just intonation chorale, all by itself. The program reads in the MIDI file and searches for a justly tuned chord that will minimize the size of the ratios in the intervals between notes in a chord, while staying reasonably close to the 12 TET note. It tries to minimize those two values, distance and ratio numerator / denominator size, to come up with cent values for the intervals in each chord. I then check to make sure I haven’t moved either of the critical notes, B♮ and F♯.

A lot of the assumptions I made about adaptive tuning were terribly naive. It’s hard. I thought all I had to do was find a chord that contained only low integer ratios by taking each interval of the chord individually. But that ended up with sub-optimal chords, since each interval competed for what the cent value of a given note should be. I ended up re-voicing those that were the most difficult, running some chords through the algorithm to tune the notes in the chord, then re-voicing them back to the way they were. The numpy function roll() provided a means to transform the chords until they found a good tuning. It takes about three and a half minutes to do a 177-chord chorale. I think I can improve that if I remove some of the debugging code.

Vertical Adaptive Tuning of Herzliebster #21

This is another in the series of Vertically Adaptive Tuning of Herzliebster, with some fixes made to the tuning optimization to search harder for just tunings that meet the criteria I set down. The last one, #17, included some wolf chords. These were caused by conflicts between different intervals in a chord. I check all six intervals between the four notes of each chord. I’d start with note 0 to note 1, then 0 to 2, 0-3, then 1-2, 1-3, finally 2-3. Doing the checks in that order guaranteed that notes 2 and 3 were subject to change by a later comparison interval. That wasn’t going to work, so I made some changes to ensure that a change would only take place if it was for the improved the score of the entire chord, not just one interval at the expense of another.

I’ve had much better results if I try different voicing of chords. For example, I was struggling to find code that would produce good results for the MIDI F# major chord: [66, 64, 61, 46], which is note names: [‘F♯’ ‘E♮’ ‘C♯’ ‘A♯’]. It’s just a revoiced F# major scale, but I could not find a set of intervals where all six resulted in consonant chords. I the four notes two at a time, and that means six choices, which sometimes conflict. But when I run four different voicings through, it finds the best ones every time. The Numpy function roll takes a four-note array and moves it over by 0,1,2,3 places, creating four different chords. There must be some bug in my code, because after I do that, it works great.

for inx in np.arange(4):
result = find_intervals(np.roll(chord_in_1200,inx), range = range)
score = score_chord_cents(result)
if score < best_score: best_choice = result best_score = score

Next step is to start the horizonal optimizations.

Vertical Adaptive Tuning of Herzliebster #17

This is another attempt at creating an adaptive tuning that fit’s my preferences as a composer. This version starts by loading a set of acceptable ratios into an array. I chose to load those that I found when studying the Tonality Diamond to the 31 Limit. It’s a set of the 213 ratios that mathematics can come up with using the overtones and undertones of a note. I stick to those ratios, because they include the most consonant of intervals, along with the challenging but interesting ones.

I then wrote some code to transform a Bach chorale known in the Music21 corpus as ‘bwv244.3’, from the St. Matthew Passion. It’s one I’ve used a few times over the years.

Herzliebster Jesu

Using some python code I wrote, it takes each chord in the chorale, and searches for the optimal based on using the lowest possible integer ratios in the tonality diamond that are closest to the 12 tone equal temperament cents of the notes in the chorale. I optimize based on the sum of three values:

  1. lowest integer ratio, expressed as the numerator and denominator of the ratio
  2. closeness to the 12 TET cent value of the note in the chorale as composed by Bach

I add them up and use that to score, with the lowest score winning a slot in the final chord. I do that for all the intervals in each four notes in each chord in the chorale. Given Soprano, Alto, Tenor, Bass, I optimize the intervals from S to A, S to T, S to B, A to T, A to B, and T to B. That’s six compares. The final chord tuning is the result of wining the lowest score for each interval.

At present, I’m not advanced enough in my exploration of adaptive tuning to consider the prior or future notes. I put that in the “horizontally adaptive tuning” category to be dealt with later.

This one sounds pretty neat. It only goes off into crazy land by around 4:30. I think the leading tones throw my code for a loop.

Vertically Adaptive Tuning of Herzliebster #15

I’ve been looking at different adaptive tuning systems, and none do what I want. My preference, which is probably crazy, is a tuning that will find the optimum tuning for a chord, on my terms. In this case, I want the ratios between notes in a chord to use the lowest possible integer ratios. That means I will favor 7/4 of 9/5, even though it’s typical for a just flatted 7th to use a 9/5. I also favor 7/5 over 10/7, even though it might create some awkward moments.

I brought this about with some python code the attempts to find the 72 EDO tuning for each chord that minimizes the size of the ratios between the notes in a chord. The source material is a real Bach chorale used in the St. Matthew Passion, known as Herzliebster. All chorales have four notes. So I wrote code that evaluated a chord by looking at the ratio distance from each note to every other note. That’s six compares: Given soprano, alto, tenor, bass as SATB, then the combinations to evaluate are S to A, S to T, S to B, A to T, A to B, and T to B.

Then I did the same after changing one of the voices by on 72 EDO step, and scored that. I continued that so that I evaluated all six combinations modified by -3 to +3 72 EDO steps, or 50 cents up and 50 cents down, in 16.67 cent steps. The result was a Vertically Adaptive Tuning of Herzliebster. Vertical means I only looked at each chord all by itself. I haven’t written the code that would permit evaluation of one chord to the previous or succeeding step, which is Horizontal Adaptive Tuning. But it’s a start.

There may be some notes that sound strange here:

The wolves of Just minor #9

This version uses a D minor just scale. I transposed a C minor just scale into D and ended up with these ratios.

In numpy for python:

edo_12_ratio_strings = np.array(['1', '25/24', '10/9', '32/27', '5/4', '4/3', '25/18', '40/27', '55/36', '5/3', '16/9', '50/27', '2'], dtype='

Or in scala form:

! d_minor_just
!
Transposition of a c minor just into d
12
!
25/24
10/9
32/27
5/4
4/3
25/18
40/27
55/36
5/3
16/9
50/27
2/1

There are some real wolves in this scale, almost enough to get me to go back to one of the tempered ones I've used lately. Victorian Rational Well Temperament is wonderful in most keys. But it has the nasty effect of having prominent beating in others. This d minor just scale that I used for this piece has the interesting characteristic of really celebrating the wolves. They scream out at the top of their lungs when the hit some of the keys.

This piece is based on a synthetic chorale manufactured by TonicNet. It's number 3,640, one of many in D minor. It has a pitch class entropy score of 3.28, which is fairly high, but not extreme, compared to others. I used music21 to determine the triad chords used in the chorale:


b minor (2)
F# major (2)
b minor
F# major
b minor
d minor (finally!)
g minor
D major
g minor
F major (3)
a minor
C major
d minor (4)
F major (2)
Bb major
F major

So even though it's in d minor, according to music21, it starts in b minor and ends in F major. I don't think this is typical of Bach. But the way TonicNet works is it tries at every moment to choose the next triad that Bach would have chosen at that time-step of the piece. It doesn't look back to consider what it did previously, except in a very limited way. It's kind of guaranteed to sound like it's just wandering around aimlessly imitating Bach without duplicating his technique.

Listen here:

Fantasy on a Highly Entropic Artificial Chorale in Victorian Rational Well Temperament #5

This is early results of exploring the highly entropic chorales created by the TonicNet model.

I built a chorale generating notebook in python that created 4900 examples using the TonicNet model, and then ran those through an evaluation routine (using muspy) to find those that had the highest degree of pitch entropy. There were at least 1000 that included all 12 tones in the tempered scale.

I then chose a few that were in the key of D major. All were strange and wonderful chorales. TonicNet writes them out as MIDI files, with a kind of piano-roll format of four voices and a certain number of notes in each voice, all 1/16th notes. If a note is being played, then a MIDI number appears in the slot for that time-step.

First

You need some logic to turn this piano roll type notation into notes with duration.

I then repeat each note 15 times, turning every 1/16th note into 1/16th less than a whole note. I then apply masks to turn notes off to create arpeggiations. Or for the woodwinds, I just have long held notes.

I transposed the Victorial Rational Well Temperament from the scala scale archive into the key of D. Some of the ratios may seem kind of extreme, but that’s what was required to accurately reflect the ratios in the temperament when transposed. This is the result of that:

! secor_vrwt_D_major.scl
!
George Secor's Victorian rational well-temperament (based on Ellis #2) in D
12
!
4073/3857
3096/2755
654/551
24284/19285
27841/20871
5436/3857
722832/482125
6107/3857
32472/19285
6863/3857
36336/19285
2/1

I then created a finger piano arpeggio vamp with eight voices, and added a double woodwind quartet (oboes, clarinets, french horns, bassoons) playing slow chords. Both voices simply took the notes that the TonicNet model created. I modified some characteristics, including envelopes, volume, timbre, and other factors. The features are changed at the 1/3 and 2/3 points in the piece. The result is a sweet sounding exploration of what the model thought Bach might do.

Exploring Some Highly Entropic Synthetic Chorales Generated by TonicNet

The traditional 12 tone scale can be described in python code as np.array([‘C♮’, ‘D♭’, ‘D♮’, ‘E♭’, ‘E♮’, ‘F♮’, ‘G♭’, ‘G♮’, ‘A♭’, ‘A♮’, ‘B♭’, ‘B♮’]), or the enharmonic equivalent as np.array([‘C♮’, ‘C♮’, ‘D♮’, ‘D♮’, ‘E♮’, ‘F♮’, ‘F♮’, ‘G♮’, ‘G♮’, ‘A♮’, ‘A♮’, ‘B♮’]). Those are basically the notes that Bach used to notate his music (with the exception of B♮ which was called “H” and “B♭” was called “B”). Go figure.

I’m working on a way to improve the sound of some synthetic chorales generated by the Deep Neural Network model known as TonicNet. I’m most interested in the synthetic chorales that have a high degree of pitch entropy. I use the python library known as muspy to evaluate the generated chorales looking for those that have a high pitch class entropy.

The pitch class entropy is defined as the Shannon entropy of the normalized note pitch class histogram.

The formula according the the muspy documentation is:
Pitch Class Entropy
It basically gives a higher score if the pitches used include a lot of notes not in the root scale of the piece. A score over 3 contains a lot of notes outside the root key.

I used the TonicNet neural network to synthesize around 5000 unique chorales in S-A-T-B format, four voices, any number of notes each. I selected the highest scoring chorales, in terms of Pitch Class Entropy, and studied them for some ideas.

I tried retuning them using some standard Well Temperaments, and obtained some nice results. But I thought I might be able to improve on them if I used an adaptive tuning. William A. Sethares has a paper on the subject here: adaptive tuning.

I still need to code it up. But I thought it would be useful to describe what I am trying to accomplish first.

Balloon Drum Music #35

I’ve made a number of changes over the past few weeks to the code that creates the music files. Many involved fixing bugs. Some of them were associated with the slides, others with varying the duration of some sections probobalistically. There are four paths through the model, and making sure the timing is accurate in each is important. You wouldn’t want the bass flute playing the verse when the balloon drums were on the bridge, for example. I also spent some time making sure that if a part repeated a section, the second time through would be quieter than the first. And the tempo of the trills was off, due to the duration of the notes being longer than they should be. I was adding 10% to each notes duration, and that messed up the timing of the trills. What was supposed to finish 8 ups and downs in the space of a half note, would actually not finish before the note should have ended.

I have some other enhancements I want to work on, but I think I have to leave this piece and move to another.
Listen here: