This task is part of project03 which is due at 23:00 EDT on 2023-09-26.
You have the option to work with a partner on this task if you wish. Working with a partner requires more work to coordinate schedules, but if you work together and make sure that you are both understanding the code you write, you will make progress faster and learn more.
You can download the starter code for this task using this link.
You can submit this task using this link.
Put all of your work for this task into the file
highNotes.py
(which is provided among the starter files)
For this problem set, you have a choice: you may complete this task OR
the treeTops task, but you do not need to complete both.
treeTops is graphics-oriented and
works with turtle
graphics, while highNotes (this task) is based on
audio and music instead, using the wavesynth
library.
This task will help you practice the material on custom functions and how
to use several functions together to achieve a complicated result. It
will also have you work with functions that have both side effects and
return values. For this task, you will work with the
wavesynth.py
audio library.
For this task, you will be writing code to create a customizable song that involves repetition with variation: three-note motifs are repeated three times to form a phrase, and the whole song consists of two such phrases. (Note: the audio can be quite loud, so you may want to turn down your sound first until you can find the right volume).
Click here to download test_song.wav
To make the task more interesting, the song
function that you write
must return the pitch value of the highest note in the song (pitch is a
number indicating how high or low a sound is).
You will need to write three separate functions that work together to
produce the song: motif
which creates three-note patterns, phrase
which creates phrases out of three motifs, and song
, which combines two
phrases into a song.
wavesynth.py
UsageFor this task, you will only need to use the
climbUp
and
climbDown
functions from wavesynth
;
we have provided a function called doubleNote
which you will use to
create notes.
The provided testing code also uses
playTrack
,
saveTrack
, and
printTrack
to play and show the results of your code.
motif
The songs that we'll be creating have repeated three-note sequences in
them, so we will begin with a function that creates these. Technically,
each 'note' will actually be a chord: two notes playing at the same time,
but you do not have to construct these chords: the provided doubleNote
function will do that.
Here are examples of what two different motifs might sound like, produced using different arguments to the motif function. In the first motif, produced by calling `motif(0.3, 1, 1)`, the three notes are simply ascending by climbing up 1 rung of the scale each time, e.g., C-D-E. In the second motif, produced by calling `motif(0.2, -1, 2)`, the second note is one rung below the first, and the third note is two rungs above the second note (so 1 rung above the first note), e.g., C-B-D.
Click here to download test_motif_1.wav
Click here to download test_motif_2.wav
motif
must accept three parameters (in
this order):
climbUp
) to climb up between
the first and second notes (to climb down, use a negative number).motif
must call doubleNote
three
times to add three
note-pairs back-to-back, using the given pitch intervals between
them. motif
must also return the
pitch of the highest note it creates. To
do this, you can simply take the return values from each call to
doubleNote
and use max
to find
the highest value, because the doubleNote
function itself returns the
pitch of the highest note it adds.
Finally, motif
must maintain an
invariant: by the end of the
function, the current pitch must be returned to the initial pitch, even
though the last note will often not be at that pitch.
We've provided some testing code which uses optimism
to test motif
.
Even if you have not defined the other functions yet, those tests will be
skipped, so once you are done with motif
, you can run the
test_highNotes.py
file to see if it is working properly.
These motif
examples show what should be printed when
it runs, indicate what the return value should be, and include an audio
snippet you can play so you can hear what it should sound like.
Once motif
is working, we need to assemble three motifs together into a
phrase, where again there is some pitch change between each motif. The
phrase
function will have four parameters
which specify the note duration, the first gap interval for the repeating
motifs, the second gap interval for the repeating motifs, and the overall
pitch interval between motifs. The first three arguments must be passed
directly to motif
to create
the three motifs, while the final argument specifies a pitch interval to
climb up in between each pair of motifs. phrase
will add nine specific
notes to the track, for example if the
gaps are both 1 and the interval is 3, as in phrase(0.2, 1, 1, 3)
, the
nine notes will each be one rung above the last and they'll form an
ascending scale.
Unlike motif
, where the pitch must return to the starting value at the
end, phrase
must leave the pitch at the pitch used for the final motif
it creates. Because motif
does
not change the current pitch, this final pitch will be the result of
climbing up twice using the specified between-motif interval. By doing
this, any additional phrases or motifs will start from the same base
pitch as the last motif of the previous phrase.
Like motif
, phrase
must return the pitch of the highest note that it
creates. This can be achieved by calling
max
with the result values from
the three motifs it created.
These phrase
examples demonstrate how it should
work.
After defining your phrase
function, you're ready to create the song
function, which has five parameters and adds
two phrases to the track. The parameters are:
Essentially, the 2nd through 5th parameters define the up or down pitch
changes used in two different motifs, and then phrase
is
used twice to create one phrase
based on the first motif and another based on the second. The inter-motif
intervals used for the two phrases are not given as arguments and are
instead always the same: 1 for the first phrase and -1 for the second
phrase.
As with motif
and phrase
, song
must return the pitch of the
highest note in the whole song. Once again, you
will use max
on the results of
the two phrase
calls to compute this value. It should also leave the
pitch back at the starting pitch when it is
done.
This song
example demonstrates how it should work.
We have provided a file named test_highNotes.py
which can be used to
test your work. It will skip tests for any functions that it can't find,
so you can use it even just to test motif
before you have written
phrase
and/or song
. We recommend that you test each function as you
build it, because the lessons you learn from debugging your earlier
functions will help you improve the later functions.
motif
function may not call addNote
directly (which is
unnecessary, since you will call doubleNote
) Similarly, your
phrase
function may not call addNote
or
doubleNote
, because it
needs to rely on motif
to produce notes, and your song
function
must also not call those
functions (it should call
phrase
to create notes).motif
function may not call
setPitch
, because it
needs to return to whatever the starting pitch was, and moving to a
specific pre-defined pitch makes that impossible. This restriction
also applies to phrase
and to song
.addNote
directly, for example).song
, expect to wait a few seconds (maybe as many as 10-20)
for the audio to play.wavesynth.py
when it prints tracks are
rounded off, but your code should not do any rounding.motif
examples
These examples show how motif
should work, including what it should
print, what it should return, and the audio it should produce.
In []:PrintssetPitch(D4) result = motif(0.2, -1, -2) printTrack() print("Highest pitch was:", round(result, 3)) playTrack()
at 0s a 0.2s keyboard note at D4 (60% vol) at 0s a 0.2s keyboard note at F4 (18% vol) at 0.2s a 0.2s keyboard note at C4 (60% vol) at 0.2s a 0.2s keyboard note at E4 (18% vol) at 0.4s a 0.2s keyboard note at A3 (60% vol) at 0.4s a 0.2s keyboard note at C4 (18% vol) Highest pitch was: 349.237Audio In []:PrintssetPitch(G3) result = motif(0.35, 2, 3) printTrack() print("Highest pitch was:", round(result, 3)) playTrack()
at 0s a 0.35s keyboard note at G3 (60% vol) at 0s a 0.35s keyboard note at B3 (18% vol) at 0.35s a 0.35s keyboard note at B3 (60% vol) at 0.35s a 0.35s keyboard note at D4 (18% vol) at 0.7s a 0.35s keyboard note at E4 (60% vol) at 0.7s a 0.35s keyboard note at G4 (18% vol) Highest pitch was: 392.005Audio
phrase
examples
These examples show how phrase
should work, including what it should
print, what it should return, and the audio it should produce.
In []:PrintssetPitch(B3) result = phrase(0.2, 1, 1, 3) printTrack() print("Highest pitch was:", round(result, 3)) playTrack()
at 0s a 0.2s keyboard note at B3 (60% vol) at 0s a 0.2s keyboard note at D4 (18% vol) at 0.2s a 0.2s keyboard note at C4 (60% vol) at 0.2s a 0.2s keyboard note at E4 (18% vol) at 0.4s a 0.2s keyboard note at D4 (60% vol) at 0.4s a 0.2s keyboard note at F4 (18% vol) at 0.6s a 0.2s keyboard note at E4 (60% vol) at 0.6s a 0.2s keyboard note at G4 (18% vol) at 0.8s a 0.2s keyboard note at F4 (60% vol) at 0.8s a 0.2s keyboard note at A4 (18% vol) at 1s a 0.2s keyboard note at G4 (60% vol) at 1s a 0.2s keyboard note at B4 (18% vol) at 1.2s a 0.2s keyboard note at A4 (60% vol) at 1.2s a 0.2s keyboard note at C5 (18% vol) at 1.4s a 0.2s keyboard note at B4 (60% vol) at 1.4s a 0.2s keyboard note at D5 (18% vol) at 1.6s a 0.2s keyboard note at C5 (60% vol) at 1.6s a 0.2s keyboard note at E5 (18% vol) Highest pitch was: 659.271Audio In []:PrintssetPitch(C4) result = phrase(0.15, 3, 2, -2) printTrack() print("Highest pitch was:", round(result, 3)) playTrack()
at 0s a 0.15s keyboard note at C4 (60% vol) at 0s a 0.15s keyboard note at E4 (18% vol) at 0.15s a 0.15s keyboard note at F4 (60% vol) at 0.15s a 0.15s keyboard note at A4 (18% vol) at 0.3s a 0.15s keyboard note at A4 (60% vol) at 0.3s a 0.15s keyboard note at C5 (18% vol) at 0.45s a 0.15s keyboard note at A3 (60% vol) at 0.45s a 0.15s keyboard note at C4 (18% vol) at 0.6s a 0.15s keyboard note at D4 (60% vol) at 0.6s a 0.15s keyboard note at F4 (18% vol) at 0.75s a 0.15s keyboard note at F4 (60% vol) at 0.75s a 0.15s keyboard note at A4 (18% vol) at 0.9s a 0.15s keyboard note at F3 (60% vol) at 0.9s a 0.15s keyboard note at A3 (18% vol) at 1.05s a 0.15s keyboard note at B3 (60% vol) at 1.05s a 0.15s keyboard note at D4 (18% vol) at 1.2s a 0.15s keyboard note at D4 (60% vol) at 1.2s a 0.15s keyboard note at F4 (18% vol) Highest pitch was: 523.264Audio
song
example
This example shows how the whole song
function should work, including
what it should print, what it should return, and the audio it should
produce. Since we use gap parameters 1
, 2
, -1
, and -2
, we get
motifs that move up and then down. The first motif with jumps of 1 and
then 2 is B-C-E when written as letters. This repeats as C-D-F and then
as D-E-G to complete the first phrase. The second phrase starts with the
second motif with jumps of -1 and -2, written as D-C-A. This repeats as
C-B-G and then B-A-F (note letters wrap around from G to A) to complete
the second phrase. The output below shows more notes because doubleNote
creates louder and quieter notes in pairs at different pitches.
In []:PrintssetPitch(B3) result = song(0.3, 1, 2, -1, -2) printTrack() print("Highest pitch was:", round(result, 3)) playTrack()
at 0s a 0.3s keyboard note at B3 (60% vol) at 0s a 0.3s keyboard note at D4 (18% vol) at 0.3s a 0.3s keyboard note at C4 (60% vol) at 0.3s a 0.3s keyboard note at E4 (18% vol) at 0.6s a 0.3s keyboard note at E4 (60% vol) at 0.6s a 0.3s keyboard note at G4 (18% vol) at 0.9s a 0.3s keyboard note at C4 (60% vol) at 0.9s a 0.3s keyboard note at E4 (18% vol) at 1.2s a 0.3s keyboard note at D4 (60% vol) at 1.2s a 0.3s keyboard note at F4 (18% vol) at 1.5s a 0.3s keyboard note at F4 (60% vol) at 1.5s a 0.3s keyboard note at A4 (18% vol) at 1.8s a 0.3s keyboard note at D4 (60% vol) at 1.8s a 0.3s keyboard note at F4 (18% vol) at 2.1s a 0.3s keyboard note at E4 (60% vol) at 2.1s a 0.3s keyboard note at G4 (18% vol) at 2.4s a 0.3s keyboard note at G4 (60% vol) at 2.4s a 0.3s keyboard note at B4 (18% vol) at 2.7s a 0.3s keyboard note at D4 (60% vol) at 2.7s a 0.3s keyboard note at F4 (18% vol) at 3s a 0.3s keyboard note at C4 (60% vol) at 3s a 0.3s keyboard note at E4 (18% vol) at 3.3s a 0.3s keyboard note at A3 (60% vol) at 3.3s a 0.3s keyboard note at C4 (18% vol) at 3.6s a 0.3s keyboard note at C4 (60% vol) at 3.6s a 0.3s keyboard note at E4 (18% vol) at 3.9s a 0.3s keyboard note at B3 (60% vol) at 3.9s a 0.3s keyboard note at D4 (18% vol) at 4.2s a 0.3s keyboard note at G3 (60% vol) at 4.2s a 0.3s keyboard note at B3 (18% vol) at 4.5s a 0.3s keyboard note at B3 (60% vol) at 4.5s a 0.3s keyboard note at D4 (18% vol) at 4.8s a 0.3s keyboard note at A3 (60% vol) at 4.8s a 0.3s keyboard note at C4 (18% vol) at 5.1s a 0.3s keyboard note at F3 (60% vol) at 5.1s a 0.3s keyboard note at A3 (18% vol) Highest pitch was: 493.895Audio
=
or by defining a parameter for a function) you must also later use that variable as part of another expression. If you need to create a variable that you won't use, it must have the name _
, but you should only do this if absolutely necessary.motif
must return the correct result
motif
function is run must match the solution result.motif
must produce the correct note sequence
motif
is called must match the solution notes in terms of timing, instruments, pitches, and volumes.motif
does not change the pitch.
motif
, setting the pitch beforehand to different values and recording the pitch afterwards. The final pitch must match the original pitch we specified.phrase
must return the correct result
phrase
function is run must match the solution result.phrase
must produce the correct note sequence
phrase
is called must match the solution notes in terms of timing, instruments, pitches, and volumes.phrase
changes the pitch correctly.
phrase
, setting the pitch beforehand to different values and recording the pitch afterwards. The change in pitch caused by the function must match the change caused by our solution: it should increase the pitch by the specified interval twice using climbUp
.song
must return the correct result
song
function is run must match the solution result.song
must produce the correct note sequence
song
is called must match the solution notes in terms of timing, instruments, pitches, and volumes.song
does not change the pitch.
song
, setting the pitch beforehand to different values and recording the pitch afterwards. The final pitch must match the original pitch we specified.motif
with 3 parameters
def
to define motif
with 3 parametersclimbUp
motif
with 3 parameters, call climbUp
or climbDown
in exactly 3 places.max
motif
with 3 parameters, call max
in exactly one place.motif
with 3 parameters
def
to define motif
with 3 parametersdoubleNote
motif
with 3 parameters, call doubleNote
in exactly 3 places.addNote
motif
with 3 parameters, do not call addNote
.setPitch
motif
with 3 parameters, do not call setPitch
.climbUp
motif
with 3 parameters, call climbUp
or climbDown
in at least one place.max
motif
with 3 parameters, call max
in at least one place.return
statement
motif
with 3 parameters, use return _
in at least one place.phrase
with 4 parameters
def
to define phrase
with 4 parametersclimbUp
phrase
with 4 parameters, call climbUp
or climbDown
in exactly 2 places.max
phrase
with 4 parameters, call max
in exactly one place.phrase
with 4 parameters
def
to define phrase
with 4 parametersmotif
phrase
with 4 parameters, call motif
in exactly 3 places.addNote
phrase
with 4 parameters, do not call addNote
or doubleNote
.setPitch
phrase
with 4 parameters, do not call setPitch
.climbUp
phrase
with 4 parameters, call climbUp
or climbDown
in at least one place.max
phrase
with 4 parameters, call max
in at least one place.return
statement
phrase
with 4 parameters, use return _
in at least one place.song
with 5 parameters
def
to define song
with 5 parametersmax
song
with 5 parameters, call max
in exactly one place.song
with 5 parameters
def
to define song
with 5 parametersaddNote
song
with 5 parameters, do not call addNote
or doubleNote
.setPitch
song
with 5 parameters, do not call setPitch
.phrase
song
with 5 parameters, call phrase
in exactly 2 places.max
song
with 5 parameters, call max
in at least one place.return
statement
song
with 5 parameters, use return _
in at least one place.