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: