Instructions for scales

(produced at 11:40 a.m. UTC on 2021-10-31)

This task is part of ps06 which is due at 23:59 EDT on 2021-11-02.

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 submit this task using this link.

Put all of your work for this task into the file scales.py
(you will create this file from scratch)

For this problem set, you have a choice: you must do the memory diagram and word search tasks, but you only need to do one of the circles or scales tasks. The circles task is graphics-oriented and works with turtle graphics, while the scales task (this task) is based on audio and music instead.

For this task, you will use for loops with the wavesynth.py library to create musical scales: sequences of notes with ascending pitches.

Background: wavesynth.py

wavesynth.py is a library for creating (simple) music just using Python functions. The dance faster task from problem set 3 has an explanation of how to use wavesynth.py which you may refer to if you want to understand the module thoroughly. There is also a reference page which explains more about how to use it and includes documentation on all of the functions in the module.

For this task, you will only need the addNote, halfStepUp, and stepUp functions. The basic instructions are: call stepUp or stepDown when you want to change the current pitch (think of this like changing the current color in turtle) and then call addNote with a duration value to add a note at the current pitch to the current track. When you call addNote it advances the time value, so that the next note you add will begin when the previous note ended, just like how in turtle each drawing command also moves the turtle.

For testing, you will also need to use at least one of printTrack, saveTrack, and/or playTrack. To use playTrack, you will have to install the simpleaudio module (in the Thonny "Tools" menu select "Manage Packages" and then search for simpleaudio and click "Install.")

Overview

For this task, you will create a new Python file named scales.py. Your file should begin with this header, which you should fill out (note that as usual you must document each function you create):

"""
Authors:
Consulted:
Date:
Purpose: scales task: create runs of ascending notes.
"""

You will needs to import wavesynth by putting the following line at the beginning of your code:

from wavesynth import *

You will use functions from wavesynth.py to modify pitch values in order to construct sequences of notes with rising pitches. There are four parts to this task:

  • In Part A you will create a variable-length scale that uses whole steps.

  • In Part B you will create a variable-length scale using whole steps in which every other note is half as long as the others.

  • In Part C you will create a variable-length arpeggio, in which there are a different number of half-steps between subsequent notes, specified using a list of integers.

Testing

In this task, you will create multiple functions, and you should test each of them. We've included a copy of optimism in the starter code which could be used along with printTrack and expectOuptutContains for automatic testing, although you could also test things manually if you prefer. Either way, it's best to put your testing code into its own function called test. You may copy paste this example test function which just includes functions calls to be manually verified as a starting point:

def test():
    """
    Tests the scales functions.
    """
    resetTracks()

    # Uncomment one of these for testing:

    # setPitch(C4)
    # scale(0.75, 3)

    # setPitch(E2)
    # unevenScale(1.44, 8)

    # setPitch(C4)
    # arpeggio(0.25, [3, 2], 3)

    # Shows the notes that have been added to the track:
    printTrack()

    # Tries to play the track (Note: you'll need to install simpleaudio
    # to get this to actually play sound):
    playTrack()

    # You could also add a call like this to save the track to a file:
    # saveTrack('test.wav')

Remember that you can use this free difference checker website to check printed output exactly against our examples.

Part A: scale

The scale function accepts two parameters: the first controls the total duration of the scale, and the second controls the number of notes in the scale. It must add an ascending sequence of notes to the current track, starting at the current pitch, and increasing by one full step (using the stepUp function) with each node added. The provided duration for the whole scale must be evenly divided among the notes in the scale. So for example, if the scale duration is 1 second and there are 4 notes, each note's duration will be 0.25 seconds.

These scale examples demonstrate how it should work.

Part B: unevenScale

The unevenScale function must work just like scale and takes the exact same parameters, with one difference: Instead of creating notes that all have the same duration, every other note in the scale sequence is half as long. The top note of the scale is always a long note, which means that depending on the number of notes, the first note may be either long or short.

To compute the length of each short note from the total duration, you should first compute the total number of short note durations in the scale, which will be the number of short notes plus twice the number of long notes, and then divide the total duration by that number. You can then get the duration of each long note by doubling your short note duration. Integer division is useful for figuring out the number of short and/or long notes in the scale, based on the total number of notes.

As an example, if the total duration is 4 seconds and there are 5 notes in the scale, since the last note is a long one, the 1st, 3rd, and 5th notes are long, while the 2nd and 4th are short. With 2 short notes and 3 long ones, the total number of short durations is 2 + 6 = 8, so the short duration value is 4 / 8 = 0.5 seconds. Accordingly, the long duration must be twice as long, or 1 second. We can verify the two 0.5-second short notes (1 second) plus 3 1-second long notes (3 seconds) adds up to the required duration of 4 seconds.

These unevenScale examples demonstrate how it should work.

Part C: arpeggio

For this last part, you'll create a more flexible function that creates ascending note sequences with variable pitch differences between the notes. The arpeggio function must accept 3 arguments: the first specifies the note duration, the second is a list of pitch differences (integers), and the third specifies the number of cycles, which indicates how many times the pattern of pitch differences should repeat.

arpeggio will add an ascending sequence of notes where the first note starts at the current pitch, the next note is a number of half-steps above that pitch specified by the first value from the list of pitch differences, the third note is a number of half-steps above that pitch specified by the second pitch-difference-list value, and so on. The steps in the pitch-differences list will be repeated as specified by the cycle count (which will always be a positive integer). The total number of notes will be one plus the cycle count times the length of the pitch-differences list.

As shown in the arpeggio examples, if the pitch differences are [3, 4, 5] and the cycle count is 2, the following notes will be added (specified in terms of half-steps above the starting pitch):

+0, +3, +7, +12, +15, +19, +24

Note that the total number of notes in this case is 1 + (2 * 3) = 7.

Notes:

  • You must use a nested loop in this function, and you may not use list-multiplication to simply repeat the pitch-differences list, even though that might be a simpler solution.
  • You will use the halfStepUp function to change pitches; note that it accepts a parameter that determines the number of steps up to take, and that it will accept negative numbers to step down, so you can simply pass the pitch difference values directly to halfStepUp to change pitches.
  • Unlike the previous two parts, the first parameter to arpeggio directly determines the length of each note it adds, so you don't need to compute a note length based on a total duration.

Bonus: scalesDesign

This extra challenge is not part of the rubric and does not factor into your grade.

Now that you've created several functions for several different kinds of note sequences, we encourage you to create some more complicated pattern using these functions. This example demonstrates one such pattern that could be created, using a loop and the rewind function to create multiple overlapping scales.

Examples

scale examples

These examples show what should be printed and what audio should be produced when scale is called, followed by printTrack and playTrack. Note that the exact pitches used depend on the current pitch when the function is called.

In []:
setPitch(C4) scale(0.75, 3) printTrack() playTrack()
Prints
a 0.25s keyboard note at C4 (60% vol) and a 0.25s keyboard note at D4 (60% vol) and a 0.25s keyboard note at E4 (60% vol)
Audio In []:
setPitch(B2) scale(0.64, 8) printTrack() playTrack()
Prints
a 0.08s keyboard note at B2 (60% vol) and a 0.08s keyboard note at C3 (60% vol) and a 0.08s keyboard note at D3 (60% vol) and a 0.08s keyboard note at E3 (60% vol) and a 0.08s keyboard note at F3 (60% vol) and a 0.08s keyboard note at G3 (60% vol) and a 0.08s keyboard note at A3 (60% vol) and a 0.08s keyboard note at B3 (60% vol)
Audio

unevenScale examples

These examples show what should be printed and what audio should be produced when unevenScale is called, followed by printTrack and playTrack. Note that the exact pitches used depend on the current pitch when the function is called.

In []:
setPitch(C4) unevenScale(0.75, 3) printTrack() playTrack()
Prints
a 0.3s keyboard note at C4 (60% vol) and a 0.15s keyboard note at D4 (60% vol) and a 0.3s keyboard note at E4 (60% vol)
Audio In []:
setPitch(E2) unevenScale(1.44, 8) printTrack() playTrack()
Prints
a 0.12s keyboard note at E2 (60% vol) and a 0.24s keyboard note at F2 (60% vol) and a 0.12s keyboard note at G2 (60% vol) and a 0.24s keyboard note at A2 (60% vol) and a 0.12s keyboard note at B2 (60% vol) and a 0.24s keyboard note at C3 (60% vol) and a 0.12s keyboard note at D3 (60% vol) and a 0.24s keyboard note at E3 (60% vol)
Audio

arpeggio examples

These examples show what should be printed and what audio should be produced when arpeggio is called, followed by printTrack and playTrack. Note that the exact pitches used depend on the current pitch when the function is called.

In []:
setPitch(C4) arpeggio(0.25, [2, 3], 3) printTrack() playTrack()
Prints
a 0.25s keyboard note at C4 (60% vol) and a 0.25s keyboard note at D4 (60% vol) and a 0.25s keyboard note at F4 (60% vol) and a 0.25s keyboard note at G4 (60% vol) and a 0.25s keyboard note at Bb4 (60% vol) and a 0.25s keyboard note at C5 (60% vol) and a 0.25s keyboard note at Eb5 (60% vol)
Audio