Playing Scales

scale.py: 16 points


A scale is a sequence of notes, defined by the intervals between them. For example, the major scale is defined by the 7 intervals (and hence 8 notes) (2,2,1,2,2,2,1), that is, there are 2 half tones between the first and second notes, between the second and third notes, but a single half tone between the third and fourth notes, and so on. The “C Major Scale” is the sequence of notes starting at C and continuing to D, E, F, G, A, B, C as determined by the major scale intervals. The D major scale is the major scale starting at D and continuing to E, F#, G, A, B, C#, and D. The A major scale is the sequence of notes A, B, C#, D, E, F#, G#, A.

There are many other interesting scales, such as the minor scale, defined by the intervals (2,1,2,2,1,2,2), and the blues scale, defined by the intervals (3,2,1,1,3,2) (the scale only contains 7 notes).

Scales on the Command Line

For this part of the lab, you should write a program scale.py that will create a WAV file with a scale specified by command-line arguments. In particular, we’d like to be able to run:

python scale.py -3 M

to create an A major scale,

python scale.py 0 N

to create a C minor scale, or

python scale.py 4 B

to create a blues scale in E.

In particular, you will pass as command-line arguments the tonic note (in its half-tone offset from middle C) and which scale to play (as a character: M for major, N for minor, and B for blues).

How can your program make use of the arguments you add after the program name? If you add:

import sys

at the beginning of your program, you’ll get access to the variable sys.argv, which is a list of the arguments passed to Python from the shell. The first of these is always the name of the program itself. But if you were to run:

python scale.py 4 B

and that program included the statement print(sys.argv), we’d get as output:

['scale.py', '4', 'B']

Given this (and possibly judicious use of the int() function), you should be able to get all the input you need from command-line arguments.

Program Requirements

To make use of your SoundWave class, include:

import soundwave

at the top of your file.

The bulk of this file should be a main() function which will first gracefully handle invalid input from the user by catching exceptions, reporting the error, and quitting the program.

Closing a Program

Previously, when you’ve wanted to quit a program due to faulty user input, you might have wrapped your code in an if statement or used an empty return within your main() function. Now that we know about the sys module, we can instead use the command exit as follows, which will quit the program entirely.

sys.exit(-1)

You should then also declare a dictionary intervals, similar to that below:

intervals = {'M':[2,2,1,2,2,2,1],
             'N':[2,1,2,2,1,2,2],
             'B':[3,2,1,1,3,2]}

Each value list corresponds to the number of half tones between successive notes in the Major, Minor, and Blues scales respectively. This should make building scales cleaner.

Next, you should create a new SoundWave object at the half tone chosen by the user in the command-line arguments. Then, use a for loop to create another SoundWave for the next note in the scale (using the intervals corresponding to the type of scale the user desires) and call extend() to add this new note to the first one you created. By the end of the for loop, you should have the entire scale in a single SoundWave object. Finally, call the save method on that SoundWave object and pass in “scale.wav” as the name of the WAV file to be created.

After running your scale.py program, you should be able to click on the scale.wav file and play it to hear the scale you created (similar to the other parts of the lab). If you encounter a 0 second long scale, remind yourself about the duration parameter to the constructor for SoundWave.