Take-home Assessment #3: Beat Machine
Electronic musical tools help democratize music, allowing those without the training or capability to play a musical instrument to express musical ideas.
In this assessment, we'll use the music library to implement one of these basic tools, the drum machine, that form the basis for many modern electronic songs!
For this take-home assessment, please write your code in a file called beat-machine.scm and turn it into Gradescope when you are done. As you format your work, please make sure to:
- Include a complete header in your source file indicating assignment, authorship, etc., similar to our labs.
- Use the
lablibrary to label the different parts of the output of your program as outlined in the instructions below.
The Beat Machine
A staple of electronic music is the drum machine. A drum machine allows the user to create patterns of drum beats from a set of sound samples imitating real-world percussive instruments such as a snare drum or a cymbal. They can be found all over modern popular music, e.g., dance pop and electronic music from the 80s and 90s:
- Phil Collin's In the Air Tonight uses a Roland CR-78.
- Michael Jackson's Man in The Mirror uses a Linn 9000
- Aphex Twin's Heliosphan uses a Roland TR-909.
The primary a user interacts with a drum machine is usually a row of button, e.g., on the Roland TR-808:

Each button corresponds to one pulse of a groove, so that a user can instruct the drum machine to play a sound on a given beat by simply pressing the appropriate button.
For example, the 16 buttons on the 808 break up a groove into 16 equal parts, i.e., sixteenth notes.
If were to press the following buttons on the 808 (x indicates that button has been pressed):
[x][ ][ ][x] [x][ ][ ][x] [x][ ][ ][x] [x][ ][x][ ]
The drum machine would produce the following beat, emulated here in Scamper's music library:
(import music)
; 16th notes on the bass drum
(define bass (note 35 sn))
; A 16th rest
(define r16 (rest sn))
(mod percussion
(seq bass r16 r16 bass
bass r16 r16 bass
bass r16 r16 bass
bass r16 bass r16))
Many drum machines feature a grid of these buttons where each row corresponds to one instrument, e.g., the bass drum or a snare and each column corresponds to a pulse. In this manner, a user of a drum machine can "program" in their beat live by toggling different voices and pulses with the grid of buttons!
Problem 1: Individual Pulses
Let's build up a definition of a beat-machine by decomposing this problem in a bottom-up fashion.
The elementary element of a drum machine is a single button that toggles whether a note or a pulse is played.
In our implementation, we'll use a number to represent a single button and whether it is toggle:
0will correspond to the button being off.1will correspond to the button being on.
(Note: a boolean is a more appropriate datatype to represent the state of a button. But in the later parts of this problem, we will add more "states" to each button than "on" and "off" which will justify our current choice of a number!)
To begin, write a function (button->comp instr d state) that takes:
- An
instrvalue corresponding to the MIDI percussion voice that should be played. - A duration
dthat is the length of the pulse. - The
stateof the button, a number.
And returns a composition that is either a rest or a note with appropriate voice and duration depending on whether state is 0 or 1, respectively.
For example:
(button->comp 35 sn 0)would produce a 16th note rest, i.e.,(rest sn)(button->comp 35 sn 1)would produce a 16th note on the bass drum, i.e.,(note 35 sn).
Problem 2: A Row of Pulses
Now let's scale up to a whole row of buttons. We haven't introduced a way to create genuine buttons in Scamper. However, we can use source code to emulate this setup!
Since one button is represented by a number, we'll use a list of numbers to represent a row. For example, the beat from the introduction to this problem:
[x][ ][ ][x] [x][ ][ ][x] [x][ ][ ][x] [x][ ][x][ ]
Would be represented by the following Scheme list:
(define bass-pattern
(list 1 0 0 1
1 0 0 1
1 0 0 1
1 0 1 0))
(Observe how I use spacing to make the division of pulses as clear as possible!)
Next, use button->comp to write a function (line->comp instr dur line) that takes:
- An
instrvalue corresponding to the MIDI percussion voice that should be played. - A duration
dthat is the length of the pulse. - A
lineof button states, i.e., a list of numbers.
And returns a composition corresponding to the given parameters.
For example, calling (line->comp 35 sn bass-pattern) with the bass-pattern defined above would produce the bass beat that we hard-coded in the introduction to this problem.
Problem 3: A Pattern of Pulses
To capture a complete drum groove, we now need to represent a grid of button. Here, we'll take advantage of our source code to represent this grid as a list of list of numbers. When formatted appropriately, our list of lists can look like a grid! For example, in our introductory lab on music, we introduced a simple rock beat with 8th note subdivisions:
Bass (35): [x][ ] [ ][ ] [x][ ] [ ][ ]
Snare (38): [ ][ ] [x][ ] [ ][ ] [x][ ]
Hi-hat (42): [x][x] [x][x] [x][x] [x][x]
Crash (49): [x][ ] [ ][ ] [ ][ ] [ ][ ]
We can represent this pattern with a list of lists as follows:
(define pattern
(list
(list 1 0 0 0 1 0 0 0)
(list 0 0 1 0 0 0 1 0)
(list 1 1 1 1 1 1 1 1)
(list 1 0 0 0 0 0 0 0)))
Using line->comp, define our main function, (beat-machine d pat), that takes:
- A duration
dthat is the length of the pulse of our pattern. - A pattern
patrepresenting the full groove as a list of four lists of numbers.
Each list in this overall list corresponds to one of the voices of the drum kit.
In the definition of pattern above:
- The first list corresponds to the bass drum (MIDI value 35).
- The second list corresponds to the snare drum (MIDI value 38).
- The third list corresponds to the hi-hat (MIDI value 42).
- The fourth list corresponds to the crash cymbal (MIDI value 49).
The order of the list is arbitrary, so you can choose to order your instruments' list in any way that you choose as long as you are consistent (and document your choices in beat-machine's comment!).
beat-machine should return a composition that is the complete groove specified by the pattern.
So (beat-machine en pattern) using the pattern defined above would produce the rock drum groove from our lab.
Make sure that beat-machine produces a composition that uses percussion voices!
Problem 4: More Options
Because our beat-machine uses numbers to represent buttons, we have the room to add more options for each pulse other than "on" and "off."
To this end, implement three additional functions that allow our beat machine to make different kinds of sounds.
(ghost instr d)creates a ghost note with the given voiceinstrand durationd. A ghost name is a (very) quiet note relative to the regular volume of the composition.(accent instr d)creates n accented note with the given voiceinstrand durationd. An accented note is a loud note relative to the regular volume of the composition.(roll instr d)creates a roll with the given voiceinstrand durationd. A roll is four evenly-spaced notes within the time durationd. These individual notes are played at a softer volume than the regular volume of the composition. For example, ifdis a quarter note (qn), then(roll ... d)produces a composition of four 16th notes (\( \frac{1}{4} \times 4 = \frac{1}{16} \)) played softly in sequence.
To modify the volume of your produced compositions, the dynamics modification will be useful.
Note that the default loudness of the composition is 64.
To implement roll, you will likely need to do some manual computation over durations.
A duration is implemented as a simple fraction with the following functions:
(dur x y)creates a duration \( \frac{x}{y} \). For example(dur 1 4)is equivalent to a quarter note.(numerator x)retrieves the numerator of a duration/fraction.(denominator y)retrieves the denominator of a duration/fraction.
Once you have implemented these functions, you will need to add them to your beat machine.
To do so, you should assign different numbers to each new function and modify button->comp to use those functions when that number is encountered in a pattern.
The choice of assignment is up to you.
However, you certainly need to document your choice in the documentation comment for beat-machine.
These kinds of assumptions about the inputs to our function (here, the interpretation of the numbers in the provided lists) are excellent candidates for documentation!
Problem 5: Make Some Beats
With our basic beat-machine finished, let's try it out!
define three different patterns and use your beat-machine to create three different grooves.
In your code, you should use the (description ...) function from the lab library to output a description of your groove.
Below the description, you should call beat-machine with the appropriate arguments to create a playable version of your groove!
Feel free to listen to your favorite music and try to approximate the beat to the best of your ability. Or take the basic rock beat and try shifting things around to find something new! If you need some inspiration, here are some ideas:
-
A basic funk groove (e.g., "What is Hip" by Tower of Power is characterized by 16th note subdivision. The hi-hat plays constant 16th notes, the snare plays on the second and fourth beats, and the bass drum on the first and third beats. However, many funk grooves make liberal use of ghost notes on the snare to fill the space!
-
Many latin grooves (e.g., "Mas Que Nada" by Sergio Mendes) create a syncopated effect with the snare and bass drum.
snare: [ ][ ] [x][ ] [ ][x] [ ][ ] bass: [x][ ] [ ][x] [x][ ] [ ][x]
Problem 6: Extended Beat Machine
The beat-machine is a complete program you can show off to your friends.
However, the joy of building a program is that there is endless room for additions and customization!
To close this assessment, add two additional features to your beat machine implementation, extending the functionality in significant ways. For example, you may:
- Add an additional percussion voice to the machine, e.g., a tom-tom drum.
- Create a new type of note like
ghostandaccentor aggregate set of notes likeroll. - Devise a way to specify include melody into the beat machine, e.g., playing a collection of notes (an apreggio) instead of a percussion voice.
Importantly, you changes must preserve the original behavior of beat-machine, i.e., the earlier parts of this assessment need to stay intact.
Beyond this requirement, you are free to tinker to your heart's content!
Notably, you may introduce additional parameters to beat-machine as needed by your implementation.
For each of your features, you should:
- Include a one-sentence description of your feature in a
(description ...)tag. - Below the
description, use the feature in a pattern and output the resulting groove withbeat-machine.