Also from Andrew Faraday

Where else can you find me?

Friday, 12 February 2016

Sail with me into the Pi (part 2)

We're still talking about Sail by AWOLNation, we're still talking about Sonic Pi. If you haven't read it yet, you might want to take a look at part 1.

In part 1 we wrote a method to collapse the code to play the opening vamp from Sail. So what's next? Well, if you listen to the song, you'll hear a second sound coming in at about the 17 second mark. It's big, low-pitched and declarative. This sound cuts in, shouting about it's presence and, here's the clever part, it spends more than half of it's time silent. It's over powering for the 2 beats it plays for at a time, but then leaves space for the other sounds.

So how do we make it? Well, we start with some analysis. The notes each time are what's known to musicians as an open fifth, or to rock guitarists as a power chord. We know it plays for two beats at a time, and we know it's a rough sound. So first, lets define a synth, at the top where we defined the vamp synth.

I've chosen a saw wave because it sounds rough, powerful and a tiny bit like a distorted guitar. Next, we need to learn the lesson from part 1. Write a method to do what the bass part will do. Namely, use the @bass_synth variable, construct a power chord, and play it for 2 beats. Let's have a look at the code:

Remember part 1, def means we are defining a method, bass_hit is the name of the method and it has one argument meaning root.

On line 25, we are telling the 'play' method to use the variable we named @bass_synth to choose what kind of sound to make.

Line 26 is using the method called 'note' to convert from a named note to the MIDI note number for that pitch, and setting that to a variable named root_note. I've discussed MIDI notes in earlier posts, but the important thing to know here is that it's a number, and each whole number represents one note on the a keyboard.

On line 27 we are building an array, or list of notes. The first is the root_note we set on line 26. The second is 7 notes higher, otherwise known as a fifth. Remember that a power chord is also called an open fifth? This is why. The third one is 12 notes higher, what's known as an octave. Basically, the point where it's the same note, but higher up. We've built the notes for a power chord, based on whichever note we pass in as the 'root' argument.

Line 28 plays the array of notes we just put together. Just like in part 1 we're going to set some of the ADSR variables. We've told it to sustain the note for 2 beats, then fade out pretty quickly, in 2 triplets.

Line 29 will wait for those 2 beats for the note to end before proceeding.

Down on line 33, we're using it. I'm passing in a low E flat as the root, and we hear a big, low pitched power chord. It sounds like this...

We got there quicker this time, all we need to do is write the rest of the chord sequence for the bass hit.

It works, we're leaving all that lovely empty space using the sleep method. And if we have a listen, we hear the chord sequence, marked out in these big power chords.

Now, the next step is obvious, surely? We know these bass hits, and the vamp from part 1 are two parts of the same song, and they will play together. So the first thought might be to just put both bits of code down on the same sheet and listen, right?

(You might have noticed, if you're sharp-eyed, that I've taken out the first vamp method call. More on why I did that later.)

Now, this doesn't work the way you might think. We'll hear the whole sequence of bass_hits, then the entire sequence of vamps. Fortunately, Sonic Pi has a simple way to handle that, something called a thread.

Now, a thread is like another program, started by the program we're writing. The difference is that our program won't wait for the code in the thread to finish before proceeding to the next line. We can put each of these two parts into a thread of it's own with in_thread.

So now when we press ctrl + r to run our code. We hear the two together. They sound a bit like this.

So we've got to the end for this section, well, almost. I'm going to wrap them both in a method. It won't take this code away, or make it shorter, but it will hide it away a little, so we can concentrate on the sequence of the song a bit more.

Okay, so lets look back at the song again for a moment. It starts with the vamp, which goes round its sequence once, then it's joined by the bass.

So all we need to do is set off the vamp_loop method, wait 8 bars for it to finish, then set off both the bass loop and the vamp loop. Oh, and we're going to put that first 'vamp' call back, which just sits before the repeating part. It looks like this:

And sounds like this:

If you'd like to try it yourself, you can see the code to-date here.

Well, that's it for today. We're starting to hear a bit of that slow build-up, but there's more parts to add, more song to play, and we're going to look at more ways of making this code easier to read, understand and change.

As ever, feel free to say hi, I'm known as @MarmiteJunction on twitter.

1 comment: