Encoding songs as data
In lab you used classes to create a piano program. An app class that represents the program itself, and a simple piano class that represents the ability to play notes.
In this assignment, you'll be adding another class, recorder, which allows you to:
Start with your working Part III code from the lab.
You will submit through blackboard:
Turning in code that runs counts for 50 points. The write-up counts for 25 points. Readable code (variable and function names, reasonable whitespace, docstrings) counts for 25 points.
Being able to play music is cool. But there's something much more striking about having a machine play back what you just did. You're going to do that be creating a recorder class.
In object-oriented terms, think of the recorder class as a separate box that connects to the piano. Every time you hit a key, if a recorder is connected, then it somehow stores that key that you hit. Then, at a later time, it somehow plays backs all those keys.
We'll start by stubbing out the interface for the recorder. That is, the methods that we think the class will need. Start with this:
"""
recorder class with stub methods. Note that "pass" must be used for empty function bodies, will be removed when you put real code in.
"""
class recorder:
def __init__(self):
"""
Default constructor, going to need some way to store notes.
"""
self.notes = []
def record_key(self, key_index):
"""
Method to record that a particular key (by index) was struck.
"""
pass
def playback(self, piano):
"""
Method to play back the stored notes using the given piano.
"""
pass
Now, you can connect up this stubbed app to see how they can work together. Follow these steps:
app constructor and set it to the value None (None is used to indicate the absence of an object)begin_recording method in the app classbegin_recording method print to the console so you can verify that your button works.begin_recording method create a new recorder object and assign it to the data member from step (1)key_down method so that if there is a recorder object (if the data member is not set to None), then it calls the record_key method on the record_key method to print to the console. Now if you play notes you shouldn't see anything, but if you press the "Record" button and then play notes, you should see the printing from record_key.That's the kind of process by which you incrementally create and test code. The design in this case is already mostly set by the lab work you did and the recorder class I gave you, so it's just a matter of making things work together. Here are some next steps that aren't quite as detailed for you.
record_key so that it stores the notes being played, instead of just printing.playback method on the recorder so that it prints the stored notes to the console.Many programs deal with time and timing. In this case, we need to know not just what notes were played, but when.
Like every programming environment, Python has a way to ask what time it is. The relevant function is:
import time now = time.time()
That time comes back as the number of seconds since midnight on Jan 1, 1970, an arbitrary Epoch chosen for Unix systems to track time. What's important is that by calling time.time() multiple times, it allows you to calculate relative time, such as the number of seconds since you started recording.
recorder constructor to store the time that it was created - that is, the time the recording starts.record_key method to store not just the notes played, but also the time since the start of the song that they were played. How do you store both? You could use parallel arrays, another class, or a list-of-lists setup.playback method, as necessary, to be able to print out the stored notes and times so that you can see if record_key is working or not.And finally, update the playback method to actually playback the song. Remember that you have the key indeces and times stored inside the recorder, and we passed in the piano object to use to play the notes. Think about how you would play a song back given a list of notes and times, a stopwatch and a piano.
Add buttons to your interface that allow the user to write the current recorded song to a file, and read that file in for playback. You can just have a single, hardcoded filename, you don't have to support multiple files.
Test Case: