Synth Reference

For producing (cheesy) synthesized sounds, we've written a synth module that can be used to build simple music out of notes and beats. The selection of instruments and their quality is extremely limited, but it allows us to make music as an alternative to drawing things with the turtle module.

Music Concepts

To use this module you will need a basic grasp of some musical concepts, although you will not need to really be familiar with music theory or know how to play an instrument. The basic concepts you'll need are:

  1. Tracks. A "track" is a series of notes that are all played together, but which can be separated from other tracks. Tracks can be saved to a '.wav' file, and if you install the extra simpleaudio module, they can be played on your speakers directly within Python. The synth module has a way to change the "current track," and any notes that you create will always be added to whichever track is current. You can use multiple tracks and later mix them together into a single track, or you can simply add all of your notes to a single track. For the most part in this class, we'll use the latter strategy, as it's simpler, and you won't have to worry too much about tracks.

  2. Notes vs. beats vs. "rests". The synth module has three functions for adding music to a track: addNote, addBeat, and addRest.

    • addRest is the simplest of these functions, because a "rest" in musical terminology is a period of silence, when the musician gets to take a rest. Adding rests doesn't actually change the sound of a track, but it may extend its duration, and it can be convenient to add a rest to create a gap between notes if you want one, although it's not strictly necessary. Note that adding a rest on top of an existing note won't erase that note (and there's currently no way to erase notes you've added to a track).
    • addBeat is still pretty simple, and is used for drums and other percussion instruments which don't have a specific pitch to them. You can specify when the beat should be added, what instrument to use, and how long the beat should last.
    • addNote is the most complicated function, and is used for any instrument that has a specific pitch. You'll need to specify when in the track to add the note, the instrument to use, the pitch of the note, and its duration.
  3. Pitches. A pitch (or tone, or frequency) in music refers to how high or low a sound is, and it's represented as a number. You won't have to do math with these numbers yourself (as that's quite complicated) but you will have to use the synth library's pitch modification functions to compute higher or lower pitches when you need them, and there are a few concepts to be aware of:

    • A "half-step" is the smallest unit of pitch difference that the synth module can easily create, and there are halfStepUp and halfStepDown functions for calculating higher and lower pitches. Half steps are used as part of the basis for classical music, and there are 12 half-steps in an "octave," which represents a doubling of pitch.
    • A "step" or "whole step" is a larger unit of pitch difference that is usually equivalent to two half-steps. You can use the stepUp and stepDown functions to produce pitches that are higher or lower by a step. However, steps are not as regular as half-steps, because there are 8 whole steps in an octave, not 6. Classical music is just complicated like that. You won't need to worry about this too much: just use the stepUp and/or stepDown functions and they'll take care of what the pitch should be.
    • A "leap" is not a real musical term, but synth has leapUp and leapDown functions which move between notes on what is called a pentatonic scale that uses 5 notes per octave instead of 8. Pentatonic scales are used in several non-Western music systems, and they're beginner-friendly because no matter what notes you combine, the result will always sound nice (in contrast, combining notes that are the wrong number of steps or half-steps apart can sound pretty horrible!). You don't need to worry too much about the details here, just pay attention to when we ask you to use 'leaps' vs. 'steps'.

    In addition to moving pitches up and down, you'll need to be able to talk about specific pitches, and there are two systems that synth provides. Scientific pitch notation is a classical pitch system which labels each of the eight steps in an octave using the letters "A" through "G", and then pairs those letters with a number indicating which octave the note is in. So for example, the note B3 is one octave below (i.e. 8 whole steps below, or 1/2 the frequency of) the note B4. Similarly, G4 is 3 steps above D4. The confusing thing about this system is that each octave starts on a C, not an A, so B3 is only one step below C4, which is the first note in octave 4. The synth module makes global variables named for all of the pitches C0 through B9 available. In addition to these variables, there is a set of variables named P0 through P14 which refers to successively higher notes on a pentatonic scale. These have the same values as some of the scientific pitch notation notes, they're just alternate names for them, but they can be used instead for a simpler way of naming higher and lower notes, with larger pitch differences between those notes. For reference, P5 is equal to C4, and that tone is a reasonable "middle" tone that's neither very high nor very low. If you happen to be familiar with musical notation, or if you've seen sheet music and wondered about how it works, here is an image of how these different pitch values map to sheet music notation:

    An image showing the bass and treble clefs with one note on each line and in each space ascending in each clef from two steps below the bottom line to one step above the top line. Each note is labeled underneath with its corresponding letter and octave number, from E2 to B3 on the bass clef and from C4 to G5 on the treble clef.

Reference

What follows is a summary of each of the most important synth functions that you'll need to use, including a brief description of what the function does and examples of how it could be used.

Note that the simplest way to get access to these functions is to use the following import statement (assuming that synth.py is in the same directory as the file you're working on):

from synth import *

Also note that in order for synth.py to play sounds directly from within Python, you'll need to the "Manage Packages" feature in the "Tools" menu in Thonny to install the "simpleaudio" package, although you can use synth.py without this feature to produce .wav files that you can use a separate media player program to play back.

Basic Sounds:
Function Effect Example(s)
addRest Adds a period of silence to the <a href="#setActiveTrack">current track</a>. The first argument specifies when the rest should begin, and the second specifies how long it will be. If the end of the rest is after the end of the current track, the track's duration will be increased. addRest(1.2, 0.4) addRest("end", 0.1)
addBeat Adds a percussive sound (with no specific pitch) to the <a href="#setActiveTrack">current track</a>. The first argument specifies when the sound should begin, the second specifies the instrument to use, and the third specifies how long it will be. If the end of the beat is after the end of the current track, the track's duration will be increased. addBeat(1.2, snare, 0.2) addBeat(0, "snare", 0.2)
addNote Adds a note to the <a href="#setActiveTrack">current track</a>. The first argument specifies when the sound should begin, the second specifies the instrument to use, the third specifies the pitch of the note, and the fourth specifies how long it will be. If the end of the note is after the end of the current track, the track's duration will be increased. addNote(0, keyboard, C4, 0.2) addNote("end", "harmonica", 440, 0.2)
Notes For all three functions, the string "end" may be given as the start time instead of a number of seconds, which will cause the rest, beat, or note to be added starting at the end of the current track. Also, for addNote and addBeat, the instrument may be either one of the instrument functions defined in synth.py, or a string which is the name of one of those functions.
Instruments:
Function Effect Example(s)
kick A percussion instrument (use it with addBeat) meant to sound like a kick drum: a deeper reverberating sound. Kick drums are hard to synthesize well, so this instrument won't sound that great, especially if you're using laptop speakers. addBeat(0, kick, 0.2)
snare Another percussion instrument (use it with addBeat) meant to sound like a snare drum: a sharp and loud sound. Snare drums are easier to synthesize, so this works better than kick. addBeat(0, snare, 0.2)
beep A very basic pitched instrument (use it with addNote) that sounds like (and well, is) an electronic beep. addNote(0, beep, 440, 0.2)
keyboard A basic pitched instrument (use it with addNote) that's supposed to sound vaguely like a piano. addNote(0, keyboard, A5, 0.1)
harmonica A pitched instrument (use it with addNote) that's supposed to sound vaguely like a harmonica or other wind instrument. addNote(1.5, harmonica, P3, 0.3)
Pitch Functions:
Function Effect Example(s)
halfStepUp Takes a pitch value (a number) and returns a pitch value that's one half-step above the provided value. An optional second argument specifies a number of half-steps to take, which could be negative to take half-steps down (but there's also the halfStepDown function for that). One half step up is the same as multiplying by 2<sup>1/12</sup>, or about 1.05946, while one half-step down is the same as dividing by this value. pitch = halfStepUp(440) pitch = halfStepUp(C4, 3) pitch = halfStepUp(pitch)
halfStepDown Works just like halfStepUp (including the optional second argument) but takes half-steps down instead of up. pitch = halStepDown(P5)
stepUp Takes a pitch value (a number) and returns a pitch that's one whole step higher than the given pitch, but first rounds that pitch to the nearest piano key. So for example, an input of 440.5 (slightly above A4) would be rounded to 440.0108 (exactly A4) before being raised a whole step to give a result of 493.8954, which is the note B4, two half-steps above A4. This gets even more complicated because sometimes the distance between whole steps is only one half-step, in which case this function only actually changes the pitch by that half step, but it will always change an A into a B, a B into a C, a C into a D, and so forth. Like halfStepUp, a second argument may be provided to step up multiple steps at once, and a negative second argument will step down (but see also stepDown). pitch = stepUp(440) higher = stepUp(lower, 2)
stepDown Works just like stepUp, except it returns a lower pitch instead of a higher pitch. The same rounding rules apply. pitch = stepDown(G3, 2) lower = stepDown(B4)
leapUp / leapDown These two functions work like stepUp and stepDown, but they return a pitch that's one place up or down a pentatonic scale, after rounding to the nearest note on that scale. This usually translates to two whole steps of pitch difference, but it depends on where you are in the scale, since the five notes per octave are not evenly spaced. Because of rounding, two leapUp results from notes that are as much as a whole step apart may be the same (e.g., leapUp(E4) and leapUp(F4) both return the pitch G4). Like stepUp and stepDown an optional second argument may be provided to leap multiple notes at once. pitch = leapUp(P3) lower = leapDown(P5, 3)
Track Controls:
Function Effect Example(s)
setActiveTrack setActiveTrack takes a track name string as its only parameter and either creates a new track with that name or switches to the existing track with that name. Any notes that are added are always added to the current track, and setActiveTrack changes which track is current. If you never call setActiveTrack, a track named "default" is used for any notes that are added. setActiveTrack("drums")
eraseTrack Completely erases the contents of the current track, and sets its duration back to zero. Use setActiveTrack to control which track is the current one. eraseTrack()
trackDuration Returns the duration of the current track, starting at t=0 and ending at the end of the last note, beat, or rest that has been added to the track. d = trackDuration()
mixTracks Mixes two tracks together to create a third, new track. The first two parameters name the tracks to be mixed, while the last parameter is the name of the new track that will be created (it must not already be used by an existing track). mixTracks('melody', 'harmony', 'song')
Generating Output:
Function Effect Example(s)
printTrack Prints out a summary of the notes, beats, and rests that have been added to the current track. Can be useful for verifying the exact pitches or durations of different notes. This should not take a long time to run. printTrack()
prepareTrack Prepares the current track for playback or saving to a file. You do not need to call this function, because it will be called automatically when saveTrack or playTrack is called for the first time. However, this function takes a while to run (usually a little longer than the duration of the track you're preparing), so be patient. Thankfully, once this function has been called, whether manually or automatically, the results are saved, and both saveTrack and playTrack should be quick afterwards. prepareTrack()
saveTrack Saves the current track in a file with the given file name. The file name argument should be a string that ends with ".wav", since the file is saved in WAV format. Note that for long tracks, this uncompressed format may result in pretty large files. This function also takes some time to process the track, often roughly about as long as the duration of the track, or even a bit longer (see prepareTrack). saveTrack("song.wav")
playTrack Plays the current track using your computer's sound system. For this to work, you must first install the simpleaudio module. Like saveTrack, this may take a while to prepare the track before playback starts, so be patient (see prepareTrack). The longer the track, the more time it will take to prepare. Also be warned: the default volume is pretty loud, so you may want to turn your computer's volume down first, especially if you're using headphones. Because playTrack isn't quick, if you want to listen to output several times, it's better to use saveTrack (or use both functions) and play back the saved file. playTrack()

Pitch Details

More details about pitches: in fact, the number that represents a pitch tells us how many times per second that sound vibrates the air: "higher" pitches are produced by faster vibrations (meaning more tiny pulses of high-pressure air per second) while "lower" pitches have slower vibrations. The range of frequencies that's comfortable goes from something like 20-30 pulses-per-second on the very low end, all the way up to something like 10,000-20,000 pulses-per-second on the high end, and the range is so large in part because pitch is multiplicative, not additive. In other words, when we go "up" a certain amount in terms of pitch, we're multiplying the frequency of the note, not adding to it. Going up by the same amount again and again represents successive multiplications, which is why there's such a large range of values. Pitches are stored using floating-point numbers (or rarely, integers), and you won't have to do any of the math yourself because of the functions like stepUp or leapDown that the synth module provides.