If you followed through my previous articles you already know how to setup your system and editor to play notes or chords with clojure and overtone, and how it actually requires less code repetition. I described in great detail how I made the
play function to do that (but mainly thanks to clojure being an awesome and powerful language). In this article I will continue further along the Sonic-Pi Tutorial and I will share some of my thought about the problems I encountered along the way and why it isn’t that easy to correct them.
Exploring how to create melodies
Continuing along the tutorial, in section 2.1, next on the list is Melody. In Sonic Pi, the code looks like this:
1 2 3 4 5 play 72 sleep 1 play 75 sleep 1 play 79
So, after every line you specify how many beats will pass before the next line is executed. Quick digression: beat is a way to measure time in music. In the case of this example from sonic pi 1 beat is the same as 1 second, because the default setting in sonic pi is 60 bpm (beats per minute).
In overtone-playground, you can do the same with this line of code:
1 (melody [72 75 79] 1000)
As you can see, it is much shorter, although it may look a little more complex. Let me briefly go through this function call. First, we’re calling the
melody function, which will just execute
play multiple times. Then, I supply the notes in seqable form, and lastly, I specify how long (in milliseconds) will the function wait until it executes
play on the next argument. It would be better if I could supply the number of beats instead of milliseconds to the function, but I have yet to figure out how to implement this without adding complexity or confusion. So, for now, it’ll have to do.
The interesting thing is that
melody function can play not just notes, but chords, allowing you to create interesting progressions. For example, try out the code below:
1 (melody [(chord :c#4 :minor) (chord :a3 :major) (chord :b3 :major)] 2000)
The principle is the same, it will play the 4th octave of C# Minor, and 3rd octaves of A Major and B Major chords with 2 seconds between each chord (1 second = 1000 milliseconds, but I have a feeling you already know that). Actually, I chose those chords because they are a part of one of the most known chord progressions of all time, as can be seen in this awesome video. Let’s modify our function call to play those chords in succession:
1 (melody [(chord :E3 :major) (chord :b3 :major) (chord :c#4 :minor) (chord :a3 :major)] 2000)
And there it is. Awesome!
Traditional note names
This functionality I already implemented (play function is the perfect example), except for one minor detail, which we will take a look at now:
1 2 3 4 5 play :C sleep 0.5 play :D sleep 0.5 play :E
The code above is from sonic-pi tutorial, and equivalent in overtone-playground would look like this:
1 (melody [:c4 :d4 :e4] 500)
The only difference is that in sonic-pi, if you omit the octave number from note name, it will default to 4th octave wherein overtone-playground you always have to specify the octave (the note names are case insensitive). Also, the syntax for sharp notes is different: In sonic-pi, E sharp 4th octave would be written as :Es4, while in overtone-playground we would use :E#4 (which is how it is written in traditional music notation anyway). The syntax is the same for flat notes, for E flat 4th octave note you would enter :Eb4 (which is also standard music notation).
The next part of the sonic-pi tutorial explains how to use Synth Options to control various parameters of the synths, allowing us to better express our ideas. Tutorial then introduces amplitude and pan control. While in sonic-pi all available synths accept same set of fundamental options (some of them have specific options), in overtone various synths have different options, and while most of them accept :amp (amplitude), not that many have a :pan option, not to mention the rest. It goes even further: some expect user to input a frequency to reproduce sound, and others will produce sound based on a midi note. Some synths (strangely) do not produce sound even if given the correct type of input for a sound (freq or (midi) note), for example fm-demo, ks-stringer and ks1-demo. Trying to use *buzz synth kills my JVM, which is very strange behavior. If you’re reading this and have some idea how this can be fixed, do feel free to contact me, help is always appreciated. I am certainly looking into it myself. The next obstacle is that i locked my
play function to only use overpad synth, and changing that would just add another layer of unnecessary complexity to the project.
Since Overtone currently has only 22 instruments in the synth namespace, I think it will be best to standardize all of them to accept the same set of arguments, and to be used in a similar matter to
play function. This will be covered in my next set of articles, and after that we can continue following the path of Sonic-Pi tutorial.