You don't need to know anything about music theory to make music today, and I don't mean that as a critique of modern music. I'm talking about tools. With patience and a piano roll interface, anybody can click and drag their way to something that sounds disturbingly like music. By the time you want to make music, you've listened to so much that you can compose using only guess and check—you can tell which notes sound good together by listening to them.
But if you want to create music by algorithm, you have to somehow imbue your computer with the same gift. To help you do that, I'm going to write everything I know about understanding music mathematically, in ways that are easy to express with code. In fact, I will express many of them with code.
ChucK
The code on this page is written in ChucK. ChucK is a widely-available programming environment for sound. Get the miniAudicle graphical environment if you can, though command-line is fun too, if you're into monospace fonts. The Audicle is just insane.
It's not required, but it's "required," okay?
On to the learning!
Frequency
First you have to understand frequency.
Think of a guitar string. When you pluck it, it oscillates. The movement of the string vibrates the air around it, and when the ripples in the air reach your ear, you hear them. The higher the note, the faster the string was vibrating, and the closer together the air ripples were when they smacked into your ear.
Imagine how the guitar string swings back and forth, left and right. Every time it gets all the way to the left, turns around, and starts going right—that's a peak in the ripple. When it turns around again and starts going left—that's a valley. When the string is moving very fast, the peaks are closer together. The number of peaks that hit your ear per second is called the frequency of the sound.
Frequency is measured in hertz, abbreviated Hz. If somebody were slapping you at 4 Hz, you would be slapped 4 times per second. Sounds you can hear are ripples of air slapping your ear between 30 and 20,000 times per second.
I'm reminded of the Zen student who asked his master about the essence of music. The master responded by slapping the student's ear 10,000 times in one second with tiny puffs of air, and the student was enlightened.
But that was a special case. Usually sounds are composed of many frequencies of noise added together. The thing is, if we perceive the sound as having a musical pitch (you could pick a piano key that would match the pitch of a wolf howling at the moon if it were in tune, but you'd have trouble doing that for an ocean wave crashing into the shore), it's usually because the loudest frequencies are multiples of one another. When we hear frequencies stacked like that, we tend to perceive them as a single pitch.
For example, we would say that the middle C key on a piano plays at about 262 Hz, but it's actually making noise at 262 * 2 Hz, 262 * 3 Hz, 262 * 4 Hz, etc., and many other frequencies as well, to lesser extents. Because of how our brain blends frequency multiples together (we call those multiples "harmonics" and "octaves"), we summarize by saying that middle C is 262 Hz, the first harmonic.
You can modify and run this ChucK script to hear what single frequencies sound like (in this case, 262 Hz):
SinOsc s => dac;
262 => s.freq; // sets the frequency in hertz
4::second => now;
By the way, if you aren't familiar with any programming languages, and you're interested in understanding the code on this page, I'd recommend going through ChucK's documentation and reading tutorials on the language. I'm going to skimp on explaining the code to keep things concise.
Regardless of whether you understand what it does, run the code! It's fun, or at least not life-threatening.
The Chromatic Scale, and MIDI
In so-called Western music, we divide octaves into 12-note scales. But as you remember, octaves result from doubling a pitch, not adding some random number like 8.3. That means the relationship between octaves isn't linear—it's exponential. That's true of the relationship between the 12 notes of a chromatic scale as well.
So what would be the 12 frequencies of the the chromatic scale ranging from 100 Hz to 200 Hz (a single octave)?
| Linear (WRONG) (why would you think this was right?) |
Logular (RIGHT) (you are on the right path) |
|---|---|
| 100 Hz | 100 Hz |
| 108.33333333333333 Hz | 105.94630943592954 Hz |
| 116.66666666666667 Hz | 112.2462048309373 Hz |
| 125 Hz | 118.92071150027212 Hz |
| 133.33333333333334 Hz | 125.99210498948734 Hz |
| 141.66666666666669 Hz | 133.48398541700345 Hz |
| 150 Hz | 141.42135623730954 Hz |
| 158.33333333333334 Hz | 149.83070768766819 Hz |
| 166.66666666666669 Hz | 158.74010519682 Hz |
| 175 Hz | 168.17928305074298 Hz |
| 183.33333333333334 Hz | 178.17974362806794 Hz |
| 191.66666666666669 Hz | 188.77486253633879 Hz |
| 200 Hz | 200 Hz |
The formula for the correct scale is easy to derive. You're hunting for a number x for which 100 ⋅ x Hz is the second note of the scale, (100 ⋅ x) ⋅ x Hz is the third note of the scale, ((100 ⋅ x) ⋅ x) ⋅ x Hz is the fourth note of the scale, and so on, so that the twelfth note is 200 Hz. In other words, x is a number which you can multiply 100 Hz by 12 times to get 200 Hz. Solve for x, campers:
100 * (x^12) = 200
x^12 = 2
x = 12th root of 2
x = 1.0594630943592953
As you can see, my ability to render math equations is top notch. It is exceeded only by my ability to render ChucK code, if I do say:
// hear the difference 1.0594630943592953 makes
SinOsc s => dac;
100 => s.freq;
while(1) {
1::second => now;
s.freq() * 1.0594630943592953 => s.freq;
}
If you'd like to hear what it would sound like if we had grown up listening to a scale with 10 or 30 notes instead of 12, solve the equation again and plug the new number into the code above.
This seems like a good time to mention that the first rule of The Computer Music Club is that you don't tell anyone you're in TCMC. The second rule is that if anyone asks, you vehemently deny it. The third rule is that following any rule of conventional music is strictly disallowed, so don't use 12-note scales. In fact, if your music is at all recognizable as music to anyone outside the club, something has gone terribly wrong. You should start over.
But say you wanted to use a conventional 12-note scale, and not only that, you want it to consist of notes you could find in conventional music software, playable by conventional instruments. (A 100 Hz key doesn't exist on any piano that I know of, except perhaps one vandalized by TCMC members.) It'd be great if you could calculate the frequency of a note more simply than with seemingly arbitrary frequencies like 262 Hz and scale factors like 1.0594630943592953.
So we have MIDI note notation. It defines MIDI note 60 as middle C, 61 as C#, 62 as D, and so on. It pretty much numbers the keys on a piano—which, I should point out, roughly follow the 1.0594630943592953 rule.
Since "middle C" has a standard definition in hertz—at least, for our purposes I'm going to say it does—it grounds all MIDI-controlled music to the same chromatic scale. If everyone chose their own starting frequency and started multiplying 1.0594630943592953 to derive the rest, nobody would be in tune with anyone else, and whenever anybody tried to play together, it would sound like that crazy piece I wrote in college and let me tell you, DJs would have a much harder time.
MIDI is great because it's a standard notation that pretty much all music software understands, even ChucK.
// hear the difference 1 makes
SinOsc s => dac;
40 => int note;
while(1) {
Std.mtof(note) => s.freq; // mtof = MIDI-to-frequency
1::second => now;
note + 1 => note;
}
Pros use MIDI, and the sooner you do, the sooner you're in business.
Even More Useful Scales
We're to the point that if you wrote a program which composed music using random notes from a chromatic scale (probably by their MIDI numbers), it might accidentally stumble across a catchy tune. In fact, try this script and let me know when you have made your first million in record sales.
SinOsc s => dac;
while(1) {
Std.mtof(40 + Std.rand2(0, 18)) => s.freq;
1::second => now;
}
If you didn't luck out, that's okay. There's more we can do: our model is still incomplete. In the same way we constrained the ocean of all possible frequencies to those related by factors of 1.0594630943592953, and in the same way we constrained what was left to the set which are represented by MIDI notes, we can constrain MIDI notes to the sets called "scales."
Here again, the octave is special, because what a scale does is specify which notes of an octave are valid. For example, of the 12 chromatic notes in an octave, the major scale allows 7 (do, re, mi, fa, so, la, and ti). The minor scale allows 7 notes as well, but the pentatonic allows only 5. There are a lot of scales, actually, all of which will sound better or worse to you depending on what type of music you're creating... and the skill with which you use them!
Here's a boring way to use them:
SinOsc s => dac;
40 => int note;
[1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1] @=> int scale[];
while(1) {
if(scale[note % 12] == 1) {
Std.mtof(note) => s.freq;
1::second => now;
}
note + 1 => note;
}
It's boring, but a useful toy for hearing a scale. The one above is a major scale, but flip the ones and zeroes and you can make any scale you like. To get you started, I've prepared you this cornucopia of scales to experiment with.
- [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]
- [1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1]
- [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0]
- [1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0]
- [1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0]
- [1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0]
- [1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1]
- [1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1]
- [1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1]
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Now, a big reason MIDI is useful is because it singles out a frequency to use as the point of reference. With scales, it's actually useful to be able to start on any note that you want (the leftmost note in the scale array above is considered the start, which is why it's always 1), so instead of picking one standard root, we name the scale by which note it starts on.
When the first note of the array lands on MIDI 60, for example, that's "C major," because MIDI 60 is a C and the array holds the major scale. The scale in the ChucK example is also C major because note % 12 is zero at every multiple of 12, including 60, which you'll have to trust me is a multiple of 12.
Grab an image of a piano keyboard (one with note names, if you're not already familiar), change the note % 12 in the code to (note + 5) % 12, and see if you can explain why we'd call that G major and why it fits in so nicely when you play it over Beethoven's Piano Concerto No. 4.
To Be Continued
Soon.