CSC 142
Music Player
You will complete this project by implementing one class. Afterwards, your program will play music from a text file.
Objectives
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:
Turn the src directory into a BlueJ project. In this folder, you'll find 3 Java files:
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. |
|
Example file (line numbers added) |
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. + 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. + 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. + 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!
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;
}
}
CSC 142 Music Player You will complete this project by implementing one class. Afterwards, your program...
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 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 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 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 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 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 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'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 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 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...