Wave-machine

Creating music the hard way

Harmonics

In the previous article, I made mention of harmonics. Harmonics are components of a wave form that have a frequency that is a multiple of the fundamental frequency. For most people, harmonics are not perceived as separate tones, but rather part of the main tone. This is because the harmonic also repeats at the fundamental frequency. Many real-world instruments produce harmonics, so they are an important concept in music.

From now on, I'm going to call wave functions used for musical tones voices. In Main.hs, I'll make the voice a function so we can easily swap it with other WaveFunction values:

voice :: WaveFunction
voice = sineWave

-- ...

playNote :: (WaveFunction,Double) -> (Int,Tone,Int) -> (WaveFunction,Double)
playNote (currentSeq,pos) (dur,tone,oct) = 
    (addWaves currentSeq thisNote, pos + duration)
    where 
        duration = fromIntegral dur * noteLength
        thisNote = delay pos $ clip duration $ applyTone tone oct voice

I'll move sineWave to Voices.hs, where I can add additional voices later on:

module WaveMachine.Audio.Voices where

import WaveMachine.Audio.Waves

sineWave :: WaveFunction
sineWave t = sin (t * 2 * pi)

Let's create a function that will add a harmonic sine wave to another:

harmonic :: Int -> WaveFunction
harmonic n t = sineWave (t * multiplier) / multiplier where multiplier = fromIntegral n

addHarmonic :: Int -> WaveFunction
addHarmonic n = addHarmonics [n]

addHarmonics :: [Int] -> WaveFunction
addHarmonics = foldr (addWaves . harmonic) sineWave

The harmonic function takes an integer and returns a sineWave that is the frequency times that integer. I've also reduced the volume of this harmonic so it doesn't overwhelm the main component of the wave. The addHarmonic function simply calls addHarmonics with just one harmonic value, and addHarmonics actually combines the waves.

The addHarmonics function has a few new things I should point out to fellow Haskell beginners. This foldr function isn't so different than the foldl function we used earlier; it just means "fold right" instead of "fold left," so it starts combining values starting with the right side of a list. This addWaves . harmonic is a bit more advanced. The . operator is used for function composition, and is meant to resemble the βΈ° mathematical operator. (f . g) x is the same as f (g x). addWaves . harmonic produces a function that will take an integer harmonic level and create a function that takes a WaveFunction and combines it with the harmonic.

So let's hear what it sounds like when we do this:

voice = addHarmonic 2

The harmonic in this case is an octave higher, since doubling a frequency produces an octave interval. So what does a harmonic of 3 do?

voice = addHarmonic 3

Recall that each octave is 12 tones. Multiplying the frequency by 2 causes us to go up all 12 tones, since 2 = 2 ^ (12/12); multiplying by three does not lead to an exact tone in the 12-tone scale, but the closest value is an octave + "perfect fifth" (19 tones). A harmonic of four is two octaves (24 tones):

voice = addHarmonic 4

Five is also not an even tone, but most closely matches two octaves + a major third (28), and six most closely resembles two octaves plus a fifth (31):

voice = addHarmonic 5

voice = addHarmonic 6

Here is how all 6 harmonics together in one wave sounds:

voice = addHarmonics [2..6]

And just for fun, here are some waves with just odds and just octave harmonics.

voice = addHarmonics [2, 4, 8, 16]

voice = addHarmonics [3, 5, 7, 9]

Sine waves + harmonics are not the only choices we have for voices. One common synthesized sound is a square wave, where the oscillating pattern just jumps between two values:

periodic :: Double -> Double
periodic t = t - fromInteger (floor t)

squareWave :: WaveFunction
squareWave t
    | t < 0.5   =  1
    | t < 1.0   = -1 
    | otherwise = squareWave $ periodic t

voice = squareWave

Sawtooth waves on the other hand are a triangular, jagged wave shape:

sawtoothWave :: WaveFunction
sawtoothWave t = (periodic t * 2) - 1

voice = sawtoothWave

It's fun to play with all these synthetic voices, but in the next article I'm going to experiment with trying to make a voice that more closely resembles a real musical instrument.

comments powered by Disqus