Question

CSC 142

Music Player

You will complete this project by implementing one class. Afterwards, your program will play music from a text file.

Objectives

  • Working with lists

Background

This project addresses playing music. A song consists of notes, each of which has a length (duration) and pitch. The pitch of a note is described with a letter ranging from A to G.   7 notes is not enough to play very interesting music, so there are multiple octaves; after we reach note G we start over at A. Each set of 7 notes is considered one octave. Notes may also be accidentals. We normally notate this by calling them sharp, flat, or natural. Music also has silences that are called rests.

For this program we will be representing notes using scientific pitch notation. This style of notation represents each note as a letter and a number specifying the octave it belongs to. For example, middle C is represented as C4. You do not need to understand any more than this about scientific pitch notation, but you can read more about it here:  http://en.wikipedia.org/wiki/Scientific_pitch_notation

You will write a Melody class that uses a list to represent a song comprised of a series of notes. It may have repeated sections; since we don't like redundancy, we will only store one copy of a repeated chunk of notes. Your Melody class will read files in a format described below and represent the song's notes using an ArrayList of Note objects.  

Starter Code

Here is a link to the starter cod http://facweb.northseattle.edu/bgoldner/csc142/HW/HW8-player/MusicPlayerStarter.zip There's a lot here, but only 1 file you need to edit.

Once you unzip this file, you'll have a folder that contains 4 sub-folders:

  • bin
  • lib
  • res
  • src

Turn the src directory into a BlueJ project. In this folder, you'll find 3 Java files:

  • Main.java - this is the class that contains the main method to run the program.
  • MelodyGui.java
  • Melody.java  

You need to implement Melody.java.

Input File Format

Music is usually printed like the example sheet music at right. The notes are a series of dots. Their position in relation to the lines determines their pitch and their tops, among other things, determine their length. Since it would be difficult for us to read input in this style, we will read input from a text file in a specific format.

An example input file is shown at right. The first two lines contain the song title and song artist, respectively.   Each subsequent line represents a single note in the following format:

             duration pitch octave accidental repeat

The first number on each line describes the duration of the note in seconds. The next letter describes the pitch of the note, using the standard letters A-G or R for a rest. The third token is the octave that the note is in. The fourth is the note's accidental value of sharp, flat, or natural. (For a rest, the octave and accidental are omitted.)  

The final token indicates whether the note is the start or stop of a repeated section: true if so, and false otherwise. In the example at right, notes 3-5 and 9-12 (lines 5-7 and 11-14) represent two repeated sections. The meaning of the data is that the song should play notes 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 9, 10, 11, 12, 13. Our format does not allow nested repetition, nor sections that repeat more than twice.
Hot Crossed Buns Hot crOssed buns Hot crossed buns One pey, two pe y Hot crossed buis.

Example file (line numbers added)

1|     My Song Title
2|     Felix the Cat
3|     0.2 C 4 NATURAL false
4|     0.4 F 4 NATURAL false
5|     0.2 F 4 NATURAL true
6|     0.4 G 4 NATURAL false
7|     0.2 A 4 NATURAL true
8|     0.2 A 4 NATURAL false
9|     0.4 R false
10|    0.2 B 4 NATURAL false
11|    0.2 C 4 NATURAL true
12|    0.4 D 4 NATURAL false
13|    0.2 C 5 NATURAL false
14|    0.2 A 4 NATURAL true
15|    0.4 D 4 NATURAL false

Note class (provided):

You are given a supplier class named Note that your Melody class will use. A Note object represents a single musical note that will form part of a melody. It keeps track of the length (duration) of the note in seconds as a double, the note's pitch (A-G, or R if the note is a rest), the octave as an int, and the accidental (sharp, natural or flat). Each Note object also uses a boolean field to keep track of whether it is the first or last note of a repeated section of the melody.  

The Note uses two types of enums named Pitch and Accidental.
•    A Pitch is a constant from Pitch.A through Pitch.G or Pitch.R, meaning the frequency of the note.
•    An Accidental indicates whether a note is sharp, flat, or neither using the constants Accidental.SHARP, Accidental.FLAT, and Accidental.NATURAL respectively.

Here is a link to the JavaDocs for Note, Accidental, and Pitch. If you're interested, you can find the source files in the folder src/melody/audio. However, you are clients of these classes only.

Specification

You are to implement the Melody class according to the following specification:


class Melody

You decide on instance variables. The only requirement is to use an ArrayList to hold the series of Notes
+ Melody(File file) throws FileNotFoundException   

Load this class with the data from the file given. There is a method in each of the enum defintions to convert from a String to the correct constant. The constructor may assume that the given file is formatted correctly


+ void changeTempo(double ratio)

Scale (multiply) the duration of each Note in this Melody by the given ratio. For example, passing a ratio of 1.0 will do nothing, while a ratio of 2.0 will make each note's duration twice as long (slow down the song), or a ratio of 0.5 will make each note half as long (speed up the song).  Throw an IllegalArgumentException for <= 0.


+ String getArtist() Return this Melody's artist

+ String getTitle() Return this Melody's title

+ double getTotalDuration()

Return this Melody's total duration (length) in seconds. In general this is equal to the sum of the durations of the song's notes, but if some sections of the song are repeated, those parts count twice toward the total. For example, a song whose notes' durations add up to 6 seconds that has a 1.5-second repeated section and a 1-second repeated section has a total duration of (6.0 + 1.5 + 1.0) = 8.5 seconds.


+ boolean octaveDown()

Modify the state of each Note in this Melody so that they are all exactly 1 octave lower in pitch than their current state. For example, a C note in octave 4 would become a C note in octave 3. Rests are not affected by this method, and the Notes' state is otherwise unchanged other than the octaves.

Octave Note.OCTAVE_MIN is the lowest possible octave allowed by our system. If any note(s) in your song are already at this octave, then the entire octaveDown call should do nothing; the call should have no effect on the state of this Melody.

Return true if this method lowered the octave, and false if it was the above special case.


+ boolean octaveUp()

Modify the state of each Note in this Melody so that they are all exactly 1 octave higher in pitch than their current state. For example, a C note in octave 4 would become a C note in octave 5. Rests are not affected by this method, and the Notes' state is otherwise unchanged other than the octaves.

Note.OCTAVE_MAX is the highest possible octave allowed by our system. If any note(s) in your song are already at this octave, then the entire octaveUp call should do nothing; the call should have no effect on the state of this Melody.

Return true if this method raised the octave, and false if it was the above special case.


+ void play()

Play this Melody so that it can be heard on the computer's speakers. Essentially this consists of calling the play method on each Note. The notes should be played from the beginning of the list to the end, unless there are notes that are marked as being part of a repeated section. If a series of notes represents a repeated section, that sequence is played twice.

This method should not modify the state of this Melody. Also, it should be possible to call play multiple times and get the same result each time.


+ void reverse()

Reverse the order of the Notes in this Melody, so that future calls to play would play the notes in the opposite of the order they were in before the call.   Two calls to reverse() would put the Notes back in their original order. Do not use any additional data structure; design your algorithm to work within the space of the existing list. You must write the reversal code yourself; do not use an existing Java library reversal code.


+ String toString()  

Return a String representation of this Melody. It should include the title, artist, and all the Notes, each separated by \n.

Documentation & Style

Use good documentation, style, and design techniques that we've discussed throughout the quarter.

Suggestions

To start, implement Melody's constructor and toString. Then you'll be able to load the file using the provided GUI and it should display the Melody content in the terminal window.

Grading

/15 execution and design
/5 documentation, style

Good Luck!

0 0
Add a comment Improve this question Transcribed image text
Answer #1

Main.java

/*
* Melody Player
*
* This file contains the main method to run the program.
*/

import melody.gui.*;

public class Main {
   /*
   * Runs the program.
   */
   public static void main(String[] args) {
       new MelodyGUI();
   }
}


Melody.java

// TODO: write this class

import java.util.*;
import java.io.*;
import melody.audio.*;

public class Melody {
   private String title;
   private String artist;
   private int numNote;
   private ArrayList<Note> noteArr;
   private int octaveAdjust = 0;
   private double tempoRatio = 1.0;

   public Melody(File file) throws IOException {
       BufferedReader f = new BufferedReader(new FileReader(file));
       title = f.readLine();
       artist = f.readLine();
       StringTokenizer st = new StringTokenizer(f.readLine());
       numNote = Integer.parseInt(st.nextToken());
       noteArr = new ArrayList<Note>();
       for (int i=0; i < numNote; i++) {
           st = new StringTokenizer(f.readLine());
           double duration = Double.parseDouble(st.nextToken());
           Pitch pitch = Pitch.getValueOf(st.nextToken());
           int octave = Integer.parseInt(st.nextToken());
           Accidental accidental = Accidental.getValueOf(st.nextToken());
           boolean repeat = (st.nextToken().equals("true") ? true : false);
                          
           noteArr.add(new Note(duration, pitch, octave, accidental, repeat));
       }
       f.close();
   }
  
   public void changeTempo(double ratio) {
       tempoRatio = ratio;
   }
  
   public String getArtist() {
       return artist;
   }

   public String getTitle() {
       return title;
   }
  
   public double getTotalDuration() {
       double totalDuration = 0;
       for (Note n : noteArr) {
           totalDuration += n.getDuration();
       }
       return totalDuration;
   }
  
   public boolean octaveDown() {
       octaveAdjust--;
       return true;
   }
  
   public boolean octaveUp() {
       octaveAdjust++;
       System.out.println("octaveAdjust = " + octaveAdjust);
       return true;
   }
  
   public void play() {
       for (Note n : noteArr) {
           n.setOctave(n.getOctave() + octaveAdjust);
           n.setDuration(n.getDuration() / tempoRatio);
           n.play();
           n.setOctave(n.getOctave() - octaveAdjust);
           n.setDuration(n.getDuration() * tempoRatio);
       }
   }
  
   public void reverse() {
       Collections.reverse(noteArr);
   }
  
   public String toString() {
       // TODO: write this method
       return "";
   }
}


MelodyGUI.java

/*
* Melody Player
*
* This instructor-provided file implements the graphical user interface (GUI)
* for the Melody Player program and allows you to test the behavior
* of your Melody class.
*/

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import melody.audio.*;
import melody.gui.*;

public class MelodyGUI implements ActionListener, StdAudio.AudioEventListener {
   // constants for important directories used by the program
   private static final String RESOURCE_FOLDER = "res";
   private static final String ICONS_FOLDER = "icons";
   private static final String MELODY_FOLDER = "melodies";
   private static final String TITLE = "Melody Player";

   // fields
   private Melody melody;
   private boolean playing; // whether a song is currently playing
   private JFrame frame;
   private Container overallLayout;
   private JLabel statusLabel;
   private JButton playButton; // buttons in the GUI
   private JButton pauseButton;
   private JButton stopButton;
   private JButton loadButton;
   private JButton reverseButton;
   private JButton changeTempoButton;
   private JButton octaveUpButton;
   private JButton octaveDownButton;
   private JFileChooser fileChooser; // for loading files
   private JSlider currentTimeSlider; // displays current time in the song
   private JLabel currentTimeLabel;
   private JLabel totalTimeLabel;
   private JSpinner changeTempoSpinner; // numeric duration field

   /*
   * Creates the melody player GUI window and graphical components.
   */
   public MelodyGUI() {
       melody = null;
       createComponents();
       doLayout();
       StdAudio.addAudioEventListener(this);
       frame.setVisible(true);
   }

   /*
   * Called when the user interacts with graphical components, such as
   * clicking on a button.
   */
   public void actionPerformed(ActionEvent event) {
       String cmd = event.getActionCommand().intern();
       if (cmd == "Play") {
           playMelody();
       } else if (cmd == "Pause") {
           StdAudio.setPaused(!StdAudio.isPaused());
       } else if (cmd == "Stop") {
           StdAudio.setMute(true);
           StdAudio.setPaused(false);
       } else if (cmd == "Load") {
           try {
               loadFile();
           } catch (IOException ioe) {
               JOptionPane.showMessageDialog(frame, "I/O error: " + ioe.getMessage(), "I/O error",
                       JOptionPane.ERROR_MESSAGE);
           }
       } else if (cmd == "Reverse") {
           if (melody != null) {
               System.out.println("Reversing.");
               melody.reverse();
               System.out.println("Melody: " + melody);
           }
       } else if (cmd == "Up") {
           if (melody != null) {
               System.out.println("Octave up.");
               if (!melody.octaveUp()) {
                   JOptionPane.showMessageDialog(frame,
                           "Can't go up an octave; maximum octave reached.", "Warning",
                           JOptionPane.WARNING_MESSAGE);
               }
               System.out.println("Melody: " + melody);
           }
       } else if (cmd == "Down") {
           if (melody != null) {
               System.out.println("Octave down.");
               if (!melody.octaveDown()) {
                   JOptionPane.showMessageDialog(frame,
                           "Can't go down an octave; minimum octave reached.", "Warning",
                           JOptionPane.WARNING_MESSAGE);
               }
               System.out.println("Melody: " + melody);
           }
       } else if (cmd == "Change Tempo") {
           if (melody != null) {
               try {
                   double ratio = ((Double) changeTempoSpinner.getValue()).doubleValue();
                   System.out.println("Change tempo by " + ratio + ".");
                   melody.changeTempo(ratio);
                   updateTotalTime();
                   System.out.println("Melody: " + melody);
               } catch (NumberFormatException nfe) {
                   // empty
               }
           }
       }
   }

   /*
   * Called when audio events occur in the StdAudio library. We use this to
   * set the displayed current time in the slider.
   */
   public void onAudioEvent(StdAudio.AudioEvent event) {
       // update current time
       if (event.getType() == StdAudio.AudioEvent.Type.PLAY
               || event.getType() == StdAudio.AudioEvent.Type.STOP) {
           setCurrentTime(getCurrentTime() + event.getDuration());
       }
   }

   /*
   * Sets up the graphical components in the window and event listeners.
   */
   private void createComponents() {
       try {
           UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
       } catch (Exception e) {
           // empty
       }
       frame = new JFrame(TITLE);
       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       frame.setResizable(false);

       String currentDir = System.getProperty("user.dir") + File.separator + RESOURCE_FOLDER
               + File.separator + MELODY_FOLDER;
       if (!(new File(currentDir).exists())) {
           currentDir = System.getProperty("user.dir");
       }
       fileChooser = new JFileChooser(currentDir);
       fileChooser.setFileFilter(GuiUtils.getExtensionFileFilter("Text files (*.txt)", "txt"));
       statusLabel = new JLabel("Welcome to the Melody Player!");

       String icon = RESOURCE_FOLDER + File.separator + ICONS_FOLDER + File.separator;
       playButton = GuiUtils.createButton("Play", icon + "play.gif", 'P', this);
       pauseButton = GuiUtils.createButton("Pause", icon + "pause.gif", 'a', this);
       stopButton = GuiUtils.createButton("Stop", icon + "stop.gif", 'S', this);
       loadButton = GuiUtils.createButton("Load", icon + "load.gif", 'L', this);
       reverseButton = GuiUtils.createButton("Reverse", icon + "reverse.gif", 'R', this);
       octaveUpButton = GuiUtils.createButton("Up", icon + "up.gif", 'U', this);
       octaveDownButton = GuiUtils.createButton("Down", icon + "down.gif", 'D', this);
       changeTempoButton = GuiUtils.createButton("Change", icon + "lightning.gif", 'h', this);
       changeTempoButton.setActionCommand("Change Tempo");
       changeTempoSpinner = new JSpinner(new SpinnerNumberModel(1.0, 0.1, 9.9, 0.1));

       currentTimeSlider = new JSlider(/* min */0, /* max */100);
       currentTimeSlider.setValue(0);
       currentTimeSlider.setMajorTickSpacing(10);
       currentTimeSlider.setMinorTickSpacing(5);
       currentTimeSlider.setPaintLabels(false);
       currentTimeSlider.setPaintTicks(true);
       currentTimeSlider.setSnapToTicks(false);
       currentTimeSlider.setPreferredSize(new Dimension(300,
               currentTimeSlider.getPreferredSize().height));
       currentTimeLabel = new JLabel("000000.0 /");
       totalTimeLabel = new JLabel("000000.0 sec");

       JSpinner.NumberEditor editor = (JSpinner.NumberEditor) changeTempoSpinner.getEditor();
       DecimalFormat format = editor.getFormat();
       format.setMinimumFractionDigits(1);
       changeTempoSpinner.setValue(1.0);
       doEnabling();
   }

   /*
   * Sets whether every button, slider, spinner, etc. should be currently
   * enabled, based on the current state of whether a song has been loaded and
   * whether or not it is currently playing. This is done to prevent the user
   * from doing actions at inappropriate times such as clicking play while the
   * song is already playing, etc.
   */
   private void doEnabling() {
       playButton.setEnabled(melody != null && !playing);
       pauseButton.setEnabled(melody != null && playing);
       stopButton.setEnabled(melody != null && playing);
       loadButton.setEnabled(!playing);
       currentTimeSlider.setEnabled(false);
       reverseButton.setEnabled(melody != null && !playing);
       octaveUpButton.setEnabled(melody != null && !playing);
       octaveDownButton.setEnabled(melody != null && !playing);
       changeTempoButton.setEnabled(melody != null && !playing);
       changeTempoSpinner.setEnabled(melody != null && !playing);
   }

   /*
   * Performs layout of the components within the graphical window. Also
   * resizes the window snugly and centers it on the screen.
   */
   private void doLayout() {
       overallLayout = Box.createVerticalBox();
       ImageIcon logoIcon = new ImageIcon(RESOURCE_FOLDER
               + File.separator + ICONS_FOLDER + File.separator + "notes.gif");
       overallLayout.add(GuiUtils.createPanel(new JLabel(logoIcon)));
       overallLayout.add(GuiUtils.createPanel(statusLabel));
       overallLayout.add(GuiUtils.createPanel(currentTimeSlider,
               GuiUtils.createPanel(new GridLayout(2, 1), currentTimeLabel, totalTimeLabel)));
       overallLayout.add(GuiUtils.createPanel(playButton, stopButton, loadButton));
       overallLayout.add(GuiUtils.createPanel(reverseButton, octaveUpButton, octaveDownButton));
       overallLayout.add(GuiUtils.createPanel(new JLabel("Tempo: "), changeTempoSpinner,
               changeTempoButton));
       frame.setContentPane(overallLayout);
       frame.pack();
       GuiUtils.centerWindow(frame);
   }

   /*
   * Returns the estimated current time within the overall song, in seconds.
   */
   private double getCurrentTime() {
       String timeStr = currentTimeLabel.getText();
       timeStr = timeStr.replace(" /", "");
       try {
           return Double.parseDouble(timeStr);
       } catch (NumberFormatException nfe) {
           return 0.0;
       }
   }

   /*
   * Pops up a file-choosing window for the user to select a song file to be
   * loaded. If the user chooses a file, a Melody object is created and used
   * to represent that song.
   */
   private void loadFile() throws IOException {
       if (fileChooser.showOpenDialog(frame) != JFileChooser.APPROVE_OPTION) {
           return;
       }
       File selected = fileChooser.getSelectedFile();
       if (selected == null) {
           return;
       }
       statusLabel.setText("Current song: " + selected.getName());
       String filename = selected.getAbsolutePath();
       System.out.println("Loading melody from " + selected.getName() + " ...");
       melody = new Melody(new File(filename));
       changeTempoSpinner.setValue(1.0);
       setCurrentTime(0.0);
       updateTotalTime();
       System.out.println("Loading complete.");
       System.out.println("Melody: " + melody);
       doEnabling();
   }

   /*
   * Initiates the playing of the current song Melody in a separate thread (so
   * that it does not lock up the GUI).
   */
   private void playMelody() {
       if (melody != null) {
           setCurrentTime(0.0);
           Thread playThread = new Thread(new Runnable() {
               public void run() {
                   StdAudio.setMute(false);
                   playing = true;
                   doEnabling();
                   String title = melody.getTitle();
                   String artist = melody.getArtist();
                   double duration = melody.getTotalDuration();
                   System.out.println("Playing \"" + title
                           + "\", by " + artist + " (" + duration + " sec)");
                   melody.play();
                   System.out.println("Playing complete.");
                   playing = false;
                   doEnabling();
               }
           });
           playThread.start();
       }
   }

   /*
   * Sets the current time display slider/label to show the given time in
   * seconds. Bounded to the song's total duration as reported by Melody.
   */
   private void setCurrentTime(double time) {
       double total = melody.getTotalDuration();
       time = Math.max(0, Math.min(total, time));
       currentTimeLabel.setText(String.format("%08.2f /", time));
       currentTimeSlider.setValue((int) (100 * time / total));
   }

   /*
   * Updates the total time label on the screen to the current total duration.
   */
   private void updateTotalTime() {
       totalTimeLabel.setText(String.format("%08.2f sec", melody.getTotalDuration()));
   }
}


Accidental.java

/*
* Melody Player
*
* This instructor-provided file represents a musical accidental:
* sharp, flat, or natural.
*/

package melody.audio;

/**
* An Accidental represents a musical accidental: sharp, flat, or natural.
*/
public enum Accidental {  
   SHARP, NATURAL, FLAT;

   /**
   * Returns the Accidental that is equivalent to the given string,
   * such as Accidental.SHARP for "SHARP", or null if the string does not
   * match any Accidental value.
   */
   public static Accidental getValueOf(String s) {
       s = s.intern();
       if (s == "SHARP") {
           return Accidental.SHARP;
       }
       if (s == "FLAT") {
           return Accidental.FLAT;
       }
       if (s == "NATURAL") {
           return Accidental.NATURAL;
       }
       return null;
   }
}


Note.java

/*
* Melody Player
*
* This instructor-provided file represents musical notes and is to be used
* by your Melody class.
*
*/

package melody.audio;

/**
* Each Note object represents a musical note or rest.
* A Note encapsulates a pitch (A-G), a duration in seconds, an octave,
* an accidental (sharp, flat, or natural), and a flag of whether it is the
* start/end of a repeated section or not.
* A song or melody can be thought of as a list or array of Note objects.
*/
public class Note {
   /**
   * Constant for the minimum legal value that an octave can have.
   */
   public static final int OCTAVE_MIN = 1;

   /**
   * Constant for the maximum legal value that an octave can have.
   */
   public static final int OCTAVE_MAX = 10;
  
   /*
   * Whether this class should print messages to the console for debugging.
   */
   public static boolean DEBUG = true;
  
   // fields
   private double duration;         // note's duration in seconds
   private Pitch pitch;             // note's pitch from A-G or R for rest
   private int octave;              // note's octave from 1-10
   private Accidental accidental;   // note's accidental: sharp, flat, natural
   private boolean repeat;          // true if this note starts/ends a repeated section

   /**
   * Constructs a Note with the given information.
   * @param duration Note's duration in seconds.
   * @param pitch Note's pitch from Pitch.A through Pitch.G, or Pitch.R for a rest.
   * @param octave Note's octave from OCTAVE_MIN through OCTAVE_MAX inclusive.
   * @param accidental Note's accidental from Accidental.SHARP, FLAT, or NATURAL.
   * @param repeat true if this note starts/ends a repeated section.
   * @throws NullPointerException if pitch or accidental is null.
   * @throws IllegalArgumentException if duration is negative or octave is not
   *                                  between OCTAVE_MIN and OCTAVE_MAX inclusive.
   */
   public Note(double duration, Pitch pitch, int octave, Accidental accidental, boolean repeat) {
       setPitch(pitch);
       setAccidental(accidental);
       setDuration(duration);
       setOctave(octave);
       this.repeat = repeat;
   }

   /**
   * Constructs a new rest (Pitch.R) of the given duration.
   * @param duration Note's duration in seconds.
   * @param repeat true if this rest starts/ends a repeated section.
   * @throws NullPointerException if accidental is null.
   * @throws IllegalArgumentException if duration is negative.
   */
   public Note(double duration, boolean repeat) {
       this(duration, Pitch.R, OCTAVE_MIN + 1, Accidental.NATURAL, repeat);
   }
  
   /**
   * Returns true if o refers to a Note object with the same state
   * as this Note object; otherwise false.
   * @param o the object to compare against
   */
   public boolean equals(Object o) {
       if (o instanceof Note) {
           Note other = (Note) o;
           if (pitch == Pitch.R && other.pitch == Pitch.R) {
               // compare two rests
               return this.duration == other.duration
                       && this.pitch == other.pitch
                       && this.repeat == other.repeat;
           } else {
               // compare two notes, or a note and a rest
               return this.duration == other.duration
                       && this.pitch == other.pitch
                       && this.octave == other.octave
                       && this.accidental == other.accidental
                       && this.repeat == other.repeat;
           }
       } else {
           return false;
       }
   }
  
   /**
   * Returns this Note's accidental value of SHARP, FLAT, or NATURAL.
   * The accidental value is meaningless for a rest; this method will
   * return Accidental.NATURAL by default if called on a rest.
   */
   public Accidental getAccidental() {
       return accidental;
   }

   /**
   * Returns this Note's duration in seconds.
   */
   public double getDuration() {
       return duration;
   }

   /**
   * Returns this Note's octave.
   * The octave value is meaningless for a rest; this method will return
   * OCTAVE_MIN + 1 by default if called on a rest.
   */
   public int getOctave() {
       return octave;
   }

   /**
   * Returns this Note's pitch value of A-G or R for a rest.
   */
   public Pitch getPitch() {
       return pitch;
   }

   /**
   * Returns true if this Note is the start or end of a repeated section.
   */
   public boolean isRepeat() {
       return repeat;
   }

   /**
   * Returns true if this Note is a rest. Equivalent to checking whether
   * this note's pitch is Pitch.R. Provided for convenience.
   */
   public boolean isRest() {
       return pitch == Pitch.R;
   }

   /**
   * Plays this note through the underlying audio system.
   * Also prints a message to the system console for debugging.
   * If the audio system is muted or paused, the note may not play.
   */
   public void play() {
       if (DEBUG) {
           System.out.println(" - playing " + duration + " " + pitch
                   + (pitch == Pitch.R ? "" : (" " + octave + " " + accidental)));
       }
       if (pitch == Pitch.R) {
           // play no sound (but do delay) if the note is a rest
           // StdAudio.play(StdAudio.note(0, duration, 0.5), duration);
           StdAudio.play(this, StdAudio.note(0, duration, 0.5), duration);
       } else {
           char note = pitch.toString().charAt(0);
           int steps = (note - 'A') * 2;

           // adjust for sharps/flats
           if (note == 'C' || note == 'D' || note == 'E') {
               steps -= 1;
           } else if (note == 'F' || note == 'G') {
               steps -= 2;
           }

           // adjust pitch for proper octave
           if (octave > 4 || (octave == 4 && note <= 'B')) {
               steps += (octave - 4) * 12;
           } else {
               steps -= (4 - octave) * 12;
           }

           // octave start at C so A and B are an octave lower
           if (note != 'A' && note != 'B') {
               steps -= 12;
           }

           // adjust for sharps and flats
           if (accidental.equals(Accidental.SHARP)) {
               steps += 1;
           } else if (accidental.equals(Accidental.FLAT)) {
               steps -= 1;
           }

           // play the note!
           double hz = 440.0 * Math.pow(2, steps / 12.0);
           StdAudio.play(this, StdAudio.note(hz, duration, 0.5), duration);
       }
   }

   /**
   * Sets this Note's accidental value to be the given value: SHARP, FLAT, or NATURAL.
   * The accidental value is meaningless for a rest, but the Note object still
   * maintains an accidental value internally (initially Accidental.NATURAL)
   * which is ignored.
   * @param accidental Note's accidental from Accidental.SHARP, FLAT, or NATURAL.
   * @throws NullPointerException if the accidental is null.
   */
   public void setAccidental(Accidental accidental) {
       if (accidental == null) {
           throw new NullPointerException();
       }
       if (pitch != Pitch.R) {
           this.accidental = accidental;
       }
   }

   /**
   * Sets this Note's duration in seconds to be the given value.
   * @param duration Note's duration in seconds.
   * @throws IllegalArgumentException if duration is negative.
   */
   public void setDuration(double duration) {
       if (duration < 0.0) {
           throw new IllegalArgumentException();
       }
       this.duration = duration;
   }

   /**
   * Sets this Note's octave to be the given value.
   * The octave value is meaningless for a rest, but the Note object still
   * maintains an octave value internally (initially OCTAVE_MIN + 1)
   * which is ignored.
   * @param octave Note's octave from OCTAVE_MIN through OCTAVE_MAX inclusive.
   * @throws IllegalArgumentException if octave is not between OCTAVE_MIN
   *                                  and OCTAVE_MAX inclusive.
   */
   public void setOctave(int octave) {
       if (octave < OCTAVE_MIN || octave > OCTAVE_MAX) {
           throw new IllegalArgumentException("Illegal octave value: " + octave);
       }
       if (pitch != Pitch.R) {
           this.octave = octave;
       }
   }

   /**
   * Sets this Note's pitch to be the given value.
   * @param pitch Note's pitch from Pitch.A through Pitch.G, or Pitch.R for a rest.
   * @throws NullPointerException if pitch is null.
   */
   public void setPitch(Pitch pitch) {
       if (pitch == null) {
           throw new NullPointerException();
       }
       this.pitch = pitch;
   }

   /**
   * Sets this Note's repeat flag to be the given value.
   * @param pitch true to indicate that this note is the start/end of a
   *              repeated section, or false if not.
   */
   public void setRepeat(boolean repeat) {
       this.repeat = repeat;
   }

   /**
   * Returns a string representation of this note.
   * @return A string such as "0.4 C 5 NATURAL false".
   */
   public String toString() {
       if (pitch == Pitch.R) {
           return duration + " " + pitch + " " + repeat;
       } else {
           return duration + " " + pitch + " " + octave + " " + accidental + " " + repeat;
       }
   }
}

Pitch.java

/*
* Melody Player
*
* This instructor-provided file represents a musical pitch from A-G or rest.
*/

package melody.audio;

/**
* A Pitch represents a musical pitch. R represents a rest, no pitch.
*/
public enum Pitch {
   A, B, C, D, E, F, G, R;
  
   /**
   * Returns the Pitch that is equivalent to the given string,
   * such as Pitch.D for "D", or null if the string does not
   * match any Pitch value.
   */
   public static Pitch getValueOf(String s) {
       s = s.intern();
       if (s == "A") {
           return Pitch.A;
       }
       if (s == "B") {
           return Pitch.B;
       }
       if (s == "C") {
           return Pitch.C;
       }
       if (s == "D") {
           return Pitch.D;
       }
       if (s == "E") {
           return Pitch.E;
       }
       if (s == "F") {
           return Pitch.F;
       }
       if (s == "G") {
           return Pitch.G;
       }
       if (s == "R") {
           return Pitch.R;
       }
       return null;
   }
}

Add a comment
Know the answer?
Add Answer to:
CSC 142 Music Player You will complete this project by implementing one class. Afterwards, your program...
Your Answer:

Post as a guest

Your Name:

What's your source?

Earn Coins

Coins can be redeemed for fabulous gifts.

Not the answer you're looking for? Ask your own homework help question. Our experts will answer your question WITHIN MINUTES for Free.
Similar Homework Help Questions
  • cpp Lab Quiz 2 Time Slot: Mon 1-2 PM Deadline: 05/13/2019 2:00 PM Submission: Email Subject: CSC 102 CC3/CC4 LabQuiz 2TS 2 FileType: cpp Write a program to define a music player. Music player has...

    cpp Lab Quiz 2 Time Slot: Mon 1-2 PM Deadline: 05/13/2019 2:00 PM Submission: Email Subject: CSC 102 CC3/CC4 LabQuiz 2TS 2 FileType: cpp Write a program to define a music player. Music player has these features: isPlaying, isStopped, currentSongTitle, nextSong Title, currentSongLength. Programmer can play or stop a song. You can also choose to skip to the next song. Songs are stored in array of strings. When you choose to move to the next song, currentSong Title and nextSongTitle...

  • Please write this program in C++, thanks Task 9.3 Write a complete C++ program to create a music player Your program sh...

    Please write this program in C++, thanks Task 9.3 Write a complete C++ program to create a music player Your program should read in several album names, each album has up to 5 tracks as well as a genre. First declare genre for the album as an enumeration with at least three entries. Then declare an album structure that has five elements to hold the album name, genre, number of tracks, name of those tracks and track location. You can...

  • PYTHON PROGRAMMING NEED HELP ASAP You will write an application to manage music collections -- a music collection is a l...

    PYTHON PROGRAMMING NEED HELP ASAP You will write an application to manage music collections -- a music collection is a list of albums. The named tuples used for Albums and Songs are defined below, and an example of a music collection is given. (DO NOT HARDCODE THE VALUES FOR MUSIC!) Album = namedtuple('Album', 'id artist title year songs') Song = namedtuple('Song', 'track title length play_count') MUSIC = [ Album("1", "Peter Gabriel", "Up", 2002, [Song(1, "Darkness", 411, 5), Song(2, "Growing Up",...

  • Many of us have large digital music collections that are not always very well organized. It...

    Many of us have large digital music collections that are not always very well organized. It would be nice to have a program that would manipulate our music collection based on attributes such as artist, album title, song title, genre, song length, number times played, and rating. For this assignment you will write a basic digital music manager (DMM). Your DMM program must have a text-based interface which allows the user to select from a main menu of options including:...

  • Introduction Welcome to Rad.io, you've been hired to work on our music streaming app, think of...

    Introduction Welcome to Rad.io, you've been hired to work on our music streaming app, think of it as Spotify only more rad! You're in charge of handling our customer’s song list. When a user selects a playlist it will load into the list a number of songs. Users can skip to the next song, move to the previous, they can select a song to play next or select a song to add to the end of their list. Objective You...

  • Write and submit one complete Java program to solve the following requirements. Your program will employ...

    Write and submit one complete Java program to solve the following requirements. Your program will employ packages (that is, source directories), and contain multiple source files. Because you are using packages, your code should be in a directory named “Greenhouse.” You should be able to compile your code using the command “javac Greenhouse\*.java” from a directory just below the Greenhouse directory. In the program for this assignment, class names have been specified. You mustuse the supplied class name for both...

  • The task is to write Song.cpp to complete the implementation of the Song class, as defined...

    The task is to write Song.cpp to complete the implementation of the Song class, as defined in the provided header file, Song.h, and then to complete a program (called sales) that makes use of the class. As in Project 1, an input data file will be provided containing the song name and other information in the same format as in Program 1: Artist    Song_Title    Year    Sales    Medium As before, the program (sales) which are individual copies, will use this information to compute the gross...

  • ise 1. Create a new project named lab6 1. You will be implementing an IceCream class. For the class attributes, let&#39...

    ise 1. Create a new project named lab6 1. You will be implementing an IceCream class. For the class attributes, let's stick to flavor, number ofscoops, and price (in cents). Note that the price per scoop of any flavor is 99 cents. Therefore, allow the flavor and scoops to be set directly, but not the price! The price should be set automatically based on the number of scoops entered. Some other requirements i. A default constructor and a constructor with...

  • (C++) Please create a tic tac toe game. Must write a Player class to store each...

    (C++) Please create a tic tac toe game. Must write a Player class to store each players’ information (name and score) and to update their information (increase their score if appropriate) and to retrieve their information (name and score).The players must be called by name during the game. The turns alternate starting with player 1 in the first move of round 1. Whoever plays last in a round will play second in the next round (assuming there is one).The rows...

  • For this assignment you will be creating a multi-file project in which you implement your own...

    For this assignment you will be creating a multi-file project in which you implement your own templated linked list and use it to create a simple list of composers. When doing this assignment, take small, incremental steps; trying to complete the lab in one go will make the lab more difficult. This means that any time you finish part of the lab, such as a linked list method, you should immediately test and debug it if necessary. Part 1: Creating...

ADVERTISEMENT
Free Homework Help App
Download From Google Play
Scan Your Homework
to Get Instant Free Answers
Need Online Homework Help?
Ask a Question
Get Answers For Free
Most questions answered within 3 hours.
ADVERTISEMENT
ADVERTISEMENT
ADVERTISEMENT