Music primitives
Definitions
Scales
This is a demonstration of the functions from MusicPrims. First thing we’ll do is import it.
import MusicPrims
When we compute a major scale we get a list of notes. Each note is a tuple of {key, octave}. Since we didn’t specify an octave, we get octave 0 which is a pretty low C.
major_scale(:C)
Let’s do the same thing with a different octave.
major_scale(:C, 4)
Middle C on a piano is {:C, 4}. This is denoted as midi note 60.
{:C, 4} |> to_midi
The key of F major has one flat.
major_scale(:F)
The key of G majore has one sharp
major_scale(:G)
We can determine which key we’re playing from the number of sharps or flats, depending on the scale type.
key(:major, 0, :sharps)
key(:minor, 0, :flats)
For any note, we might want to know the fifth above.
next_fifth({:C, 1})
next_fifth({:G, 1})
We also might want to know the next fourth, which for G is C
next_fourth({:G, 0})
Thus, the next fourth of the next fifth brings us back to the starting point one octave higher.
next_fifth({:C, 1}) |> next_fourth
To compute the circle of fifths, we iterate on the next_fifth:
Stream.iterate({:C, 1}, &next_fifth(&1)) |> Enum.take(12)
chromatic_scale({:C, 0}) |> Enum.take(4)
Chords
Chord follow a similar computational pattern as scales
major_chord(:F)
The same chord with the first inversion.
major_chord(:F) |> first_inversion
major_chord(:F) |> second_inversion
major_seventh_chord(:F)
Midi
We’ve already seen to_midi for a single note. It’s also defined for note sequences like chords and scales.
major_seventh_chord(:F) |> to_midi
And for chord sequences
Chord patterns
import ChordPrims
chord_syms_to_chords([:I, :IV, :vi, :V], {{:G, 0}, :major})
Enum.map(chord_syms_to_chords([:I, :IV, :vi, :V], {{:G, 0}, :major}), &chord_to_notes(&1))
chord_syms_to_midi([:I, :IV, :vi, :V], {{:G, 0}, :major})
You never give me your money
yngmym = [:i7, :iv7, :VII7, :III, :VImaj7, :ii7, :V7, :i]
chord_syms_to_chords(yngmym, {{:A, 2}, :minor})
chord_syms_to_midi(yngmym, {{:A, 0}, :minor})
Now for something kind of fun. We define the major diatonic progression as:
major_diatonic_progression()
and the minor diatonic progression as:
minor_diatonic_progression()
The diatonic progressions are basically chord scales.
Using the table of usual progressions which we’ve gotten from Walter Piston’s book Harmony (https://www.amazon.com/Harmony-Walter-Piston/dp/B0041OKJDO)
table_of_usual_progressions()
where the keys are the current chord and the values are a list of chords that flow from that chord in normal order. We’ve defined the likelyhood of hitting one or the order with the following list of odds:
usual_odds()
Why have we chosen to represent the usual progressions as numbers instead of symbols? Because it works for any progression of chords scales of which we’ve so far defined two: major diatonic and minor diatonic.
The sum total of the odds is:
usual_odds() |> Enum.sum()
Thus, if your root chord is 1, the odds of the next chord being 4 are 10/26 where the odds of it being 3 are just 1/26.
Using these progressions and odds we can define a random, but well formed, chord pattern generator:
# jere we are going to get a 4 chord progression starting at root chord 1
random_progression(4, 1)
Taking that a step further, let’s define a progression of random length that ends when it comes back to the root which we will convert to an actual set of chord symbols based on a given chord scale. This will give different results each time it is evaluated.
chord_progression = random_progression_to_root(1, major_diatonic_progression())
Let’s reify that to an actual chord progression:
chord_progression |> chord_syms_to_chords({{:G, 0}, :major})
It’s an automatic song writer - well, chords at least, you’ll still have to write the lyrics :)
I’ve played a bunch of these patterns on my guitar and they generally sound pretty good. Play around with strum and durations. Truthfully, several of them reminded me of Radiohead songs - I’d heard that Jonny Greenwood was into programming so maybe this is how they do it :)
Looking at these progressions makes me wonder about the distribution of the length. In other words, each progression is built by walking a directed, but random walk to the root chord. How many chords do we get?
f = &random_progression_to_root/0
freqs = Stream.repeatedly(f) |> Enum.take(100_000) |> Enum.map(&length(&1)) |> Enum.frequencies()
freqs |> Map.keys() |> Enum.sort() |> Enum.map(&{&1, freqs[&1] / 1000})
So, 26 percent of the progressions have two chords, 17 percent have three and so on. This strikes me as a reasonable estimate of the songs in popular music. at least in the Anglo-Saxon world, if not beyond that.