Overtone is an audio programming library useing the Supercollider syntheizer engine via the expressive programming language
Clojure. It combines the power of Supercollider and Clojure enabeling many artistic endeavors, includeing:
Midi/OSC hardware interfaces
Data science and "big data" art
AbletonLive clock synchronization via AbletonLink
It is my hope that this book will be a gentle introduction to
Overtone for a complete beginner as well for a novice. I hope to approach the topic of Overtone with grain of cynicism; "what can go wrong will go wrong". Overtone is still highly stable library and for a workshop of 6-8 participants, it takes usually half an hour or less to get everyone started. That said, you’ll most likely encounter redundant details and/or debugging instructions for highly improbably scenereos while reading this book. Keep in mind that what may seem obvious to you isn’t necessarily obvious to everyone.
The computer can be amazing, exciting tool to fulfill our creative needs. But at the same time the computer, and its building blocks, computer programs and code, can be at times undescribeably painful, misconfigured and unpredictive. The hurdles you may encounter may include tooling, mac/linux/windows specific problems, files and libraries mislocated, version incompatabilities, and the list can go on forever. Luckily we will be getting ourselves comfortable with the programming language Clojure, which in the total 10 years of its existance, has seen an explosion of book publications, tutorials, workshops and stackoverflow questions/answers. So if you find youself in the unfortunate situation of getting stuck with something unrelated to Overtone, your odds are good that someone else on the internet already had the same problem as you, and you’re only one google search away from getting yourself back on track. At any point in time for any problem you are faceing, you should always feel welcome to open ticket on github, ask question on stackoverflow, the irc(freenode) or on slack(clojurians). The software community as a whole is riddled with micro-aggressive "bromance" behaviour and hence can be at times quite intimidateing for beginners to reach out for help. Which among other factors(in my subjective opinion), we end up with demographics of coders, be it art related or not, completly out of porpotion in relation to the demographics of the general population. Please be aware that the Clojure community has on all social-media platforms, meetups and conferences a set of guidelines for behaviour, protocols in case of violation and group of confidentials to report violations to. I believe that these measures has made the Clojure community relatively more inclusive and diverse, every day I see experienced developers go out of their way to help someone in need of help and I hope you will enjoy the benefits of the Clojure community in the same way as I did when I was a total beginner.
As my personal advice as an artist to artists stepping into the journey of creative codeing; don’t believe everything software engineers tell you about software, in the same way you shouldn’t believe everything an artist tells you about how art should be. An artist and software-enginner will and should approach code in different ways. You will never hear stories about Beethoven implementing a Continous Integration test to determine if his 7th symphony accedentally lost a note while he was writing his 8th, or Stravinsky trip out on his Bassoon type checker when he wrote the introduction to the Rite of Spring. Learning from the best is always a good approach and becoming better at programming is only going to make you faster and more effective, but do watch out for the abyss of infinite information and take moderate actions. If our goal is to create art for the real world, be completly shameless if your code isn’t orthodoxial as long as it produces the desired result and you had fun writing the code. While the "old-school" composer is sitting next to a piano with pen and empty staff notation paper, in the same way you will find ourself with an empty text document in your text editor, with your fingers on the keyboard, ready to explore the physical boundries of music, sound and time.
This is a work in progress. If you find an error, please submit a PR to fix it, or an issue with details of the problem.
The examples and code used in this book will try to be as neutral as possible when it comes to choice of text-editor. All modern text editors have some sort of plugin to run and evaluate Clojure code. If you are starting out with the programming language Clojure, you may easily get confused or lost on how to replicate some of the examples in your text editor. Therefore we aim to make all of our examples repliceable useing nothing but Leiningen in the terminal via
lein repl, and assume from the reader basic understanding of the terminal/cmd (if you know how to explore directories and install apps with the terminal, you probably know enough). Leiningen is the most popular build tool in Clojure and is the build tool that glues Overtone’s sources and dependencies togeather. Overtone does work with alternative build tools like
boot, as well as a standalone java jar file, but that and how to integrate Overtone to specific text-editor will not be included in this book.
Please be also aware, that topics often overlap, if something isn’t clear, have a look at the chapter titles to see if the concepts you’re trying to understand is explained elswhere in the book.
You will need:
Optionally you could want:
If running on Linux you must have JACK Audio connection toolkit version 2 or later (which
qjackctl provides along with easy to use GUI)
$ sudo apt-get install qjackctl
$ sudo dnf install qjackctl
$ sudo yum install qjackctl
$ nix-env -i libjack2
Although it is possible to start Overtone directly from the github repository by downloading it as zip or cloneing it with
git clone https://github.com/overtone/overtone.git in the terminal. It is recommended that
Overtone is used as any other Clojure library in your own project. So we will do exacly that.
First create an empty directory, we’ll call it overtone (the name is irrelevant here) and go into it
$ mkdir overtone $ cd overtone
Then in your text-editor, create a new textfile and save it into the newly created directory as
project.clj, this is file that leiningen looks for inside the folder leiningen was started from (ie. you can’t start leiningen in directory x and expect it to find
project.clj in directory y). Then paste the following code into
project.clj and save the file again.
(defproject overtone-tutorial "1.0.0" :dependencies [[overtone/overtone "0.10.3"]] :native-path "native" :source-paths ["src"])
With only one file in your directory run the following lein command and let’s see what happens
$ lein deps
If you’re running leiningen for the first time you should see whole bunch of text appearing to the screen, indicateing that leiningen is downloading the Clojure dependencies needed to run Overtone. After this process, directories
native should have been created, both of these directories can be safely omitted if you’re planing on save your code on for example github, and
target can even be deleted at any time, by literally deleteing it or by running
lein clean which by default deletes
target, that will only be useful if you’re trying to debug your program as
target gets created every time you run leiningen to store various information irrelevant to us at this moment. But do keep
native untouched as it stores the necessary files needed to run Supercollider from within Overtone.
Now let’s start Clojure via
$ lein repl
|REPL stands for READ-EVAL-PRINT-LOOP, and is a fancy word for the Clojure shell/interpreter. In simple terms, a [Clojure]REPL is any type of program or tool that you can give Clojure code to to be evaluated.|
If all went right, you should see something similar in your terminal window
nREPL server started on port 34189 on host 127.0.0.1 - nrepl://127.0.0.1:34189 REPL-y 0.3.7, nREPL 0.2.12 Clojure 1.9.0 OpenJDK 64-Bit Server VM 1.8.0_172-02 Docs: (doc function-name-here) (find-doc "part-of-name-here") Source: (source function-name-here) Javadoc: (javadoc java-object-or-class-here) Exit: Control+D or (exit) or (quit) Results: Stored in vars *1, *2, *3, an exception in *e user=>
Now for our sanity, let’s see if this REPL prints
user=> (println "Hello World!") Hello World! nil user=>
Yup we are ready, then to the moment of truth, now let’s boot up Overtone with this easy-to-remember command
If all went accordingly without errors you should see something similar in your terminal window, note that I’m running here on Linux, so for me Jack will be automatically booted and connected.
user=> (use 'overtone.live) --> Loading Overtone... --> Booting internal SuperCollider server... Found 0 LADSPA plugins Cannot connect to server socket err = No such file or directory Cannot connect to server request channel Cannot lock down 82280346 byte memory area (Cannot allocate memory) Cannot use real-time scheduling (RR/5)(1: Operation not permitted) JackClient::AcquireSelfRealTime error JackDriver: client name is 'SuperCollider' SC_AudioDriver: sample rate = 48000.000000, driver's block size = 2048 --> Connecting to internal SuperCollider server... --> Connection established JackDriver: max output latency 128.0 ms _____ __ / __ /_ _____ _____/ /_____ ____ ___ / / / / | / / _ \/ ___/ __/ __ \/ __ \/ _ \ / /_/ /| |/ / __/ / / /_/ /_/ / / / / __/ \____/ |___/\___/_/ \__/\____/_/ /_/\___/ Collaborative Programmable Music. v0.11 Hello Hlolli, may algorithmic beauty pour forth from your fingertips today. nil user=>
If something went wrong, see if the stacktrace gave you any meaningful information and proceed to Configuration and try to rule out that something isn’t misconfigured. And come back here and try to start Overtone again before continueing. A good rule of thumb is to read stacktraces from top to bottom, the uppermost lines being the most important ones in most of the cases.
If you’re on Linux too and encounter
Now that we have Overtone running successfully. We can start all the functions that Overtone provides at our disposal. Among them is an important helper function called
doc, which will print the documentation to any function in your scope/reach. Let’s try it on the symbols
user=> (doc demo) ------------------------- overtone.live/demo ([& body]) Macro Listen to an anonymous synth definition for a fixed period of time. Useful for experimentation. If the root node is not an out ugen, then it will add one automatically. You can specify a timeout in seconds as the first argument otherwise it defaults to *demo-time* ms. See #'run for a version of demo that does not add an out ugen. (demo (sin-osc 440)) ;=> plays a sine wave for *demo-time* ms (demo 0.5 (sin-osc 440)) ;=> plays a sine wave for half a second nil user=>
user=> (doc sin-osc) ------------------------- overtone.live/sin-osc ([freq phase mul add]) Sine table lookup oscillator [freq 440.0, phase 0.0, mul 1, add 0] freq - Frequency in Hertz phase - Phase offset or modulator in radians mul - Output will be multiplied by this value. add - This value will be added to the output. Outputs a sine wave with values oscillating between -1 and 1 similar to osc except that the table has already been fixed as a sine table of 8192 entries. Sine waves are often used for creating sub-basses or are mixed with other waveforms to add extra body or bottom end to a sound. They contain no harmonics and consist entirely of the fundamental frequency. This means that they're not suitable for subtractive synthesis i.e. passing through filters such as a hpf or lpf. However, they are useful for additive synthesis i.e. adding multiple sine waves together at different frequencies, amplitudes and phase to create new timbres. Categories: Generators -> Deterministic Rates: [ :ar, :kr ] Default rate: :ar nil user=>
Much of what is written in the documentation will be explained in subsequent chapters. But let’s suffice to say that
demo is a function intended to evaluate an instrument and play it immediately for 2 seconds, which can come in handy when developing sounds and you want to hear the results quickly. And
sin-osc is an oscillator that produces sinewave shape audio-signal (or control-signal, more on that later). Unlike
demo which needs at least 1 instrument to be given as a parameter, then
sin-osc can be called without any parameter, if that’s the case, then
sin-osc will look at its own default parameter and use those instead. Which would be 440 cycles per second on full amplitude.
We will conculde this chapter by playing 2 seconds of 440Hz sinewave. It wont sound pretty but it’s fast and effective way to determine if everything is working accordingly.
Application of envelope via graphical breakpoints
The term envelope may or may not sound alien to you. But if you’ve ever used attack or decay on modular synthesizer or written breakpoints in a DAW like Ardour, ProTools or Reaper, you have used envelopes. Fade-in and fade-out is another commonly used form of envelope. The basic idea is that as time passes, often shown as x-axis in a plot, some value changes accordingly, often drawn on y-axis. In fact envelope can be an oscillator and an oscillator can be used an envelope.
Envelopes are most traditionally used to control amplitude and frequency, as will be shown later, envelopes in Overtone can be used for many other things, like amplitude modulation, oscillation, trigger, arpeggiator, trill/tremolo ornaments. Basically anyhing that ties numbers and time togeather.
Overtone has a function to create envelope shapes in a format that Supercollider understands, called simply
envelope. For every envelope there needs to be two vectors provided as arguments, levels (y-axis) and durations in seconds (x-axis), since durations specifies the durations of each step, its size will need to be the size of the levels vector minus 1. You can choose an envelope shape by directly specifying each point via
:step, or use one of the following keywords to appply a function to your points.
:lina linear function that creates straight lines from given points
:expa natural exponential function (eⁿ), no value on y-axis can be zero
:sinapplies Hann function to given points
:wela power spectrum generator via Welch method
:sqrapplies square (n²) function to given points
:cubapplies cubed (n³) function to given points
Let’s try a simple example.
(envelope [0 0.5 1] [1 1] :step)
This shape will create a straigt line from 0 to 0.5 and from 0.5 to 1, with each of the two steps takeing 1 second (totalling 2 seconds, which is the default time
demo plays an instrument).
To most clearly hear what’s going on here, let’s try to play this line as an upwards glissando(gliding note) from 200Hz to 400Hz and see if this works.
(let [env (envelope [0 0.5 1] [1 1] :step)] (demo (sin-osc :freq (+ 200 (* 200 (env-gen env :action FREE))))))
This clearly didn’t sound like glissando, so what’s going on here? We did indeed specify a line from 0 to 1 and multiplied the line by 200 to glide 200Hz ending at 400Hz. By useing
:step the envelope makes no attempt bridge the gap between the points we provited it. So as soon as the instrument started we heard 300Hz and halfway trough it changed to 400Hz. So what happened to our first point of 200Hz? In those cases where the envelope loops, there’s a chance of distorted hum if the envelope doesn’t begin where it ends, so point 0 on x-axis works as a pivot point and will never be played.
Let’s try again, but this time let’s try to bridge the gap by applying a linear function to the points useing
(let [env (envelope [0 0.5 1] [1 1] :lin)] (demo (sin-osc :freq (+ 200 (* 200 (env-gen env :action FREE))))))
This sounded much smoother, no jumps but constantly gliding note for the duration of the two seconds. But to human ears, this linear glide doesn’t sound very linear even tough the computer did perfectly good job of executeing it linearly. To produce a glissando that a human would sing or perform on a violin, we need to apply a non-linear function to the provided points. Keep in mind our auditory senses are non-linear both for the perception of amplitude and frequency. Where we usually control and measure amplitude on logarithmic scale in decibels, and frequency with note names, midi note number or pitch class sets, all of which are in in squared relationship to the Hz they represent. So for our last attempt glissando, we’ll apply a squared function to points from 0 to 1 in 2 second interval.
(let [env (envelope [0 1]  :sqr)] (demo (sin-osc :freq (+ 200 (* 200 (env-gen env :action FREE))))))
The envelope function can also take a number and/or vector of numbers instead of keywords for the shape. In that case, 0 marks a linear curve and the higher the number, the curvier. Negative values means curve in the opposite direction. These can produce interesting curves which can be hard to visualize, you may want to try the examples from the Supercollider documentation in your Supercollider and use the nice plotter as aid.
Like mentioned, you can use the function
envelope to define any arbitrary shape you want via
:step and apply various function to a vector of provided points. But most of the time you’ll want to use built-in functions that provide the most commonly used envelope shapes for sound-designing.
adsrcreate an attack decay sustain release envelope
lincreates a linear trapezoidal shaped envelope
perccreates non-linear exponentially shaped envelope
trianglecreates an envelope which has triangle shape
sinecreates a hanning window shaped envelope
asrcreates attack sustain release envelope
Additionally you’ll find in Overtone:
All shapes returned by
envelope, or the built-in envelope functions which themselves all are built on top of
envelope, must be used in combination with
env-gen is what turns the envelope data into a signal which can be passed around as an operator to other signals, as well as direct argument to various inputs. The first parameter of
env-gen is the formentioned
envelope shape itself. The other parameters are as follows
A lesser used minimal alternative ugen to
linen is a linear attack-sustain-release envelope generator that takes only
action as extra arguments and runs only on control-rate (see Data Types to understand the various rates) and returns a signal that can be used in the same manner as
The following two expressions will be played exacly the same.
(demo (* (env-gen (lin 0.1 1 1 0.25) :action FREE) (sin-osc))) (demo (* 0.25 (linen (impulse 0) 0.1 1 1.9 :action FREE) (sin-osc)))
Since an envelope can be any modulating signal, we can create an envelope shape useing low-frequency-oscillators or LFO for short. Low frequency oscillator is typically an oscillator that oscillates at a frequency under the lower limit of human hearing range ~22Hz. In overtone you can find LFO oscillators starting with LF, which include
lf-tri and countless more. They differ only from traditional oscillator in that they are not band-limited and are more performant, since they don’t try to anti-aliase high frequencies. When working with LFO’s in most cases, it doesn’t matter if you use band-limited or non-band-limited oscillator, they behave exacly the same on low frequencies.
The following expression plays a sawtooth oscillator for 1 second with even attack and decay time created from the shape of sinewave. Note that we are converting Hertz(1/sec) to seconds, as reference 1Hz takes 1 second to finish 1 cycle. As we are useing sinewave shape from
sin-osc:kr we are only concerned with the positive values from 0 to 1, which occupy the first half of the sinewave. Therefore we multiply the wanted note duration by 2, so that the signal cuts off exacly before the sinewave goes from 0 to a negative value. The
line:kr serves as timeout that frees the synth node after 1 second and hence prevents it to play the full 4 seconds which the demo time defaults to.
(demo (let [dur 1 env (sin-osc:kr (/ 1 (* 2 dur)))] (line:kr 0 1 dur :action FREE) (* env (saw 220))))
Now if we wanted a sinewave envelope with two peaks, we need to apply abs (asbolute value) on the LFO signal as not to end with negative amplitute values. Here we don’t need to cut the sinewave signal in half since applying
abs causes the sinewave to become unipolar (from 0 to 1) as opposed to bipolar (from -1 to 1).
(demo (let [dur 1 env (abs (sin-osc:kr (/ 1 dur)))] (line:kr 0 1 dur :action FREE) (* env (saw 220))))
We can also shift the sin-osc to become unipolar by useing the parameter
:add. By adding 1 to a bipolar signal, will cause a shift from ←1,1> to <0,2>, and since we want to limit our range from within the sensible amplitude boundries of <0,1> we need to multiply the signal by 1/2 (or divide it by 2). That can also be achieved by setting the parameter
:mul to 0.5 as shown below.
(demo (let [dur 1 env (abs (sin-osc:kr :freq (/ 1 dur) :mul 0.5 :add 1))] (line:kr 0 1 dur :action FREE) (* env (saw 220))))
With sawtooth oscillator we can emulate a linear fadein. Here we also need to convert the bipolar sawtooth-wave signal into unipolar signal.
(demo (let [dur 1 env (abs (lf-saw :freq (/ 1 dur) :mul 0.5 :add 1))] (line:kr 0 1 dur :action FREE) (* env (saw 220))))
By providing negative
:iphase value we can reverse the signal and get a linear fadeout.
(demo (let [dur 1 env (lf-saw :freq (/ 1 dur) :iphase -2 :mul 0.5 :add 1)] (line:kr 0 1 dur :action FREE) (* 0.1 env (saw 220))))