Philip Potter

Pitch and Frequency

Posted on 26 May 2012

I’ve just come back from EuroClojure 2012, where there were a number of Overtone talks and a number of tweets asking for music theory resources aimed at computer scientists. This will hopefully blog number 1 in a series on that theme.

Note that the code examples are designed to be pasted into a Clojure repl, so that you can take the code and play with it yourself.


The basic atoms of synthesizing sounds in Overtone are oscillators. An oscillator takes a frequency and makes a noise. Here are some examples:

(use 'overtone.live)
(demo (sin-osc 440))  ; sine wave
(demo (saw 440))      ; saw-tooth wave
(demo (square 440))   ; square wave

In each of these, we’re making an oscillator and giving it a frequency of 440 Hz — or equivalently, 440 cycles per second (cps). The problem we find is that most music isn’t defined in terms of frequency, it’s defined in terms of notes of the scale: C D E F G A B C and all the sharps and flats in between. How do we play a tune on a sine wave generator when the tune is made of notes rather than frequencies?

First, let’s write some helpers to play a sequence of frequencies through a sine wave:

(definst sine-wave [freq 440]
   (sin-osc freq))
;=> #<instrument: sine-wave>
(defn play-freqs
      ([freqs] (play-freqs freqs (now) (sine-wave (first freqs))))
      ([freqs time inst]
        (if-let [freqs (seq freqs)]
          (do (at time (ctl (:id inst) :freq (first freqs)))
              (apply-at (+ time 300) #'play-freqs [(rest freqs) (+ time 300) inst]))
          (at time (kill (:id inst))))))
;=> #'user/play-freqs
(play-freqs [440 660 330 440 220 880 220])
; *beautiful melody*
;=> #<ScheduledJob id: 76, created-at: Sat 09:32:26s, initial-delay: 0, desc: "Overtone delayed fn", scheduled? false>

We define a simple intrument, sine-wave, which has one parameter, freq. We can start the instrument with a particular frequency by writing (sine-wave 440); this returns a map of data about the particular oscillator instance which is generating the note. We can change the freq parameter of a running instrument by using (ctl (:id inst) :freq 660). Finally, we can stop a running inst with (kill (:id inst)). (And if it all goes horribly wrong, we can stop absolutely everything with (stop)).

Our goal is to be able to use play-freqs to play tunes made of keywords rather than frequencies: [:c4 :d4 :e4 :d4 :c4].

Frequency of notes from first principles

You can calculate frequency from pitch using only four fundamental axioms:

  1. When an orchestra tunes up at the start of a rehearsal, they tune to A. A is normally defined to be 440 Hz.
  2. Going up one octave is the same as doubling the frequency. That is, one octave above tuning A is 880 Hz, and one octave below is 220 Hz.
  3. There are twelve semitones in an octave.
  4. All semitones are equally sized.

If going up an octave doubles the frequency, then going up twelve semitones must also double the frequency. This means we must find the number semitone, where:

(nth (iterate #(* semitone %) 440) 12)
;=> 880

In other words, we want semitone^12 == 2, so semitone must be the twelfth root of 2:

; from the contrib library [org.clojure/math.numeric-tower "0.0.1"]
(require '([clojure.math.numeric-tower :as 'math]))
;=> nil
(def semitone (math/expt 2 1/12))
;=> #'user/semitone
(nth (iterate #(* semitone %) 440) 12)
;=> 880.0000000000003
; or alternatively:
(* (math/expt semitone 12) 440)
;=> 880.0000000000003

We can now define a function to find a frequency a given number of semitones from tuning A:

(defn semitones-from-a [semis]
  (* (math/expt semitone semis) 440))
;=> #'user/semitones-from-a
(semitones-from-a 0)
;=> 440.0
(semitones-from-a 12)
;=> 880.0000000000003
(semitones-from-a 3)
;=> 523.2511306011974
(semitones-from-a -9)
;=> 261.6255653005985

Normally, however, we use MIDI notes as a numerical representation of notes, rather than displacement from tuning A. In the MIDI note system, tuning A is defined to be 69, and going up or down one semitone increases or decreases the note value by one. So, for example, middle C is 60. We can get from midi notes to frequencies like this:

(defn midi-to-hz [midi-note]
  (semitones-from-a (- midi-note 69)))
;=> #'user/midi-to-hz
(midi-to-hz 69)
;=> 440.0
(midi-to-hz 81)
;=> 880.0000000000003
(midi-to-hz 72)
;=> 523.2511306011974
(midi-to-hz 60)
;=> 261.6255653005985

In fact, Overtone provides a function midi->hz to do exactly this transformation:

(midi->hz 69)
;=> 440.0
(midi->hz 81)
;=> 880.0
(midi->hz 72)
;=> 523.2511306011972
(midi->hz 60)
;=> 261.6255653005986

So with a melody as MIDI notes, we can play it as follows:

(play-freqs (map midi->hz [60 62 64 62 60 72 67 60]))

If you’ve got this far, the maths is over. The last mile is to be able to use keywords instead of raw MIDI values. Overtone provides a function for this called note:

(note :a4)
;=> 69
(note :a5)
;=> 81
(note :c5)
;=> 72
(note :c4)
;=> 60

So we can play our tune by chaining this with midi->hz:

(play-freqs (map (comp midi->hz note)
    [:c4 :c5 :e4 :f4 :g4 :f4 :e4 :d4 :c4 :c4]))
; *beautiful music*