LMICSE: Lego Mindstorms in Computer Science Education

Site Map | Contact Us
Project Overview | Staff | Grant Information
Short Workshops | Primary Workshops
CS 1 | Data Str. & Algo. | Prog. Languages | Architecture | Intelligent Sys. | Operating Sys. | Net-centric
Ada | C | C++ | Java | Lisp

Lab 5: Classes

small logo

Designing Classes

In this lab we will construct a Note class and then use that class to have the Turtle play a simple tune. The goal of the lab is to practice defining classes and to think about designing programming solutions using classes.

A class is a description of a kind of object. You can use "new" to create one of those objects (called an instance of the class), and you can invoke methods on that object that you defined in your class. Thus, a class will almost always have method definitions so that someone who makes instances of the class can do things with those objects. Also, each instance of a class almost always has data (usually called instance variables) that are unique to that instance and define that object's state.

Prerequisite Knowledge

This lab requires

  • the basic understanding of the Turtle robot and its methods
  • knowledge of method creation and implementation
  • knowledge of the general structure of a class definition
  • how to construct a basic if statement

Materials

Standard RCX Brick (the rest of the Turtle is not required nor will its presence hinder this lab).

New Turtle Methods

playNote: You know that the Turtle can beep, but you may not know that it can play music. So

Turtle.playNote(pitchIndex,centiseconds); 

will have the Turtle play a note with a given pitch index (defined in the next section) for the specified number of centiseconds (100's of a second).

sleep: The sleep method cause the Turtle to stop accepting commands for a designated amount of time. So

Turtle.sleep(milliseconds);

will have the Turtle stop accepting commands for the specified number of milliseconds (1000's of a second). If the Turtle's motors were running, they will continue to run during this time.

The Note Class

The Turtle can play a note (a given pitch and duration) and so we can build up simple melodies. We'll define a Java class called Note to represent individual notes.

Designing a class often starts with its interface: the set of methods it supports. What do we want of our Note class?

  • construct a note, with pitch, duration and the Turtle which would play it
  • have the Turtle play a note

Later tasks in this lab will add additional constructors and methods, but this will get us started.

Remember that if we want to tell the Turtle to do something, we must import the Turtle class.

Representing a note's pitch: the pitch of a note could be defined just by its frequency. Many of us have heard of A440, which is the note A above the note middle C on the piano. Orchestras usually tune so that A is a frequency of about 440 Hertz (cycles per second). Given that, do you know what pitch is the B a whole step above A440? How about the A# (A-sharp) that is between them? Unless you've had some training in music theory and the mathematics of music, it can be hard to determine these things. So, working with frequencies is hard.

The Turtle uses a simpler representation for pitch. The various pitches are numbered, with A440 being note number 40. Each half step is numbered consecutively, so the A# is note 41, the B is note 42 and so forth. There are 12 half-steps in an octave, so the octave below A440 is note 28, and that makes middle C note 31. The following picture shows the numbering scheme relative to layout of keys on a keyboard:

Representing a note's duration: Let's use a representation which maps well to the usual way of specifying duration: whole notes, half notes, quarter notes, and so on. Use the float type, with a value of 1.0f being a whole note, 0.5f a half note, 0.25f a quarter note, and likewise for even shorter durations.

So we need three instance variables in our class definition, let's call them pitchIndex, duration, and t (for Turtle).

Representing tempo: But how long does a note last? We can use a constant to represent the length of a whole note in centiseconds like this:

final int wholeNoteTime = 64;

You might think that 64 is too long or too short, in which case you should certainly change it; I chose 64 only because it's easily divisible into eighth notes, sixteenth notes and so on.

The methods: We're now ready to decide on the signatures of our methods. We need to decide (1) their names, (2) their arguments, and (3) their return values. The constructor is easy, because its name is the same as the name of the class, which we call Note. It must have two arguments, the pitch-index, and the duration. Finally, because it's a constructor, we don't have to specify an return type for it. Thus, we get:

public Note(int pitchIndex, int duration);

For the other method, we could simply call it "play," and it doesn't need any arguments and it doesn't need to return any values, so it's easy to define it as:

public void play();

That's it for now - a class variable, three instance variables, a constructor, and a method. A UML (Unified Modeling Language) diagram for this class summarizes the essentials:

Task One: Three Blind Mice

Implement the Note class and then write a main class (call it Task1) that creates three notes to play Three Blind Mice (any descending sequence of three whole steps will do). Remember that whole steps go down by two, not one: for example, 32 to 30 is a whole step. The first three notes will all have the same duration. These notes then repeat.

Here's a model for your main class, which uses methods to initialize the notes and to play the song:

class Task1 {
    static Note c, d, e;

    ...
    public static void initNotes () {
        c = new Note(31, 0.5f);
        d = new Note(33, 0.5f);
        e = new Note(35, 0.5f);
        ...
    }

    public static void playSong () {
        e.play();
        d.play();
        c.play();
        ... 
     }

    public static void main (String [] args) {
        ...

    }
}

Once you have the first six notes, handle the next four notes, the first of which is seven half-steps above the lowest. Notice that the next two notes (the 8th and 9th overall) are quicker; about half the time of the first ones.

Construct a "junior" version of the song, which has the first three notes repeated twice, followed by the middle four notes repeated twice, followed by the first three notes repeated twice again. How could you use some additional methods to make the programming of the song simpler?

Task Two: Rests

The song doesn't sound quite right, because we've left out the pauses, which musicians call "rests." There is a rest at the end of the first three notes that is equal in length to the notes. We could put the pauses in the main method, but we'd like to work towards a class that helps us with musical notation, so let's implement rests in our Note class. (You might be bothered that a rest is not a note, and be wondering why it is in our Note class. This is a valid point, and "Note" now seems like a misnomer. Perhaps the class should be renamed "MusicalNotationElement," but for brevity we will call it "Note." Think of a rest as a note played really, really softly.)

How should we construct a rest? It has duration but not pitch, so we can simply add another constructor that has a signature of only one float argument.

How can we implement a rest? When we play a note, the play method needs to be able to determine whether the note is a rest or not. There are several ways to do this, but one way is to add a boolean instance variable that records whether the note is a rest or not: all the constructor methods need to record a value, unless you define the instance variable to have a default value. Then, when we play a rest note, the method just needs to wait ( call Turtle.sleep() instead of calling Turtle.playNote() ). Use an if statement to accomplish this decision.

Task two is to implement rests and have the Turtle play Three Blind Mice with appropriate rests.

Task Three: Class Variables

It would be nice if we could change the tempo of the song from the main program. That way we could play the song, then play it again at a faster tempo. Let's change the constant wholeNoteTime into a class variable. A class variable is like an instance variable, in that it is accessible in every instance, but instead of each instance having its own variable, that all share the same one. You can think of this variable as living in the class to which all the instances belong. Assuming we decide to call our variable wholeNoteTime, we can declare a class variable by using the static keyword, as follows:

static int wholeNoteTime = 64;

Then create static accessor and mutator methods which can be used to access or change the value of wholeNoteTime.

Have your program play the song three times, each time faster than the last. Start with the default tempo, and then increase it by 1.5 times each time it plays again.

Additional Problems

  1. Finish the rest of the Three Blind Mice song.
  2. Teach your Turtle to a play one or more songs of your choice.

Conclusion

In this lab, you learned the following:

  • How to implement classes
  • What instance variables and class variables are
  • How to use the Turtle's playNote() and sleep() methods