Wednesday, 5 February 2014

Making music with Ruby and Beep (Linux only)

I've not posted in a long time, I know. This is mostly because I've explained most of what I know about Pure Data which can be explained in a blog post without relying 100% on prior knowledge. So this is a little about Ruby, and a little about Linux. A nice, short topic which can get you writing a little bit of code in a short time. 

Firstly, you will need two applications, Ruby and Beep, these are easily installed with a simple command. On Ubuntu distributions you can use apt-get, like so. 


If you are using a Redhat variation of Linux, such as CentOS you might need to use yum instead. 

Now, beep is an application for controlling the internal speaker in most computers (towers), although for laptops it will usually work through the sound card, with the in-built speakers. So, to use it in it's simplest form, use this command:


Unsurprisingly, your computer will make a beeping sound. It's not very interesting, or very musical, but it beeps. 

There are two problems you may well run into at this stage, firstly, it might not beep at all. If this is the case, I suggest you check out this forum topic, for some linux jiggery pokery on fixing that issue. You may also find that it comes through VERY LOUDLY, if this happens, try alsamixer, which usually has a volume level entitled beep, which you can navigate to and lower using the arrow keys.

Back to beep, a single beep, on it's own, or a few one after another, still don't really sound musical, but we can take a little more control. As with most command line applications, you can bring up a help page by using the -h command, like so.


 Okay, so this may be a little baffling if you're not used to using the command line, but basically there's a list of flags, preceded by a hyphen(-), which you can add to the command while calling beep, to change change the result in one way or another. Some of these would be followed by an attribute (after the flag but within the same brackets) to control this more directly. 

Now, the first two options on a beep are frequency, and length. These two options are, really, the minimum you need to start producing musical sequences. The frequency is in Herz (as I've covered before, this means it's roughly between 20 and 20,000 and increases the pitch by an octave every time it is doubled), so we can try a simple sequence by typing these commands one after another...



Now, you'll notice a couple of things here. Firstly, those three tones sound quite nice in sequence. More importantly, however, you'll notice that you can't type quickly enough to hear them as any kind of tune. Clearly this is not something you can easily control directly from the command line. 

But, again as I've mentioned before, the beauty of programming computers is that when you don't want to (or aren't able to) keep controlling a computer directly, you can give it some rules and let the computer follow these. So, the first thing we need to do is use a programming language capable of running terminal commands for us. I'm using my main programming language, Ruby. 

We'll use irb (or instant ruby) for now, which just runs ruby commands line-by-line, instead of reading through a file.


You'll notice the start of the line has changed, but you're still being prompted to type. 

We're going to use a standard Ruby method, system, with a single argument which is a string of text. So we'll make our argument, that last beep command, for now.


This returned true, which basically says, yes, this worked. You'll also hear the beep. You may also notice that you typed a longer, slightly more complicated line for exactly the same result. This part is just a primer, however, to show you ruby running the terminal command. Next we're going to use a single variable, which we'll substitute for the frequency number. 

First let's take a step back, a variable is a single piece of named information within Ruby (and most other programming languages). You can think of it a little bit like doing algebra at school, if we set a variable called f (say, f = 440) we can then do some actions on f, which would be the same as doing those on the original number. For instance...


You can see that we've done some maths with f, the results are worked out for us, and if we define f again as something different, the answers to the other lines would change. Although in this case, f doesn't change unless we use f = something again. 

We can define f as the result of some of this maths, like so...


You'll see that this time f has changed with each line. So the result of the each bit of maths is based on the one before. 

For these maths functions, we can usually shortcut this by putting the = and the operator together (like *= to multiply and set the variable...


 Notice how the results are the same as the method I used before. 

So what has this got to do with making music with beep? Well, once we have our variable we can do a string substitution to put it into the beep command, then send it to the terminal with system. 

So if we change f, and then run the beep command again, the pitch will change. 

String substitutions in Ruby can be done on double quotes ("") and are a hash followed by curly brackets. That might not make sense but they look like this: "Solid string: #{variable}" For instance: 


What this substitution (#{}) does is evaluate the ruby code within, and then put it in that place in the string, so we could put one of our *= commands in there, so f would be multiplied by the same figure whenever we run that line...


To help you do this, in irb (and in the terminal) you can re-run lines by pressing up until you see the previous line you want to run, then pressing enter. 

This may look the same, but you'll hear that the pitch has increased by an octave each time and see at the end that our variable f has been doubled four times. 

So, we've established that we can make the frequency change by running the same line of code a few times over, hearing the beep each time. The next step is to run that line a few times over without having to run each one manually. For this we're going to use a ruby times loop, this does the thing which is in curly brackets ({}) after it a given number of times. like:

4.times {system("beep")}

Lets try that last sequence, but in just 2 lines of Ruby:


This will sound the same, but a lot more fluent than the previous example, the four beeps sounding one after another. 

When using a times loop, we can also set a variable which comes from times to use inside the loop, which is defined within pipe characters (|) at the start of the loop. For instance:


This uses the Ruby puts method (which simply puts it's argument down in the terminal) to show you what i is each time this loop is run, you can clearly see it's the numbers from 0 to 7. It's basically the number of times that loop has been run so far. 

So if we use this alongside our f variable, we listen to the pitch changing based on algorithm which changes in the same way each time, based on how many times the loop has been run. 

Lets apply some pure data knowledge here, way back in June last year in Algorithms of the Street: Part 3 - Additive Synthesis I told you that when you multiply a frequency by whole numbers, you get what is known as the harmonic series, which is a set of frequencies which go together, often they are present together when you hear musical notes, and strings tuned to the first of these will vibrate in sympathy when a tone is played at one of the other frequencies in the list. 

So frequencies which are multiples of the same note go together. 

So lets try that times loop, but multiplying f by i each time. I'm going to start a little lower down and run this line 8 times (although because i starts at 0, I'm putting a little bit of ruby in brackets to use i + 1, so we actually get the numbers 1 to 8):


You'll now hear beep running through the harmonic series, from that initial frequency of 220 hertz.

Let's try a different approach, let's use a method called rand, which generates a random number. When we give it an argument, it will generate a random number between 0 and 1 less than that number (the same as pure data's random object). 

 
This time you'll hear a sequence of 8 beeps at random pitches, and if you run it again you'll hear different random sequence. 

Incidentally, you could substitute 8.times with loop, which is an infinite loop in Ruby (followed by the curly brackets in the same way). However, you will have to press ctrl + c to stop this running. 

Lets try combining the last two approaches, a loop which randomly chooses a value for f, then runs up the first 8 partials in it's harmonic series.

Sounds complicated? Well it is, and if you do any coding, you'll wind up doing some complicated things, that's just the way it is. So let's start by making it simpler. Firstly, this shouldn't be done on just one line. And because we're relying on a few lines, it's probably best to use a ruby file (a text file who's name ends in .rb). 

Also, because we are working across multiple lines we'll replace those surrounding curly brackets with the words do to start and end to end. The convention to make these more readable is to indent the lines between these two words by two spaces, so you can scan down the left hand side of the file and see that those which are indented are inside that block. 

So, in your favourite text editor (but not word processor, just a simple text editor. I'm using vim) put this in a file and save it as beeps.rb in your home directory. 

(notice that I've replaced system with back-ticks(`), which do the same thing, only they wait for the command to finish, if you use system here you might get a shock when you try to stop the ruby file from running)



I've added a -l flag of 100 milliseconds to make this slightly faster. So you see the { and } have been replaced with do and end and the lines between are indented, so you can scan down from the do line to the end line without any interruption. You don't have to understand this at first, but please do it. This makes code readable! 

You can now run this in the terminal, in your home folder, with this command:

ruby beeps.rb

press ctrl and c to stop this script

You'll hear the same kind of random melody. 

Now we're going to put a times block inside our loop block. Again, replacing curly brackets with do and end, and the line within will be indented a further 2 spaces. 

I'm also going to add a puts line so you can see the frequency as the script runs.


Now when you run ruby beeps.rb, you'll hear a random note, beeps running up it's harmonic series, then a new random note and it's harmonic series, and so on. 

And that is some algorithmic music made in Ruby and Beep, no pure data at all this time. 

I realise it will be very heavy for people who haven't written any code before, but it's quite rewarding. Remember that code is, above all else, very logical, you can often work out problems as you are writing it with a few rules:

  • Finish everything you start
  • Read error messages carefully, they may be talking about something you're not currently thinking about.
  • If stuck, ask google
  • If stuck, ask someone who knows about it. 
That last rule is important, the internet is full of well-meaning techies who will often help beginners with a little leg-up in the world of code. I'm amongst them (feel free to grab me on twitter, @MarmiteJunction). 

I hope you found this little tutorial interesting, informative, or even fun. By all means tell me what parts of it make no sense to you. 

God bless and have fun coding

- Andrew F

No comments:

Post a Comment