5. Object Oriented Programming


Download project ZIP | Submit to TigerFile

In this assignment, you will write programs that use and create data types. You will use Picture and Color to process images and In to read input from files.

Getting Started

  • Read Sections 3.1 and 3.2 of the textbook and review the corresponding lectures and precepts.
  • Download and unzip oop.zip , then open the oop project in IntelliJ.
  • Consult the Picture, Color, and In APIs as needed.

Partnering

This is a partner assignment. Partnering is optional, but we strongly recommend doing at least one partnered assignment. Pair programming improves how you communicate about code and is often faster, less error-prone, and less frustrating than working alone.

  • See Ed for instructions on finding a parter and creating a TigerFile group.
  • Follow these partnering rules.

Rectangular Tile of an Image

A rectangular tile repeats an image in a grid with a specified number of columns and rows. For example, consider the following image:

1-by-1 heart

Here is a 6-by-3 rectangular tile:

6-by-3 heart

Write a program Tile.java that takes three command-line arguments (the image filename, the number of columns, and the number of rows) and displays the corresponding rectangular tile.

Organize your program according to the following API:

public class Tile {

    // Returns a cols-by-rows tiling of the given image.
    public static Picture tile(Picture picture, int cols, int rows)

    // Takes three command-line arguments (the image filename, the number
    // of columns, and the number of rows), and displays a rectangular
    // tiling of the image with the given number of columns and rows.
    public static void main(String[] args)
}

Here are some sample executions:

~/Desktop/oop> java-introcs Tile heart.png 1 1
1-by-1 heart

~/Desktop/oop> java-introcs Tile heart.png 6 3
6-by-3 heart

~/Desktop/oop> java-introcs Tile princeton.png 1 1
1-by-1 Princeton

~/Desktop/oop> java-introcs Tile princeton.png 5 2
5-by-2 Princeton

~/Desktop/oop> java-introcs Tile bricks.png 1 1
1-by-1 bricks

~/Desktop/oop> java-introcs Tile bricks.png 9 4
9-by-4 bricks

Tracker

In this part, you will create a data type for a tracking device, such as an AirTag or Tile Pro. Each tracking device has a name and a location. A device may also be in lost mode, which conserves power and limits access to personal information.

Implement Tracker.java as specified by the following API:

public class Tracker {

    // Creates a tracker with the given name and initial location.
    // Lost mode is initially disabled.
    public Tracker(String name, double initialLatitude, double initialLongitude)

    // Is this tracker in lost mode?
    public boolean isInLostMode()

    // Enables lost mode on this tracker.
    public void enableLostMode()

    // Disables lost mode on this tracker.
    public void disableLostMode()

    // Moves this tracker to the given location.
    public void move(double newLatitude, double newLongitude)

    // Returns the great-circle distance between the two trackers.
    public double distanceTo(Tracker other)

    // Returns a string representation of this tracker.
    public String toString()

    // Unit tests the Tracker data type.
    public static void main(String[] args)

}

Here is some more information about the required behavior:

  • Test client. The main() method must call every public instance method.

  • String representation. Return a string consisting of the tracker name and its current location (latitude, longitude) in the following format:

    "backpack (40.34386, -74.6593)"

  • Corner cases. You may assume that all arguments are meaningful values (e.g., not null, NaN, Double.POSITIVE_INFINITY, or Double.NEGATIVE_INFINITY).

  • Locations. Locations are specified using latitude and longitude in degrees. For example, Nassau Hall is located at $40.34386^\circ$ latitude and $-74.6593^\circ$ longitude.

  • Distance. Let $(x_1, y_1)$ and $(x_2, y_2)$ be the latitude and longitude of two points on the surface of a sphere of radius $r$. The great-circle distance between them is the length of the shortest path along the surface.

    great-circle distance between two points

    The Haversine formula gives the great-circle distance:

    $$ distance \; = \; 2r \arcsin \left ( \sqrt{ \sin^2 \left ( \frac{x_2 - x_1}{2} \right ) + \cos x_1 \cos x_2 \sin^2 \left ( \frac{y_2 - y_1}{2} \right ) } \right ) $$

    Use the mean Earth radius $r = \text{6,371.0}$ kilometers, which gives a good approximation.

Q.The arguments are given in degrees but Java’s trig functions use radians. What can I do?
A.
Use code like radians = Math.toRadians(degrees) to convert from degrees to radians.
Q.Which Math library functions should I use?
A.
Use whichever functions from Java’s Math library that you need.
Q.I’m getting the wrong result. How can I check my formula?
A.

Create a small example that you can work out by hand. Then verify that each intermediate term matches.

great circle trace
Q.Can I use a different formula to compute the great-circle distance?
A.
No. Use the formula provided. Different formulas can produce slightly different results due to floating-point rounding, especially for nearly antipodal points.

Musical Notes

Musical notes are the building blocks of music. In this assignment, each note is specified by its MIDI number and an instrument.

Implement Note.java as specified by the following API:

public class Note {

    // Creates a note with the given MIDI number and instrument name.
    public Note(int midiNumber, String instrumentName)

    // Returns this note's MIDI number.
    public int midi()

    // Returns this note's frequency.
    public double frequency()

    // Plays this note to standard audio using the associated WAV file.
    public void play()

    // Returns this note's name (e.g., C or A#).
    public String name()

    // Returns this note's octave.
    public int octave()

    // Returns a new Note transposed by delta semitones.
    public Note transpose(int delta)

    // Returns a string representation of this note.
    public String toString()

    // Unit tests the Note data type.
    public static void main(String[] args)

}

Here is additional information about the required behavior:

  • Corner cases. You may assume that all arguments are meaningful values (e.g., MIDI numbers are between 0 and 127).

  • Note names and octaves. Musicians often use scientific pitch notation, such as A4 for concert A (MIDI number 69) and C4 for middle C (MIDI number 60). Scientific pitch notation consists of a note name (C, C#, D, D#, E, F, F#, G, G#, A, A#, or B) followed by an octave number. Each octave contains 12 notes. The following (partial) table shows the mapping between the two notations:

    MIDI pitch MIDI pitch MIDI pitch MIDI pitch MIDI pitch
    36 C2 48 C3 60 C4 72 C5 84 C6
    37 C#2 49 C#3 61 C#4 73 C#5 85 C#6
    38 D2 50 D3 62 D4 74 D5 86 D6
    39 D#2 51 D#3 63 D#4 75 D#5 87 D#6
    40 E2 52 E3 64 E4 76 E5 88 E6
    41 F2 53 F3 65 F4 77 F5 89 F6
    42 F#2 54 F#3 66 F#4 78 F#5 90 F#6
    43 G2 55 G3 67 G4 79 G5 91 G6
    44 G#2 56 G#3 68 G#4 80 G#5 92 G#6
    45 A2 57 A3 69 A4 81 A5 93 A6
    46 A#2 58 A#3 70 A#4 82 A#5 94 A#6
    47 B2 59 B3 71 B4 83 B5 95 B6

    For the sake of brevity, we use only sharp accidentals (such as A#) for the note names, and not the equivalent flat accidentals such as (B♭).

  • String representation. Return a string consisting of the MIDI number, the note name, the octave, and the instrument in the following format: "69 A4 (piano)".

  • Play. Play the WAV file whose filename is the instrument name followed by the MIDI number, with extension .wav. For example, for instrument piano and MIDI number 69, call StdAudio.play('piano/piano69.wav).

  • Frequency. The frequency $f$ corresponding to MIDI number $m$ is

$$ f \; = \; 440 \; \times \; 2^{\, (m - 69) \, / \, 12} $$

  • Transpose. Transposing a note with MIDI number $m$ by $\Delta$ semitones yields a note with MIDI number $m + \Delta$. This method must return a new Note object; it must not modify the invoking Note.

  • Test client. The main() method must call every public method.

Q.How can I define constants that are global to a class?
A.
Recall from the Creating Data Types lecture that you can declare a constant using the private static final modifiers. For example: private static final int MINUTES_PER_HOUR = 60;
Q.Which Math library functions should I use?
A.
Use whichever functions from Java’s Math library that you need. The function ``Math.pow()` is particularly useful for computing frequency.
Q.MIDI values go from 0 to 127, however the table provided only shows the relationship between MIDI and octaves from 36 to 95. Should we only focus on those MIDI values? Or are we expected to generalize the relationship for the other values as well?
A.
You need to generalize. See the Table of note frequencies in Scientific pitch notation.
Q.There are only instrument audio files for MIDI numbers 12 through 116. Are our programs purposefully not intended to play MIDI numbers 0 through 11 and 117 through 127?
A.
While MIDI is defined for values 0 to 127, most instruments do not support that full range. For example, a typical piano has only 88 keys, not 128. You need not worry about that. The autograder won’t call play() on a MIDI note for which there is no accompanying WAV file.
Q.Do I need conditionals to determine a note name or octave?
A.
You can implement name() and octave() without any if statements. If you a have large multiway if statement ladder, you are probably not finding the most elegant way to implement these methods. Consider using integer division/remainder and an array.

Play These Notes

Your final task is to write a client program PlayTheseNotes.java that reads a sequence of notes from a file, transposes them by a given $\Delta$, plays them on standard audio, and prints the transposed notes to standard output.

The program takes three command-line arguments:

  • the name of the input file
  • the instrument name: piano, guitar, clarinet, or mbira
  • the transposed amount $\Delta$ (in semitones)

Organize your client according to the following API:

public class PlayTheseNotes {

    // Reads a sequence of notes from filename and returns them 
    // as an array, using the given instrument.
    public static Note[] read(String filename, String instrument)

    // Takes three command-line arguments (a filename, an instrument name,
    // and a transposition amount delta), reads the notes; transposes them;
    // plays them on standard audio; and prints the transposed notes.
    public static void main(String[] args)
}

Here are some details about the API:

  • Approach. In main(), first call read() to obtain an array of Note objects for the specified instrument. Then, in order, transpose each note by $\Delta$, play it, and print it.

  • Input file format. A notes file consists of a sequence of lines:

    • The first line contains the number of notes.
    • Each remaining line contains a MIDI number followed by a floating-point duration (in seconds), separated by whitespace.
    input file format
  • Input files. We provide several files containing simple songs in this format. You may also create and submit your own song in a file named music.txt. It may be original or by another artist; include credits at the end of the file.

  • Reading from a file. The In data type (Section 3.1) is an object-oriented version of StdIn. Use In in read() since you will be reading from a file.

  • Duration. Duration is typically used to represent note lengths (eighth notes, quarter notes, half notes, and whole notes). In this assignment, we provide only quarter notes. You must read the duration, but you will not use it when creating or playing notes.

  • Output. Print the transposed pitch (note name and octave) for each note as it plays. Print all pitches on one line, separated by single spaces, and end the line with a newline.

Here are some sample executions:

~/Desktop/oop> java-introcs PlayTheseNotes C4-major-scale.txt piano 0
C4 D4 E4 F4 G4 A4 B4 C5

~/Desktop/oop> java-introcs PlayTheseNotes C4-major-scale.txt guitar 0
C4 D4 E4 F4 G4 A4 B4 C5

~/Desktop/oop> java-introcs PlayTheseNotes C4-major-scale.txt piano 8
G#4 A#4 C5 C#5 D#5 F5 G5 G#5 

Submission

Upload all files to TigerFile :

  • Tile.java
  • Tracker.java
  • Note.java
  • PlayTheseNotes.java
  • readme.txt
  • acknowledgments.txt
  • music.txt (if you created your own composition)

Grading Breakdown

File Points
Tile.java 10
Tracker.java 10
Note.java 10
PlayTheseNotes.java 6
readme.txt 4
Total 40

This assignment was developed by Kevin Wayne. Copyright © 2024-2026.