In the last post I ended by saying we would be able to use GoAudio to generate some simple tunes. In this post we will actually put that to the test. Click the video to hear the end result :)
Frère Jacques | Brother John
Brother John / Frère Jacques is a popular nursing rhyme, and also one of the few things I was taught to play on the piano as a kid. As such, I thought it’d be fitting to try to code this song this time. The notes for playing this are taken from true-piano-lessons.com. The code takes the notes starting in the key of C. (Maybe since it’s in Go, I should have started in G. Feel free to change it).
Generating notes
In the previous posts, we actually ignored the problem of turning notes into frequencies. GoAudio works with given frequencies. So we’ll need to first implement a way to turn the notes into frequencies. For this, I came up with a somewhat hackish solution but I didn’t know a better way other than hardcoding it.
As we’re making the music using the equal-tempered scale we can calculate the frequencies for each
note given a reference frequency. I’m using middle C
as the reference frequency, which can be
derived from concert A (A440
) in this way: middleC = (concertA * math.Pow(2, 3/12)) / 2
.
To follow along with how we derive the frequencies for each note, I suggest taking a look at
wikipedia. The basic idea however is that we have
12 semitones in an octave, so for each octave we have to generate the 12 semitones based off a
reference frequency. Confusingly however, an octave starts at C
and not at A
. (it does loop
around) so we get: C D E F G A B
. Each time we loop around and get back to C, we move up one
octave (meaning higher frequency, thus higher pitch).
You can view a table of all frequencies from C0 (C at octave 0) up to B8 (B at octave 8) here.
The code for this is a bit hacky, so if you have a better way please let me know! :-)
|
|
Here I am generating in both the fourth and fifth octave, from C4 -> B5. If we print the map generated by this code we get (abbreviated)
map[A#4:466.1637615180899
A#5:932.3275230361797
A4:439.99999999999994
A5:879.9999999999999
B4:493.8833012561241
B5:987.7666025122483
C#4:277.1826309768721
C#5:554.3652619537442
...
WAIT:0
]
Also note that I have added WAIT
with frequency 0 in there. This is a bit hacky (and unnecessary
in Go). I’ve added this so we can have some more wait time between playing different sections of the song.
As the default-zero value of a float is zero, and because you can access a map safely even when a
key is missing, this was not necessary. We could just call the map with the WAIT
key even if it
wasn’t inserted and our result would still be correct.
|
|
If you’re reading this, there’s a pretty good chance you know this. But I wanted to highlight it, as
I think this is a pretty cool feature of Go! Yet, in this case, I chose to be explicit about the
intend of the WAIT
note.
Playing a note
Alright, so we have our notes and our frequencies associated with them. We still need to actually generate a wave. For this I’ve introduced the convenience function “play” which takes a few parameters and returns a frame:
play(LookupOscillator, Note, Duration, Notefreqs) -> []Frame
This is quite similar to the past about
ADSR, and just as in that post we
will apply the ADSR envelope to the signal here as well. Notice that ‘play’ takes a LookupOscillator
.
In the previous post we’ve build a few of these for generating sine waves, triangle, square, etc.
This function tells us, play a note for a certain duration (in seconds) with a given oscillator, and return a slice of frames. This function allowed me to quickly change the type of wave, and play around with the result of playing different notes for different timespans.
|
|
Getting the ADSR envelope to sound like I wanted to was honestly the goold old way of trial and error. 😅.
Composing the song
Finally we have everything ready to compose the song. The below code is definitely not pretty, but it gets the job done!
First we generate the notes, then we create a wave table of a shape that we’d like. We
plug this into an Oscillator, and then we start calling the Play
function with the notes that we
need.
For convenience I’ve added some variables to easily adapt the duration of sections of the song. Variables, yeah!
|
|
Download the result, or view the video at the beginning of this post.
Or, viewed in audacity
If you liked this and want to know when I write new posts, the best way to keep up to date is by following me on twitter.