Wednesday, 17 February 2016

Sail with me into the Pi (part 3)

We're talking about Sonic Pi, we're talking about Sail by AWOLNation. In part 2 we put together a second part, a pattern for playing separate parts together using the `in_thread` method, and we looked at reducing repetition by defining methods for code that's used more than once.

Part three is going to be a lot like part two, we'll take another step in building the music up and do a bit more abstraction.

First things first, we're going to add one more to our collection of synthesisers. It looks like this:


Actually, there's a bit of repetition here. A collection of variables which all end in '_synth', you could even call it a collection of synths. There's a data structure in Ruby which is a collection of values which are labelled, it's called a hash. For our synths, it looks a bit like this:


We can get one of these values out of our hash with squared brackets, it looks like this:


This is another example of a refactor, which, as you may remember from part 1, is a change to our code which doesn't change what it does, but changes something about how it works. But this is only the start of this refactor, we've changed how we structure our data, we need to change the uses of that data. So wherever we see `@vamp_synth` we need to change it to `@synths[:vamp]`. Fortunately, we've reduced enough repetition in our code that there's only two places we need to do that:


How do we know that our refactor was successful? Simple, we run the code and check that it does the same thing. Fortunately Sonic Pi makes it quite enjoyable to do this, we just tap alt + r and listen. It sounds the same, so now we can move on.

The new part we're going to introduce is a riff, if you listen to the track it's played on a plucked cello. It's played immediately after those bass hits are finished, so there'll be a sleep of 2 beats to coincide with that. You'll also notice that it's often repeating the same note three times in a row, three times in one beat.

We can decide from this that we need two methods before we start worrying about what the actual notes are. We need one method to play one of these plucking notes, and one to pluck the same note three times in a single beat. Here they are:


Let's see; I'm defining the plucking method on line 35. I'm grabbing the :riff synth from our synths hash and changing to that synth (line 36), then I'm playing the note it's been given on line 37. You'll notice that I've set attack, decay, and sustain to 0. Skipping the first 3 parts of the ADSR curve, and going right to the release. The note will be short, one triplet. Then we'll wait for the length that's been passed in before proceeding (line 38).

Line 41 defines our triplet method. We know what it should be doing is playing a note 3 times, and you can see that the code says `3.times`, that's pretty self-explanatory, but what about the next part.

Next on line 42 we see some curly brackets. This is another way to define a block in Ruby. Where we saw do and end earlier on, this is doing the same thing. So the code between { and } will be run 3 times. Then we're calling riff_pluck, which we've just defined, passing in the note that's been given to riff_triplet and saying to wait 1 triplet for the next pluck.

So now we can start to write it. The riffs to go along with the two parts we've already written, in full, looks like this.



There's a lot of that, I can't even fit it all on one screen (thus, I can't fit it all in one screen shot). You can probably see that I've already used all of the techniques we've discussed so far to shorten the code. Each use of the pluck_triplet method saves us two more lines, and I've used the `times` method again to repeat those two bars that don't change.

Oh, and it sounds like this:


I can't cut this down any more, we need each line we see, but I can tidy it up a bit, and make it easier to navigate. Well, the obvious place to split this sequence up is when we see that sleep for 2 beats. We're going to make each of these parts a method of its own, named after the first note in it. Each part of this is what's known as a riff, a pattern based on the notes of a chord. So, splitting that sequence up at each sleep, it looks like this:


And now that we've put each riff away in it's own method, the sequence looks like this (I've put it away in a _loop method like the other two parts):


This looks a lot tidier. We haven't got rid of that code, but we have put it away in methods which describe what they are, and the sound is, of course, exactly the same.

Now, we learned in part 2 what we need to get more than one part to play together and at the right time. We call the methods on successive lines, the parts sleep through the part where they're not playing, and each part is wrapped in an in_thread loop. We've met all of these requirements, so we can add it to our existing code like so:


And it sounds like this:


We're starting to get the feel of the song now. You can grab the full code at this stage here.

Next time we're going to look into a bit more of Sonic Pi, how to play samples, sound files, play them differently, and play some drums.

Thanks for reading, as ever, feel free to say hi on Twitter at @MarmiteJunction.

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.

Thursday, 11 February 2016

Sail with me into the Pi (part 1)

Prerequisite: This is a tutorial about Sonic Pi, a live-coding music platform using the Ruby programming language. If you want to code along, first go to the Sonic Pi Website and follow their instructions to download it.

Okay, a bit of background. From time to time I come across a song which becomes an 'earworm'. Getting stuck in my head, struggling to get out. One such song is Sail by AWOLNATION. I've sat and learned it for the guitar, beat-boxed that slow, immersive groove, I've sequenced it on my Network Ensemble project, and last Friday, it was the turn of Sonic Pi.

Now Sonic Pi, in short, is awesome. At a lower level, its pretty familiar to me, it works in the same way as Network Ensemble and Text to Music. When running code in Sonic Pi, it pushes messages to a sound engine (theirs is written in Supercollider), via a network port. The exciting part is that where I've written messages as and when I need them. They've made a very nice Ruby API for sound control, including a number of synthesisers, effects and samples, all ready for people to dive right in to using.

The really cool part is that, Sonic Pi is being used in schools, using music as instant feedback for young people to learn about code. Personally, I know that if I'd had this kind of introduction to code earlier on, it would have been a strong introduction to programming, and brought me much earlier into the trade that has become my life.

Okay, enough background, I used Sonic Pi to create a version of Sail, and I'd like to show you how I did it. The first thing I did was add something to the language, to add some methods which I'd like to see.

Yes, I know I'm starting with something pretty complicated, but trust me, this will make the rest much easier to understand. Here's the code:

module NumberMethods
  def beat; self; end
  def beats; self; end
  def triplet; self.to_f / 3; end
  def triplets; self.to_f / 3; end
  def bar; self * 4; end
  def bars; self * 4; end
end
::Numeric.send(:include, NumberMethods)

So what's happening here is that I'm creating a Ruby module containing some methods, then I'm including that module in the Numeric class. This means that I now have methods named beat, triplet, bar etc. when I use a number in my code. It's fairly simple code, beat and bats return the number itself, unchanged, triplet and triplets return a third of that number, and bar returns 4 times the number.

This is because all of the time operations in Sonic Pi are based on beats. So while usually `sleep 1` in Ruby will wait for 1 second before continuing, in Sonic Pi it will wait for one beat, an amount of time defined by the Beats Per Minute (or BPM). You can use the method `use_bpm` to change the speed, and so `sleep 1` will wait for 1 second if you first call `use_bpm 60` or for half a second if you first call `use_bpm 120`. The song Sail, has 4 beats in the bar, and divides its beats into 3, known in music as a triplet, so that's why I've given myself these methods.

Just to prove that this works, I can write some code using my new methods and the Ruby `puts` method, then run it (alt + r) to see that the expected changes have been made to those numbers.


On the log (right) we can see that 1.beat is, as expected, 1. 1.bar is 4 beats (the number 4) and 2.triplets is two thirds (0.66666666). This means that instead of worrying about our note values, or repeatedly writing the code to divide 1 by 3, we can use these names, known to most musicans, in our code. By making this small change to the way numbers work in Ruby, we have made our code more Domain Specific, meaning we can write our code in a way that's more like the way we would talk to people who have some knowledge in the domain of music.

Okay, we've named our note values, let's have a little look at the notes. At the start of the song, we hear what's sometimes called a vamp, the current chord is played in a way which sets out the rhthmic feel of the piece.

If we analyse this a little further, we hear that it's playing a chord (a collection of notes, played together) on each beat, for the first 2 thirds of each beat, laying out the 'triplet' feel to the song. So what I really want to make the music do is play a combination of notes a given number of times, with this triplet feel.

Once you've got used to writing code, you can usually write what might be described as Speculative Code or, as I like to think of it, Fantasy Code. We write the code we would like to work, then see if we can make it work. We can imagine a line of code which says "vamp Eb and Ab for 2 beats". It would look a little bit like this:

vamp([:Eb4, Ab4], 2)

And there we have the first two beats of the song, right? Well, not quite. There's no method called 'vamp' in Sonic Pi, so we'll have to write one. It looks like this:


Let's break this down a little. Line 12 uses the def keyword, so we are defining a method. This means that when we use the word 'vamp' in future, Ruby will look to this definition to decide what to do. Inside the brackets we are naming two arguments, notes and length, which will be expected when we call the `vamp` method, to tell it how, exactly, to vamp on that occasion.

Line 13 tells Sonic Pi to use the synthesizer named :tri, so when we call `play` on line 15, it will play those notes on the :tri synthesiser.

Line 14 uses the do keyword to start a block, so whatever number is passed in for the 'length' argument, it will run the code between do and the end on line 17 that many times.

Line 15 shows us the first bit of Sonic Pi sound making. The `play` method will play the notes we give it on a synthesiser. We're just passing in the notes that have been set as the 'notes' argument, then there's some more named arguments for the `play` method.

These all belong to the volume envelope, commonly described as an ADSR envelope. We don't want it to take any noticeable time to increase the volume of this note from silence, so I've set the attack argument to 0.01, a negligable amount. Now, I want this vamp to play for the first 2 triplets of each beat, so I'm setting sustain to 1 triplet, so that will play for the first triplet at full volume, then release is the next triplet, the third triplet is silent. This is a strong way to introduce this triplet 'feel' to the song. Notice I've used the `triplet` method we introduced earlier, instead of dividing 1 by 3 repeatedly.

An important thing to remember about the `play` method is that it will not stop your code working until it's done. If we were to call `play` again immediately afterwards, it would play at the same time as the first. For this reason, we need to tell it to wait for 1 beat before playing again. This is what the code on line 16 says.

Line 18 uses the end keyword to finish defining the `vamp` method. We're done.

Now to use it, on line 20 I set the beats per minute to 120, simply deciding how fast I want this to be played.

Then, on line 21, we've got the fantasy code I wrote earlier, hoping we could make it work, and now it does. A quick alt + r and success! It sounds a little like the first two notes of the song.


Okay, so that seems pretty hard work for one chord, played twice. But that's the beauty of it, we've written a method that we can use to play this vamp on any chord for any amount of time. So we can drop in the rest of the opening sequence with a few more vamp methods.


So the vamp method has paid off, we've said "play this for this amount of time" 8 times instead of re-writing the specific instructions on lines 15 and 16 over and over again. Without moving the repeated code into the `vamp` method the instructions in these 8 lines would take 34 lines of code, all of which would be unchanged. With all that near-identical code it is very difficult to read the code and work out what, exactly, it is going to do. So we've made it shorter and easier to read.

It sounds like this:

We've go the introduction, the first 8 bars sounding ok. There's just two little changes I'm going to make. Firstly, the synthesiser the vamp is played on is inside the `vamp` method, so when this grows bigger, we'll have to find the method in order to change this information. So I'm going to define this in a variable, earlier on.


This is a change to the code which won't change what it actually does, known as a refactor. We've done exactly the same thing, slightly differently. This does open up a few possibilities for future changes, however. For one, when there's a lot more code, we can define all of the synths up at the top, so we could change what kind of sound each part will make, without having to first dig up the method they are using. The second is that we could change the synth dynamically, in another part of the code, but more on that later.

The second change I'd like to make is one to the way it sounds. We've added our vamp, and it sounds ok, but we can make it sound better. We're going to use an effect. We do this by putting our play method inside a `with_fx` block, like so:


Now, Sonic Pi provides some very clear and full help for all of it's effects. You just click help on the top-right of the Sonic Pi window.

Then select the Fx tab, at the bottom, then select the effect we're using, in this case, reverb.


So, armed with this information, we can add a few arguments to change the amount of reverb that'll be applied to our vamp:


That's it for today, it sounds good. Try it yourself. Here's all the code in this tutorial, you can just copy and paste it into Sonic Pi (gotcha: Sonic Pi uses alt + v to paste, not ctrl + v). 


module NumberMethods
  def beat; self; end
  def beats; self; end
  def triplet; self.to_f / 3; end
  def triplets; self.to_f / 3; end
  def bar; self * 4; end
  def bars; self * 4; end
end
::Numeric.send(:include, NumberMethods)

@vamp_synth = :tri

def vamp(notes, length)
  use_synth @vamp_synth
  length.times do
    with_fx :reverb, damp: 0.8, room: 0.4 do
      play notes, attack: 0.01, sustain: 1.triplets, release: 1.triplet
      sleep 1.beat
    end
  end
end

use_bpm 120
vamp([:Eb4, :Ab4], 2)
vamp([:eb4, :gb4, :eb3], 6)
vamp([:eb4, :ab4, :eb3], 2)
vamp([:eb4, :gb4, :eb3], 6)
vamp([:eb4, :ab4, :eb3], 2)
vamp([:db4, :f4,  :db3], 8)
vamp([:gb4, :db5, :gb3], 4)
vamp([:db4, :f4,  :db3], 4)


I'm done for now, but keep an eye on this site, there'll be more of this tutorial. Next time, we're going to look at some of those heavy bass sounds and get the two parts playing together.

Thanks for reading, feel free to get in touch on twitter, where I'm called @MarmiteJunction, or drop a comment here. I'm always up for chatting code.