first commit
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,65 @@
|
||||
#BlueJ class context
|
||||
comment0.params=filename
|
||||
comment0.target=MusicFilePlayer(java.lang.String)
|
||||
comment0.text=\n\ Creates\ a\ new\ MusicFilePlayer\ instance.\n\ @param\ filename\ The\ file\ to\ be\ played.\n
|
||||
comment1.params=
|
||||
comment1.target=void\ play()
|
||||
comment1.text=\n\ Play\ the\ whole\ file.\n
|
||||
comment10.params=
|
||||
comment10.target=int\ getFrameNumber()
|
||||
comment10.text=\n\ Return\ the\ current\ frame\ number.\n\ @return\ The\ number\ of\ the\ last\ frame\ played,\ or\ -1\ if\ nothing\ played\ yet.\n
|
||||
comment11.params=start\ end
|
||||
comment11.target=boolean\ playFrames(int,\ int)
|
||||
comment11.text=\n\ Play\ the\ number\ of\ frames\ left.\n\ @return\ true\ If\ finished\ for\ any\ reason,\ false\ if\ paused.\n
|
||||
comment12.params=position
|
||||
comment12.target=void\ moveTo(int)
|
||||
comment12.text=\n\ Set\ the\ playing\ position.\n\ @param\ position\ (in\ frames)\n
|
||||
comment13.params=
|
||||
comment13.target=void\ close()
|
||||
comment13.text=\n\ Cloases\ this\ player.\ Any\ audio\ currently\ playing\ is\ stopped\n\ immediately.\n
|
||||
comment14.params=
|
||||
comment14.target=boolean\ decodeFrame()
|
||||
comment14.text=\n\ Decodes\ a\ single\ frame.\n\n\ @return\ true\ if\ there\ are\ no\ more\ frames\ to\ decode,\ false\ otherwise.\n
|
||||
comment15.params=
|
||||
comment15.target=boolean\ skipFrame()
|
||||
comment15.text=\n\ skips\ over\ a\ single\ frame\n\ @return\ false\ \ \ \ if\ there\ are\ no\ more\ frames\ to\ decode,\ true\ otherwise.\n
|
||||
comment16.params=
|
||||
comment16.target=void\ stop()
|
||||
comment16.text=\n\ closes\ the\ player\ and\ notifies\ <code>PlaybackListener</code>\n
|
||||
comment17.params=filename
|
||||
comment17.target=int\ getFrameCount(java.lang.String)
|
||||
comment17.text=\n\ Count\ the\ number\ of\ frames\ in\ the\ file.\n\ This\ can\ be\ used\ for\ positioning.\n\ @param\ filename\ The\ file\ to\ be\ measured.\n\ @return\ The\ number\ of\ frames.\n
|
||||
comment18.params=
|
||||
comment18.target=javazoom.jl.decoder.Header\ readFrame()
|
||||
comment18.text=\n\ Read\ a\ frame.\n\ @return\ The\ frame\ read.\n
|
||||
comment19.params=
|
||||
comment19.target=void\ openAudio()
|
||||
comment19.text=\n\ Open\ an\ audio\ device.\n
|
||||
comment2.params=frames
|
||||
comment2.target=boolean\ play(int)
|
||||
comment2.text=\n\ Plays\ a\ number\ of\ MPEG\ audio\ frames.\n\n\ @param\ frames\ \ \ \ The\ number\ of\ frames\ to\ play.\n\ @return\ \ true\ if\ the\ last\ frame\ was\ played,\ or\ false\ if\ there\ are\n\ \ \ \ \ \ \ \ \ \ more\ frames.\n
|
||||
comment20.params=filename
|
||||
comment20.target=void\ openBitstream(java.lang.String)
|
||||
comment20.text=\n\ Open\ a\ BitStream\ for\ the\ given\ file.\n\ @param\ filename\ The\ file\ to\ be\ opened.\n\ @throws\ IOException\ If\ the\ file\ cannot\ be\ opened.\n
|
||||
comment3.params=start\ end
|
||||
comment3.target=boolean\ play(int,\ int)
|
||||
comment3.text=\n\ Plays\ a\ range\ of\ MPEG\ audio\ frames\n\ @param\ start\ The\ first\ frame\ to\ play\n\ @param\ end\ \ \ \ \ \ \ The\ last\ frame\ to\ play\n\ @return\ true\ if\ the\ last\ frame\ was\ played,\ or\ false\ if\ there\ are\ more\ frames.\n
|
||||
comment4.params=start
|
||||
comment4.target=boolean\ playFrom(int)
|
||||
comment4.text=\n\ Play\ from\ the\ given\ position\ to\ the\ end.\n\ @param\ start\ The\ first\ frame\ to\ play.\n\ @return\ true\ if\ the\ last\ frame\ was\ played,\ or\ false\ if\ there\ are\ more\ frames.\n
|
||||
comment5.params=
|
||||
comment5.target=int\ getLength()
|
||||
comment5.text=\n\ Get\ the\ length\ of\ the\ file\ (in\ frames).\n\ @return\ The\ file\ length,\ in\ frames.\n
|
||||
comment6.params=
|
||||
comment6.target=int\ getPosition()
|
||||
comment6.text=\n\ Get\ the\ current\ playing\ position\ (in\ frames).\n\ @return\ The\ current\ frame\ number.\n
|
||||
comment7.params=position
|
||||
comment7.target=void\ setPosition(int)
|
||||
comment7.text=\n\ Set\ the\ playing\ position\ (in\ frames).\n\ Playing\ does\ not\ start\ until\ resume()\ is\ called.\n\ @param\ position\ The\ playing\ position.\n
|
||||
comment8.params=
|
||||
comment8.target=void\ pause()
|
||||
comment8.text=\n\ Pause\ the\ playing.\n
|
||||
comment9.params=
|
||||
comment9.target=void\ resume()
|
||||
comment9.text=\n\ Resume\ the\ playing.\n
|
||||
numComments=21
|
@@ -0,0 +1,413 @@
|
||||
/**
|
||||
* 11/19/04 1.0 moved to LGPL.
|
||||
*-----------------------------------------------------------------------
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Library General Public License as published
|
||||
* by the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*----------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import javazoom.jl.decoder.Bitstream;
|
||||
import javazoom.jl.decoder.BitstreamException;
|
||||
import javazoom.jl.decoder.Decoder;
|
||||
import javazoom.jl.decoder.Header;
|
||||
import javazoom.jl.decoder.JavaLayerException;
|
||||
import javazoom.jl.decoder.SampleBuffer;
|
||||
import javazoom.jl.player.AudioDevice;
|
||||
import javazoom.jl.player.FactoryRegistry;
|
||||
|
||||
/**
|
||||
* Play music files.
|
||||
* This class is a modified version of javazoom.jl.player.advanced.AdvancedPlayer,
|
||||
* which is part of the javazoom JLayer library.
|
||||
* The main modifications consist of:
|
||||
* + Restriction to playing files rather than streams.
|
||||
* + Pre-reading of the audio file to determine its length in frames.
|
||||
* These modifications permit arbitrary seek operations.
|
||||
*
|
||||
* Modifications by David J. Barnes and Michael Kölling.
|
||||
* @version 2016.02.29
|
||||
* This class is not suitable for playing streams as a file is read
|
||||
* completely before playing.
|
||||
*/
|
||||
public class MusicFilePlayer
|
||||
{
|
||||
// The MPEG audio bitstream.
|
||||
private Bitstream bitstream;
|
||||
// The MPEG audio decoder.
|
||||
private Decoder decoder;
|
||||
// The AudioDevice the audio samples are written to.
|
||||
private AudioDevice audio;
|
||||
// Whether currently playing.
|
||||
private boolean playing = false;
|
||||
// The file being played.
|
||||
private String filename;
|
||||
|
||||
// The number of frames.
|
||||
private int frameCount;
|
||||
// The current frame number.
|
||||
private int frameNumber;
|
||||
// The position to resume, if any.
|
||||
private int resumePosition;
|
||||
|
||||
/**
|
||||
* Creates a new MusicFilePlayer instance.
|
||||
* @param filename The file to be played.
|
||||
*/
|
||||
public MusicFilePlayer(String filename) throws JavaLayerException
|
||||
{
|
||||
this.filename = filename;
|
||||
|
||||
openAudio();
|
||||
|
||||
frameCount = getFrameCount(filename);
|
||||
|
||||
// Open a fresh bitstream following the frame count.
|
||||
openBitstream(filename);
|
||||
|
||||
frameNumber = 0;
|
||||
resumePosition = -1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the whole file.
|
||||
*/
|
||||
public void play() throws JavaLayerException
|
||||
{
|
||||
playFrames(0, frameCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a number of MPEG audio frames.
|
||||
*
|
||||
* @param frames The number of frames to play.
|
||||
* @return true if the last frame was played, or false if there are
|
||||
* more frames.
|
||||
*/
|
||||
public boolean play(int frames) throws JavaLayerException
|
||||
{
|
||||
return playFrames(frameNumber, frameNumber + frames);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a range of MPEG audio frames
|
||||
* @param start The first frame to play
|
||||
* @param end The last frame to play
|
||||
* @return true if the last frame was played, or false if there are more frames.
|
||||
*/
|
||||
public boolean play(int start, int end) throws JavaLayerException
|
||||
{
|
||||
return playFrames(start, start + end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Play from the given position to the end.
|
||||
* @param start The first frame to play.
|
||||
* @return true if the last frame was played, or false if there are more frames.
|
||||
*/
|
||||
public boolean playFrom(int start) throws JavaLayerException
|
||||
{
|
||||
return playFrames(start, frameCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of the file (in frames).
|
||||
* @return The file length, in frames.
|
||||
*/
|
||||
public int getLength()
|
||||
{
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current playing position (in frames).
|
||||
* @return The current frame number.
|
||||
*/
|
||||
public int getPosition()
|
||||
{
|
||||
return frameNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the playing position (in frames).
|
||||
* Playing does not start until resume() is called.
|
||||
* @param position The playing position.
|
||||
*/
|
||||
public void setPosition(int position) throws JavaLayerException
|
||||
{
|
||||
pause();
|
||||
resumePosition = position;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Pause the playing.
|
||||
*/
|
||||
public void pause() throws JavaLayerException
|
||||
{
|
||||
synchronized(this) {
|
||||
playing = false;
|
||||
resumePosition = frameNumber;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume the playing.
|
||||
*/
|
||||
public void resume() throws JavaLayerException
|
||||
{
|
||||
if(!playing) {
|
||||
int start;
|
||||
if(resumePosition >= 0) {
|
||||
start = resumePosition;
|
||||
}
|
||||
else {
|
||||
start = frameNumber;
|
||||
}
|
||||
resumePosition = -1;
|
||||
playFrames(start, frameCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current frame number.
|
||||
* @return The number of the last frame played, or -1 if nothing played yet.
|
||||
*/
|
||||
public int getFrameNumber()
|
||||
{
|
||||
return frameNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the number of frames left.
|
||||
* @return true If finished for any reason, false if paused.
|
||||
*/
|
||||
private boolean playFrames(int start, int end) throws JavaLayerException
|
||||
{
|
||||
// Clear any resumption position.
|
||||
resumePosition = -1;
|
||||
|
||||
if(end > frameCount) {
|
||||
end = frameCount;
|
||||
}
|
||||
|
||||
// Make sure the player is in the correct position in the input.
|
||||
synchronized(this) {
|
||||
moveTo(start);
|
||||
playing = true;
|
||||
}
|
||||
|
||||
// Play until finished, paused, or a problem.
|
||||
boolean ok = true;
|
||||
while (frameNumber < end && playing && ok) {
|
||||
ok = decodeFrame();
|
||||
if(ok) {
|
||||
frameNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
// Stopped for some reason.
|
||||
synchronized(this) {
|
||||
playing = false;
|
||||
// last frame, ensure all data flushed to the audio device.
|
||||
AudioDevice out = audio;
|
||||
if (out != null) {
|
||||
out.flush();
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the playing position.
|
||||
* @param position (in frames)
|
||||
*/
|
||||
private void moveTo(int position) throws JavaLayerException
|
||||
{
|
||||
if(position < frameNumber) {
|
||||
synchronized(this) {
|
||||
// Already played too far.
|
||||
if(bitstream != null) {
|
||||
try {
|
||||
bitstream.close();
|
||||
}
|
||||
catch (BitstreamException ex) {
|
||||
}
|
||||
}
|
||||
if(audio != null) {
|
||||
audio.close();
|
||||
}
|
||||
openAudio();
|
||||
openBitstream(filename);
|
||||
frameNumber = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while(frameNumber < position) {
|
||||
skipFrame();
|
||||
frameNumber++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cloases this player. Any audio currently playing is stopped
|
||||
* immediately.
|
||||
*/
|
||||
public void close()
|
||||
{
|
||||
synchronized(this) {
|
||||
if (audio != null) {
|
||||
AudioDevice out = audio;
|
||||
audio = null;
|
||||
// this may fail, so ensure object state is set up before
|
||||
// calling this method.
|
||||
out.close();
|
||||
try {
|
||||
bitstream.close();
|
||||
}
|
||||
catch (BitstreamException ex) {
|
||||
}
|
||||
bitstream = null;
|
||||
decoder = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a single frame.
|
||||
*
|
||||
* @return true if there are no more frames to decode, false otherwise.
|
||||
*/
|
||||
protected boolean decodeFrame() throws JavaLayerException
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized (this) {
|
||||
if (audio == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Header h = readFrame();
|
||||
if (h == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// sample buffer set when decoder constructed
|
||||
SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream);
|
||||
|
||||
if(audio != null) {
|
||||
audio.write(output.getBuffer(), 0, output.getBufferLength());
|
||||
}
|
||||
}
|
||||
|
||||
bitstream.closeFrame();
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
ex.printStackTrace();
|
||||
throw new JavaLayerException("Exception decoding audio frame", ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* skips over a single frame
|
||||
* @return false if there are no more frames to decode, true otherwise.
|
||||
*/
|
||||
protected boolean skipFrame() throws JavaLayerException
|
||||
{
|
||||
Header h = readFrame();
|
||||
if (h == null) {
|
||||
return false;
|
||||
}
|
||||
frameNumber++;
|
||||
bitstream.closeFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* closes the player and notifies <code>PlaybackListener</code>
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of frames in the file.
|
||||
* This can be used for positioning.
|
||||
* @param filename The file to be measured.
|
||||
* @return The number of frames.
|
||||
*/
|
||||
protected int getFrameCount(String filename) throws JavaLayerException
|
||||
{
|
||||
openBitstream(filename);
|
||||
int count = 0;
|
||||
while(skipFrame()) {
|
||||
count++;
|
||||
}
|
||||
bitstream.close();
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a frame.
|
||||
* @return The frame read.
|
||||
*/
|
||||
protected Header readFrame() throws JavaLayerException
|
||||
{
|
||||
if(audio != null) {
|
||||
return bitstream.readFrame();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an audio device.
|
||||
*/
|
||||
protected void openAudio() throws JavaLayerException
|
||||
{
|
||||
audio = FactoryRegistry.systemRegistry().createAudioDevice();
|
||||
decoder = new Decoder();
|
||||
audio.open(decoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a BitStream for the given file.
|
||||
* @param filename The file to be opened.
|
||||
* @throws IOException If the file cannot be opened.
|
||||
*/
|
||||
protected void openBitstream(String filename)
|
||||
throws JavaLayerException
|
||||
{
|
||||
try {
|
||||
bitstream = new Bitstream(new BufferedInputStream(
|
||||
new FileInputStream(filename)));
|
||||
}
|
||||
catch(java.io.IOException ex) {
|
||||
throw new JavaLayerException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@@ -0,0 +1,42 @@
|
||||
#BlueJ class context
|
||||
comment0.params=folderName
|
||||
comment0.target=MusicOrganizer(java.lang.String)
|
||||
comment0.text=\n\ Create\ a\ MusicOrganizer.\n\ @param\ folderName\ The\ folder\ of\ audio\ files.\n
|
||||
comment1.params=filename
|
||||
comment1.target=void\ addFile(java.lang.String)
|
||||
comment1.text=\n\ Add\ a\ track\ file\ to\ the\ collection.\n\ @param\ filename\ The\ file\ name\ of\ the\ track\ to\ be\ added.\n
|
||||
comment10.params=t1\ t2
|
||||
comment10.target=int\ compare(Track,\ Track)
|
||||
comment11.params=index
|
||||
comment11.target=void\ removeTrack(int)
|
||||
comment11.text=\n\ Remove\ a\ track\ from\ the\ collection.\n\ @param\ index\ The\ index\ of\ the\ track\ to\ be\ removed.\n
|
||||
comment12.params=index
|
||||
comment12.target=boolean\ indexValid(int)
|
||||
comment12.text=\n\ Determine\ whether\ the\ given\ index\ is\ valid\ for\ the\ collection.\n\ Print\ an\ error\ message\ if\ it\ is\ not.\n\ @param\ index\ The\ index\ to\ be\ checked.\n\ @return\ true\ if\ the\ index\ is\ valid,\ false\ otherwise.\n
|
||||
comment13.params=folderName
|
||||
comment13.target=void\ readLibrary(java.lang.String)
|
||||
comment2.params=track
|
||||
comment2.target=void\ addTrack(Track)
|
||||
comment2.text=\n\ Add\ a\ track\ to\ the\ collection.\n\ @param\ track\ The\ track\ to\ be\ added.\n
|
||||
comment3.params=index
|
||||
comment3.target=Track\ getTrack(int)
|
||||
comment3.text=\n\ Get\ a\ track\ from\ the\ collection.\n\ @param\ index\ The\ index\ of\ the\ track.\n\ @return\ The\ selected\ track,\ or\ null\ if\ it\ does\ not\ exist.\n
|
||||
comment4.params=
|
||||
comment4.target=int\ getNumberOfTracks()
|
||||
comment4.text=\n\ Return\ the\ number\ of\ tracks\ in\ the\ collection.\n\ @return\ The\ number\ of\ tracks\ in\ the\ collection.\n
|
||||
comment5.params=
|
||||
comment5.target=java.util.List\ getAllTracks()
|
||||
comment5.text=\n\ Return\ a\ copy\ of\ all\ the\ tracks\ in\ the\ collection.\n\ @return\ All\ the\ tracks\ in\ the\ collection.\n
|
||||
comment6.params=
|
||||
comment6.target=java.util.List\ sortByArtist()
|
||||
comment6.text=\n\ Return\ a\ list\ of\ the\ tracks,\ sorted\ by\ artist.\n\ @return\ The\ tracks,\ sorted\ by\ artist.\n
|
||||
comment7.params=
|
||||
comment7.target=java.util.List\ sortByTitle()
|
||||
comment7.text=\n\ Return\ a\ list\ of\ the\ tracks,\ sorted\ by\ title.\n\ @return\ The\ tracks,\ sorted\ by\ title.\n
|
||||
comment8.params=comparator
|
||||
comment8.target=java.util.List\ sortBy(java.util.Comparator)
|
||||
comment8.text=\n\ Return\ a\ sorted\ copy\ of\ the\ track\ list.\n\ @param\ comparator\ The\ comparator\ for\ the\ sort.\n\ @return\ A\ sorted\ copy\ of\ the\ list.\n
|
||||
comment9.params=field
|
||||
comment9.target=java.util.List\ sortByField(java.lang.String)
|
||||
comment9.text=\n\ Return\ a\ list\ of\ the\ tracks,\ sorted\ by\ the\ given\ field\ name.\n\ @param\ field\ The\ field\ to\ sort\ by;\ e.g.,\ Artist,\ Title,\ etc.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ @see\ Track.FIELDS\n\ @return\ The\ tracks,\ sorted\ by\ the\ field.\n
|
||||
numComments=14
|
@@ -0,0 +1,174 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class to hold details of audio tracks.
|
||||
* Individual tracks may be played.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class MusicOrganizer
|
||||
{
|
||||
// An ArrayList for storing music tracks.
|
||||
private ArrayList<Track> tracks;
|
||||
// A reader that can read music files and load them as tracks.
|
||||
private TrackReader reader;
|
||||
|
||||
/**
|
||||
* Create a MusicOrganizer.
|
||||
* @param folderName The folder of audio files.
|
||||
*/
|
||||
public MusicOrganizer(String folderName)
|
||||
{
|
||||
tracks = new ArrayList<>();
|
||||
reader = new TrackReader();
|
||||
readLibrary(folderName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a track file to the collection.
|
||||
* @param filename The file name of the track to be added.
|
||||
*/
|
||||
public void addFile(String filename)
|
||||
{
|
||||
tracks.add(new Track(filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a track to the collection.
|
||||
* @param track The track to be added.
|
||||
*/
|
||||
public void addTrack(Track track)
|
||||
{
|
||||
tracks.add(track);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a track from the collection.
|
||||
* @param index The index of the track.
|
||||
* @return The selected track, or null if it does not exist.
|
||||
*/
|
||||
public Track getTrack(int index)
|
||||
{
|
||||
if(indexValid(index)) {
|
||||
return tracks.get(index);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of tracks in the collection.
|
||||
* @return The number of tracks in the collection.
|
||||
*/
|
||||
public int getNumberOfTracks()
|
||||
{
|
||||
return tracks.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a copy of all the tracks in the collection.
|
||||
* @return All the tracks in the collection.
|
||||
*/
|
||||
public List<Track> getAllTracks()
|
||||
{
|
||||
return new ArrayList<>(tracks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the tracks, sorted by artist.
|
||||
* @return The tracks, sorted by artist.
|
||||
*/
|
||||
public List<Track> sortByArtist()
|
||||
{
|
||||
return sortByField("Artist");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the tracks, sorted by title.
|
||||
* @return The tracks, sorted by title.
|
||||
*/
|
||||
public List<Track> sortByTitle()
|
||||
{
|
||||
return sortByField("Field");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a sorted copy of the track list.
|
||||
* @param comparator The comparator for the sort.
|
||||
* @return A sorted copy of the list.
|
||||
*/
|
||||
private List<Track> sortBy(Comparator<Track> comparator)
|
||||
{
|
||||
List<Track> copy = getAllTracks();
|
||||
Collections.sort(copy, comparator);
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the tracks, sorted by the given field name.
|
||||
* @param field The field to sort by; e.g., Artist, Title, etc.
|
||||
* @see Track.FIELDS
|
||||
* @return The tracks, sorted by the field.
|
||||
*/
|
||||
public List<Track> sortByField(final String field)
|
||||
{
|
||||
return sortBy(new Comparator<Track>() {
|
||||
public int compare(Track t1, Track t2)
|
||||
{
|
||||
return t1.getField(field).compareTo(t2.getField(field));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a track from the collection.
|
||||
* @param index The index of the track to be removed.
|
||||
*/
|
||||
public void removeTrack(int index)
|
||||
{
|
||||
if(indexValid(index)) {
|
||||
tracks.remove(index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given index is valid for the collection.
|
||||
* Print an error message if it is not.
|
||||
* @param index The index to be checked.
|
||||
* @return true if the index is valid, false otherwise.
|
||||
*/
|
||||
private boolean indexValid(int index)
|
||||
{
|
||||
// The return value.
|
||||
// Set according to whether the index is valid or not.
|
||||
boolean valid;
|
||||
|
||||
if(index < 0) {
|
||||
System.out.println("Index cannot be negative: " + index);
|
||||
valid = false;
|
||||
}
|
||||
else if(index >= tracks.size()) {
|
||||
System.out.println("Index is too large: " + index);
|
||||
valid = false;
|
||||
}
|
||||
else {
|
||||
valid = true;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
public void readLibrary(String folderName)
|
||||
{
|
||||
ArrayList<Track> tempTracks = reader.readTracks(folderName, ".mp3");
|
||||
|
||||
// Put all thetracks into the organizer.
|
||||
for(Track track : tempTracks) {
|
||||
addTrack(track);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=MusicPlayer()
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ MusicPlayer\n
|
||||
comment1.params=filename
|
||||
comment1.target=void\ startPlaying(java.lang.String)
|
||||
comment1.text=\n\ Start\ playing\ the\ given\ audio\ file.\n\ The\ method\ returns\ once\ the\ playing\ has\ been\ started.\n\ @param\ filename\ The\ file\ to\ be\ played.\n
|
||||
comment10.params=
|
||||
comment10.target=void\ run()
|
||||
comment11.params=
|
||||
comment11.target=void\ killPlayer()
|
||||
comment11.text=\n\ Terminate\ the\ player,\ if\ there\ is\ one.\n
|
||||
comment12.params=
|
||||
comment12.target=void\ reportProblem()
|
||||
comment12.text=\n\ Report\ a\ problem\ playing\ the\ current\ file.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ stop()
|
||||
comment2.text=\n\ Stop\ playing\ the\ current\ file.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ pause()
|
||||
comment3.text=\n\ Pause\ the\ current\ file.\n
|
||||
comment4.params=
|
||||
comment4.target=void\ resume()
|
||||
comment4.text=\n\ Resume\ playing\ following\ a\ pause.\n
|
||||
comment5.params=
|
||||
comment5.target=void\ run()
|
||||
comment6.params=position
|
||||
comment6.target=void\ seekTo(int)
|
||||
comment6.text=\n\ Seek\ to\ the\ given\ position\ in\ the\ current\ file.\n\ The\ track\ will\ be\ paused\ as\ a\ result\ of\ this\ operation.\n\ \n\ @param\ position\ What\ position\ in\ the\ file\ to\ move\ to.\n
|
||||
comment7.params=
|
||||
comment7.target=int\ getLength()
|
||||
comment7.text=\n\ Return\ the\ length\ of\ the\ current\ music\ file,\ if\ any.\n\ The\ length\ is\ in\ 'frames'\ rather\ than\ seconds,\ for\ instance.\n\ \n\ @return\ The\ file\ length\ in\ frames.\n
|
||||
comment8.params=filename
|
||||
comment8.target=void\ setupPlayer(java.lang.String)
|
||||
comment8.text=\n\ Set\ up\ the\ player\ ready\ to\ play\ the\ given\ file.\n\ @param\ filename\ The\ name\ of\ the\ file\ to\ play.\n
|
||||
comment9.params=start
|
||||
comment9.target=void\ playFrom(int)
|
||||
comment9.text=\n\ Play\ from\ the\ given\ position.\n\ @param\ start\ The\ starting\ position\ for\ playing.\n\ \ \ \ \ \ \ \ \ \ \ \ \ \ Must\ be\ within\ the\ current\ file's\ length.\n
|
||||
numComments=13
|
@@ -0,0 +1,191 @@
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import javazoom.jl.decoder.JavaLayerException;
|
||||
import javazoom.jl.player.AudioDevice;
|
||||
import javazoom.jl.player.FactoryRegistry;
|
||||
|
||||
/**
|
||||
* Provide basic playing of MP3 files via the javazoom library.
|
||||
* See http://www.javazoom.net/
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class MusicPlayer
|
||||
{
|
||||
// The current player. It might be null.
|
||||
private MusicFilePlayer player;
|
||||
// The current file being played.
|
||||
private String filename;
|
||||
|
||||
/**
|
||||
* Constructor for objects of class MusicPlayer
|
||||
*/
|
||||
public MusicPlayer()
|
||||
{
|
||||
player = null;
|
||||
filename = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Start playing the given audio file.
|
||||
* The method returns once the playing has been started.
|
||||
* @param filename The file to be played.
|
||||
*/
|
||||
public void startPlaying(final String filename)
|
||||
{
|
||||
try {
|
||||
setupPlayer(filename);
|
||||
playFrom(0);
|
||||
}
|
||||
catch (JavaLayerException ex) {
|
||||
reportProblem();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop playing the current file.
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
killPlayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause the current file.
|
||||
*/
|
||||
public void pause()
|
||||
{
|
||||
if(player != null) {
|
||||
try {
|
||||
player.pause();
|
||||
}
|
||||
catch(JavaLayerException e) {
|
||||
reportProblem();
|
||||
killPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume playing following a pause.
|
||||
*/
|
||||
public void resume()
|
||||
{
|
||||
if(player != null) {
|
||||
Thread playerThread = new Thread() {
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
player.resume();
|
||||
}
|
||||
catch(JavaLayerException e) {
|
||||
reportProblem();
|
||||
killPlayer();
|
||||
}
|
||||
}
|
||||
};
|
||||
playerThread.setPriority(Thread.MIN_PRIORITY);
|
||||
playerThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to the given position in the current file.
|
||||
* The track will be paused as a result of this operation.
|
||||
*
|
||||
* @param position What position in the file to move to.
|
||||
*/
|
||||
public void seekTo(int position)
|
||||
{
|
||||
if(player != null && position >= 0 && position < player.getLength()) {
|
||||
// Set the player's position.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the current music file, if any.
|
||||
* The length is in 'frames' rather than seconds, for instance.
|
||||
*
|
||||
* @return The file length in frames.
|
||||
*/
|
||||
public int getLength()
|
||||
{
|
||||
if(player != null) {
|
||||
return player.getLength();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the player ready to play the given file.
|
||||
* @param filename The name of the file to play.
|
||||
*/
|
||||
private void setupPlayer(String filename)
|
||||
{
|
||||
try {
|
||||
if(player != null) {
|
||||
killPlayer();
|
||||
}
|
||||
this.filename = filename;
|
||||
player = new MusicFilePlayer(filename);
|
||||
}
|
||||
catch(JavaLayerException e) {
|
||||
System.out.println("Problem setting up player");
|
||||
e.printStackTrace();
|
||||
reportProblem();
|
||||
killPlayer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Play from the given position.
|
||||
* @param start The starting position for playing.
|
||||
* Must be within the current file's length.
|
||||
*/
|
||||
private void playFrom(final int start) throws JavaLayerException
|
||||
{
|
||||
Thread playerThread = new Thread() {
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
player.playFrom(start);
|
||||
}
|
||||
catch(JavaLayerException e) {
|
||||
reportProblem();
|
||||
killPlayer();
|
||||
}
|
||||
}
|
||||
};
|
||||
playerThread.setPriority(Thread.MIN_PRIORITY);
|
||||
playerThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the player, if there is one.
|
||||
*/
|
||||
private void killPlayer()
|
||||
{
|
||||
synchronized(this) {
|
||||
if(player != null) {
|
||||
player.stop();
|
||||
player = null;
|
||||
filename = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a problem playing the current file.
|
||||
*/
|
||||
private void reportProblem()
|
||||
{
|
||||
System.out.println("There was a problem playing: " + filename);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
#BlueJ class context
|
||||
comment0.params=args
|
||||
comment0.target=void\ main(java.lang.String[])
|
||||
comment0.text=\n\ Main\ method\ for\ starting\ the\ player\ from\ a\ command\ line.\n
|
||||
comment1.params=
|
||||
comment1.target=MusicPlayerGUI()
|
||||
comment1.text=\n\ Create\ a\ SoundPlayer\ and\ display\ its\ GUI\ on\ screen.\n
|
||||
comment10.params=trackList
|
||||
comment10.target=java.lang.String[]\ getTracksDisplayList(java.util.List)
|
||||
comment10.text=\n\ Get\ a\ display\ version\ of\ the\ track\ list.\n\ @param\ trackList\ The\ list\ of\ tracks\ to\ be\ displayed.\n\ @return\ The\ tracks\ in\ display\ format.\n
|
||||
comment11.params=
|
||||
comment11.target=void\ makeFrame()
|
||||
comment11.text=\n\ Create\ the\ complete\ application\ GUI.\n
|
||||
comment12.params=
|
||||
comment12.target=void\ makeMenuBar()
|
||||
comment12.text=\n\ Create\ the\ main\ frame's\ menu\ bar.\n
|
||||
comment2.params=
|
||||
comment2.target=void\ play()
|
||||
comment2.text=\n\ Play\ the\ sound\ file\ currently\ selected\ in\ the\ file\ list.\ If\ there\ is\ no\n\ selection\ in\ the\ list,\ or\ if\ the\ selected\ file\ is\ not\ a\ sound\ file,\ \n\ do\ nothing.\n
|
||||
comment3.params=
|
||||
comment3.target=void\ stop()
|
||||
comment3.text=\n\ Stop\ the\ currently\ playing\ sound\ file\ (if\ there\ is\ one\ playing).\n
|
||||
comment4.params=
|
||||
comment4.target=void\ pause()
|
||||
comment4.text=\n\ Stop\ the\ currently\ playing\ sound\ file\ (if\ there\ is\ one\ playing).\n
|
||||
comment5.params=
|
||||
comment5.target=void\ resume()
|
||||
comment5.text=\n\ Resume\ a\ previously\ suspended\ sound\ file.\n
|
||||
comment6.params=message
|
||||
comment6.target=void\ showInfo(java.lang.String)
|
||||
comment6.text=\n\ Display\ information\ about\ a\ selected\ sound\ file\ (name\ and\ clip\ length).\n\ @param\ message\ The\ message\ to\ display.\n
|
||||
comment7.params=
|
||||
comment7.target=void\ quit()
|
||||
comment7.text=\n\ Quit\ function\:\ quit\ the\ application.\n
|
||||
comment8.params=
|
||||
comment8.target=void\ showAbout()
|
||||
comment8.text=\n\ About\ function\:\ show\ the\ 'about'\ box.\n
|
||||
comment9.params=ordering
|
||||
comment9.target=void\ setListOrdering(java.lang.String)
|
||||
comment9.text=\n\ Set\ the\ ordering\ of\ the\ track\ list.\n\ @param\ ordering\ The\ ordering\ to\ use.\n
|
||||
numComments=13
|
@@ -0,0 +1,308 @@
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.*;
|
||||
import javax.swing.border.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* A simple sound player. To start, create an instance of this class.
|
||||
*
|
||||
* The sound player provides an interface to the MusicOrganizer class
|
||||
* from chapter 4.
|
||||
*
|
||||
* @author Michael Kölling and David J. Barnes
|
||||
* @version 1.0
|
||||
*/
|
||||
public class MusicPlayerGUI extends JFrame
|
||||
{
|
||||
private static final String VERSION = "Version 1.0";
|
||||
private static final String AUDIO_DIR = "../audio";
|
||||
|
||||
private JList<String> fileList;
|
||||
private JSlider slider;
|
||||
private JLabel infoLabel;
|
||||
private MusicOrganizer organizer;
|
||||
// A player for the music tracks.
|
||||
private MusicPlayer player;
|
||||
// The current track list.
|
||||
private List<Track> trackList;
|
||||
|
||||
/**
|
||||
* Main method for starting the player from a command line.
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
MusicPlayerGUI gui = new MusicPlayerGUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a SoundPlayer and display its GUI on screen.
|
||||
*/
|
||||
public MusicPlayerGUI()
|
||||
{
|
||||
super("Music Player");
|
||||
organizer = new MusicOrganizer(AUDIO_DIR);
|
||||
player = new MusicPlayer();
|
||||
|
||||
makeFrame();
|
||||
}
|
||||
|
||||
/**
|
||||
* Play the sound file currently selected in the file list. If there is no
|
||||
* selection in the list, or if the selected file is not a sound file,
|
||||
* do nothing.
|
||||
*/
|
||||
private void play()
|
||||
{
|
||||
int index = fileList.getSelectedIndex();
|
||||
if(index >= 0 && index < trackList.size()) {
|
||||
slider.setValue(0);
|
||||
player.startPlaying(trackList.get(index).getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the currently playing sound file (if there is one playing).
|
||||
*/
|
||||
private void stop()
|
||||
{
|
||||
player.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the currently playing sound file (if there is one playing).
|
||||
*/
|
||||
private void pause()
|
||||
{
|
||||
player.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Resume a previously suspended sound file.
|
||||
*/
|
||||
private void resume()
|
||||
{
|
||||
player.resume();
|
||||
}
|
||||
|
||||
/**
|
||||
* Display information about a selected sound file (name and clip length).
|
||||
* @param message The message to display.
|
||||
*/
|
||||
private void showInfo(String message)
|
||||
{
|
||||
infoLabel.setText(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Quit function: quit the application.
|
||||
*/
|
||||
private void quit()
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* About function: show the 'about' box.
|
||||
*/
|
||||
private void showAbout()
|
||||
{
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Music Player\n" + VERSION,
|
||||
"About Music Player",
|
||||
JOptionPane.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ordering of the track list.
|
||||
* @param ordering The ordering to use.
|
||||
*/
|
||||
private void setListOrdering(String ordering)
|
||||
{
|
||||
trackList = organizer.sortByField(ordering);
|
||||
String[] tracks = getTracksDisplayList(trackList);
|
||||
fileList.setListData(tracks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a display version of the track list.
|
||||
* @param trackList The list of tracks to be displayed.
|
||||
* @return The tracks in display format.
|
||||
*/
|
||||
private String[] getTracksDisplayList(List<Track> trackList)
|
||||
{
|
||||
int numTracks = trackList.size();
|
||||
String[] tracks = new String[numTracks];
|
||||
for(int i = 0; i < numTracks; i++) {
|
||||
String[] fields = trackList.get(i).getFields();
|
||||
StringBuilder listing = new StringBuilder();
|
||||
for(String field : fields) {
|
||||
listing.append(field);
|
||||
listing.append(" ");
|
||||
}
|
||||
tracks[i] = listing.toString().trim();
|
||||
}
|
||||
return tracks;
|
||||
}
|
||||
|
||||
// ---- Swing stuff to build the frame and all its components and menus ----
|
||||
|
||||
/**
|
||||
* Create the complete application GUI.
|
||||
*/
|
||||
private void makeFrame()
|
||||
{
|
||||
// the following makes sure that our application exits when
|
||||
// the user closes its window
|
||||
setDefaultCloseOperation(EXIT_ON_CLOSE);
|
||||
|
||||
JPanel contentPane = (JPanel) getContentPane();
|
||||
contentPane.setBorder(new EmptyBorder(6, 10, 10, 10));
|
||||
|
||||
makeMenuBar();
|
||||
|
||||
// Specify the layout manager with nice spacing
|
||||
contentPane.setLayout(new BorderLayout(8, 8));
|
||||
|
||||
// Create the left side with combobox and scroll list
|
||||
JPanel leftPane = new JPanel();
|
||||
{
|
||||
leftPane.setLayout(new BorderLayout(8, 8));
|
||||
|
||||
// Set up components for ordering the list of tracks.
|
||||
JPanel orderingPanel = new JPanel();
|
||||
orderingPanel.setLayout(new BorderLayout());
|
||||
orderingPanel.add(new JLabel("Order by:"), BorderLayout.NORTH);
|
||||
|
||||
// Get the list of field names, used for ordering.
|
||||
String[] ordering = Track.FIELDS;
|
||||
|
||||
// Create the combo box.
|
||||
JComboBox<String> formatList = new JComboBox<>(ordering);
|
||||
formatList.addActionListener(e -> {
|
||||
int index = formatList.getSelectedIndex();
|
||||
if(index >= 0) {
|
||||
String selectedOrder = formatList.getItemAt(index);
|
||||
setListOrdering(selectedOrder);
|
||||
}
|
||||
});
|
||||
orderingPanel.add(formatList, BorderLayout.CENTER);
|
||||
|
||||
leftPane.add(orderingPanel, BorderLayout.NORTH);
|
||||
|
||||
// Create the scrolled list for track listing.
|
||||
fileList = new JList<>();
|
||||
fileList.setForeground(new Color(140,171,226));
|
||||
fileList.setBackground(new Color(0,0,0));
|
||||
fileList.setSelectionBackground(new Color(87,49,134));
|
||||
fileList.setSelectionForeground(new Color(140,171,226));
|
||||
JScrollPane scrollPane = new JScrollPane(fileList);
|
||||
scrollPane.setColumnHeaderView(new JLabel("Audio files"));
|
||||
leftPane.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
// Set up the initial listing.
|
||||
setListOrdering(ordering[0]);
|
||||
}
|
||||
contentPane.add(leftPane, BorderLayout.CENTER);
|
||||
|
||||
// Create the center with image, text label, and slider
|
||||
JPanel centerPane = new JPanel();
|
||||
{
|
||||
centerPane.setLayout(new BorderLayout(8, 8));
|
||||
|
||||
JLabel image = new JLabel(new ImageIcon("title.jpg"));
|
||||
centerPane.add(image, BorderLayout.NORTH);
|
||||
centerPane.setBackground(Color.BLACK);
|
||||
|
||||
infoLabel = new JLabel(" ");
|
||||
infoLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
infoLabel.setForeground(new Color(140,171,226));
|
||||
centerPane.add(infoLabel, BorderLayout.CENTER);
|
||||
|
||||
slider = new JSlider(0, 100, 0);
|
||||
TitledBorder border = new TitledBorder("Seek");
|
||||
border.setTitleColor(Color.white);
|
||||
slider.setBorder(new CompoundBorder(new EmptyBorder(6, 10, 10, 10), border));
|
||||
// Provide a body for the change-listener lambda to react to changes
|
||||
// of the slider.
|
||||
slider.addChangeListener(e -> { });
|
||||
slider.setBackground(Color.BLACK);
|
||||
slider.setMajorTickSpacing(25);
|
||||
slider.setPaintTicks(true);
|
||||
centerPane.add(slider, BorderLayout.SOUTH);
|
||||
}
|
||||
contentPane.add(centerPane, BorderLayout.EAST);
|
||||
|
||||
// Create the toolbar with the buttons
|
||||
JPanel toolbar = new JPanel();
|
||||
{
|
||||
toolbar.setLayout(new GridLayout(1, 0));
|
||||
|
||||
JButton button = new JButton("Play");
|
||||
button.addActionListener(e -> play());
|
||||
toolbar.add(button);
|
||||
|
||||
button = new JButton("Stop");
|
||||
button.addActionListener(e -> stop());
|
||||
toolbar.add(button);
|
||||
|
||||
button = new JButton("Pause");
|
||||
button.addActionListener(e -> pause());
|
||||
toolbar.add(button);
|
||||
|
||||
button = new JButton("Resume");
|
||||
button.addActionListener(e -> resume());
|
||||
toolbar.add(button);
|
||||
|
||||
}
|
||||
|
||||
contentPane.add(toolbar, BorderLayout.NORTH);
|
||||
|
||||
// building is done - arrange the components
|
||||
pack();
|
||||
|
||||
// place this frame at the center of the screen and show
|
||||
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
|
||||
setLocation(d.width/2 - getWidth()/2, d.height/2 - getHeight()/2);
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the main frame's menu bar.
|
||||
*/
|
||||
private void makeMenuBar()
|
||||
{
|
||||
final int SHORTCUT_MASK =
|
||||
Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
|
||||
|
||||
JMenuBar menubar = new JMenuBar();
|
||||
setJMenuBar(menubar);
|
||||
|
||||
JMenu menu;
|
||||
JMenuItem item;
|
||||
|
||||
// create the File menu
|
||||
menu = new JMenu("File");
|
||||
menubar.add(menu);
|
||||
|
||||
item = new JMenuItem("Quit");
|
||||
item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, SHORTCUT_MASK));
|
||||
item.addActionListener(e -> quit());
|
||||
menu.add(item);
|
||||
|
||||
// create the Help menu
|
||||
menu = new JMenu("Help");
|
||||
menubar.add(menu);
|
||||
|
||||
item = new JMenuItem("About Music Player...");
|
||||
item.addActionListener(e -> showAbout());
|
||||
menu.add(item);
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
Project: musicplayer. A project to play audio files.
|
||||
Authors: David J. Barnes and Michael Kölling
|
||||
|
||||
This project is part of the material for the book
|
||||
|
||||
Objects First with Java - A Practical Introduction using BlueJ
|
||||
Sixth edition
|
||||
David J. Barnes and Michael Kölling
|
||||
Pearson Education, 2016
|
||||
|
||||
It is discussed in chapter 13.
|
||||
|
||||
To start:
|
||||
|
||||
Create a MusicPlayerGUI object.
|
||||
Select a track from the list and play it.
|
||||
|
@@ -0,0 +1,29 @@
|
||||
#BlueJ class context
|
||||
comment0.params=artist\ title\ filename
|
||||
comment0.target=Track(java.lang.String,\ java.lang.String,\ java.lang.String)
|
||||
comment0.text=\n\ Constructor\ for\ objects\ of\ class\ Track.\n\ @param\ artist\ The\ track's\ artist.\n\ @param\ title\ The\ track's\ title.\n\ @param\ filename\ The\ track\ file.\ \n
|
||||
comment1.params=filename
|
||||
comment1.target=Track(java.lang.String)
|
||||
comment1.text=\n\ Constructor\ for\ objects\ of\ class\ Track.\n\ It\ is\ assumed\ that\ the\ file\ name\ cannot\ be\n\ decoded\ to\ extract\ artist\ and\ title\ details.\n\ @param\ filename\ The\ track\ file.\ \n
|
||||
comment2.params=
|
||||
comment2.target=java.lang.String\ getArtist()
|
||||
comment2.text=\n\ Return\ the\ artist.\n\ @return\ The\ artist.\n
|
||||
comment3.params=
|
||||
comment3.target=java.lang.String\ getTitle()
|
||||
comment3.text=\n\ Return\ the\ title.\n\ @return\ The\ title.\n
|
||||
comment4.params=
|
||||
comment4.target=java.lang.String\ getFilename()
|
||||
comment4.text=\n\ Return\ the\ file\ name.\n\ @return\ The\ file\ name.\n
|
||||
comment5.params=field
|
||||
comment5.target=java.lang.String\ getField(java.lang.String)
|
||||
comment5.text=\n\ Return\ the\ value\ of\ the\ named\ field.\n\ The\ field\ should\ be\ an\ element\ of\ Track.FIELDS\n\ @param\ field\ Which\ field\ to\ return.\n
|
||||
comment6.params=
|
||||
comment6.target=java.lang.String[]\ getFields()
|
||||
comment6.text=\n\ Return\ the\ values\ of\ the\ fields.\n\ @return\ The\ fields.\n
|
||||
comment7.params=
|
||||
comment7.target=java.lang.String\ getDetails()
|
||||
comment7.text=\n\ Return\ details\ of\ the\ track\:\ artist,\ title\ and\ file\ name.\n\ @return\ The\ track's\ details.\n
|
||||
comment8.params=artist\ title\ filename
|
||||
comment8.target=void\ setDetails(java.lang.String,\ java.lang.String,\ java.lang.String)
|
||||
comment8.text=\n\ Set\ details\ of\ the\ track.\n\ @param\ artist\ The\ track's\ artist.\n\ @param\ title\ The\ track's\ title.\n\ @param\ filename\ The\ track\ file.\ \n
|
||||
numComments=9
|
132
Semester 1/Programming 1/Java/examples/projects/chapter13/musicplayer/Track.java
Executable file
132
Semester 1/Programming 1/Java/examples/projects/chapter13/musicplayer/Track.java
Executable file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* Store the details of a music track,
|
||||
* such as the artist, title, and file name.
|
||||
* Use the FIELDS class variable for the names of
|
||||
* the available attributes.
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class Track
|
||||
{
|
||||
// The artist.
|
||||
private String artist;
|
||||
// The track's title.
|
||||
private String title;
|
||||
// Where the track is stored.
|
||||
private String filename;
|
||||
|
||||
// Names for the available fields.
|
||||
public static final String[] FIELDS = {
|
||||
"Artist",
|
||||
"Title",
|
||||
"Filename",
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor for objects of class Track.
|
||||
* @param artist The track's artist.
|
||||
* @param title The track's title.
|
||||
* @param filename The track file.
|
||||
*/
|
||||
public Track(String artist, String title, String filename)
|
||||
{
|
||||
setDetails(artist, title, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for objects of class Track.
|
||||
* It is assumed that the file name cannot be
|
||||
* decoded to extract artist and title details.
|
||||
* @param filename The track file.
|
||||
*/
|
||||
public Track(String filename)
|
||||
{
|
||||
setDetails("unknown", "unknown", filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the artist.
|
||||
* @return The artist.
|
||||
*/
|
||||
public String getArtist()
|
||||
{
|
||||
return artist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the title.
|
||||
* @return The title.
|
||||
*/
|
||||
public String getTitle()
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file name.
|
||||
* @return The file name.
|
||||
*/
|
||||
public String getFilename()
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value of the named field.
|
||||
* The field should be an element of Track.FIELDS
|
||||
* @param field Which field to return.
|
||||
*/
|
||||
public String getField(String field)
|
||||
{
|
||||
if (field.equals("Artist")) {
|
||||
return artist;
|
||||
}
|
||||
else if (field.equals("Title")) {
|
||||
return title;
|
||||
}
|
||||
else if (field.equals("Filename")) {
|
||||
return filename;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Unknown field name: " + field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the values of the fields.
|
||||
* @return The fields.
|
||||
*/
|
||||
public String[] getFields()
|
||||
{
|
||||
String[] fields = new String[FIELDS.length];
|
||||
for(int i = 0; i < FIELDS.length; i++) {
|
||||
fields[i] = getField(FIELDS[i]);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return details of the track: artist, title and file name.
|
||||
* @return The track's details.
|
||||
*/
|
||||
public String getDetails()
|
||||
{
|
||||
return artist + ": " + title + " (file: " + filename + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Set details of the track.
|
||||
* @param artist The track's artist.
|
||||
* @param title The track's title.
|
||||
* @param filename The track file.
|
||||
*/
|
||||
private void setDetails(String artist, String title, String filename)
|
||||
{
|
||||
this.artist = artist;
|
||||
this.title = title;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
#BlueJ class context
|
||||
comment0.params=
|
||||
comment0.target=TrackReader()
|
||||
comment0.text=\n\ Create\ the\ track\ reader,\ ready\ to\ read\ tracks\ from\ the\ music\ library\ folder.\n
|
||||
comment1.params=folder\ suffix
|
||||
comment1.target=java.util.ArrayList\ readTracks(java.lang.String,\ java.lang.String)
|
||||
comment1.text=\n\ Read\ music\ files\ from\ the\ given\ library\ folder\n\ with\ the\ given\ suffix.\n\ @param\ folder\ The\ folder\ to\ look\ for\ files.\n\ @param\ suffix\ The\ suffix\ of\ the\ audio\ type.\n
|
||||
comment2.params=dir\ name
|
||||
comment2.target=boolean\ accept(java.io.File,\ java.lang.String)
|
||||
comment2.text=\n\ Accept\ files\ with\ matching\ suffix.\n\ @param\ dir\ The\ directory\ containing\ the\ file.\n\ @param\ name\ The\ name\ of\ the\ file.\n\ @return\ true\ if\ the\ name\ ends\ with\ the\ suffix.\n
|
||||
comment3.params=file
|
||||
comment3.target=Track\ decodeDetails(java.io.File)
|
||||
comment3.text=\n\ Try\ to\ decode\ details\ of\ the\ artist\ and\ the\ title\n\ from\ the\ file\ name.\n\ It\ is\ assumed\ that\ the\ details\ are\ in\ the\ form\:\n\ \ \ \ \ artist-title.mp3\n\ @param\ file\ The\ track\ file.\n\ @return\ A\ Track\ containing\ the\ details.\n
|
||||
numComments=4
|
@@ -0,0 +1,91 @@
|
||||
import java.io.File;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* A helper class for our music application. This class can read files from the file system
|
||||
* from a given folder with a specified suffix. It will interpret the file name as artist/
|
||||
* track title information.
|
||||
*
|
||||
* It is expected that file names of music tracks follow a standard format of artist name
|
||||
* and track name, separated by a dash. For example: TheBeatles-HereComesTheSun.mp3
|
||||
*
|
||||
* @author David J. Barnes and Michael Kölling
|
||||
* @version 2016.02.29
|
||||
*/
|
||||
public class TrackReader
|
||||
{
|
||||
/**
|
||||
* Create the track reader, ready to read tracks from the music library folder.
|
||||
*/
|
||||
public TrackReader()
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
/**
|
||||
* Read music files from the given library folder
|
||||
* with the given suffix.
|
||||
* @param folder The folder to look for files.
|
||||
* @param suffix The suffix of the audio type.
|
||||
*/
|
||||
public ArrayList<Track> readTracks(String folder, final String suffix)
|
||||
{
|
||||
File audioFolder = new File(folder);
|
||||
ArrayList<Track> tracks = new ArrayList<>();
|
||||
File[] audioFiles = audioFolder.listFiles(new FilenameFilter() {
|
||||
/**
|
||||
* Accept files with matching suffix.
|
||||
* @param dir The directory containing the file.
|
||||
* @param name The name of the file.
|
||||
* @return true if the name ends with the suffix.
|
||||
*/
|
||||
public boolean accept(File dir, String name)
|
||||
{
|
||||
return name.toLowerCase().endsWith(suffix);
|
||||
}
|
||||
});
|
||||
|
||||
// Put all the matching files into the organizer.
|
||||
for(File file : audioFiles) {
|
||||
Track trackDetails = decodeDetails(file);
|
||||
tracks.add(trackDetails);
|
||||
}
|
||||
return tracks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to decode details of the artist and the title
|
||||
* from the file name.
|
||||
* It is assumed that the details are in the form:
|
||||
* artist-title.mp3
|
||||
* @param file The track file.
|
||||
* @return A Track containing the details.
|
||||
*/
|
||||
private Track decodeDetails(File file)
|
||||
{
|
||||
// The information needed.
|
||||
String artist = "unknown";
|
||||
String title = "unknown";
|
||||
String filename = file.getPath();
|
||||
|
||||
// Look for artist and title in the name of the file.
|
||||
String details = file.getName();
|
||||
String[] parts = details.split("-");
|
||||
|
||||
if(parts.length == 2) {
|
||||
artist = parts[0];
|
||||
String titlePart = parts[1];
|
||||
// Remove a file-type suffix.
|
||||
parts = titlePart.split("\\.");
|
||||
if(parts.length >= 1) {
|
||||
title = parts[0];
|
||||
}
|
||||
else {
|
||||
title = titlePart;
|
||||
}
|
||||
}
|
||||
return new Track(artist, title, filename);
|
||||
}
|
||||
}
|
@@ -0,0 +1,115 @@
|
||||
#BlueJ package file
|
||||
dependency1.from=MusicOrganizer
|
||||
dependency1.to=Track
|
||||
dependency1.type=UsesDependency
|
||||
dependency2.from=TrackReader
|
||||
dependency2.to=Track
|
||||
dependency2.type=UsesDependency
|
||||
dependency3.from=MusicOrganizer
|
||||
dependency3.to=TrackReader
|
||||
dependency3.type=UsesDependency
|
||||
dependency4.from=MusicPlayerGUI
|
||||
dependency4.to=MusicOrganizer
|
||||
dependency4.type=UsesDependency
|
||||
dependency5.from=MusicPlayerGUI
|
||||
dependency5.to=MusicPlayer
|
||||
dependency5.type=UsesDependency
|
||||
dependency6.from=MusicPlayerGUI
|
||||
dependency6.to=Track
|
||||
dependency6.type=UsesDependency
|
||||
dependency7.from=MusicPlayer
|
||||
dependency7.to=MusicFilePlayer
|
||||
dependency7.type=UsesDependency
|
||||
objectbench.height=76
|
||||
objectbench.width=842
|
||||
package.editor.height=487
|
||||
package.editor.width=734
|
||||
package.editor.x=70
|
||||
package.editor.y=80
|
||||
package.numDependencies=7
|
||||
package.numTargets=6
|
||||
package.showExtends=true
|
||||
package.showUses=true
|
||||
project.charset=UTF-8
|
||||
readme.editor.height=751
|
||||
readme.editor.width=1110
|
||||
readme.editor.x=41
|
||||
readme.editor.y=49
|
||||
target1.editor.height=704
|
||||
target1.editor.width=831
|
||||
target1.editor.x=41
|
||||
target1.editor.y=23
|
||||
target1.height=60
|
||||
target1.name=MusicPlayerGUI
|
||||
target1.naviview.expanded=true
|
||||
target1.showInterface=false
|
||||
target1.type=ClassTarget
|
||||
target1.typeParameters=
|
||||
target1.width=110
|
||||
target1.x=170
|
||||
target1.y=30
|
||||
target2.editor.height=777
|
||||
target2.editor.width=1112
|
||||
target2.editor.x=124
|
||||
target2.editor.y=23
|
||||
target2.height=60
|
||||
target2.name=MusicFilePlayer
|
||||
target2.naviview.expanded=true
|
||||
target2.showInterface=false
|
||||
target2.type=ClassTarget
|
||||
target2.typeParameters=
|
||||
target2.width=110
|
||||
target2.x=160
|
||||
target2.y=370
|
||||
target3.editor.height=700
|
||||
target3.editor.width=930
|
||||
target3.editor.x=41
|
||||
target3.editor.y=100
|
||||
target3.height=60
|
||||
target3.name=MusicPlayer
|
||||
target3.naviview.expanded=true
|
||||
target3.showInterface=false
|
||||
target3.type=ClassTarget
|
||||
target3.typeParameters=
|
||||
target3.width=110
|
||||
target3.x=50
|
||||
target3.y=250
|
||||
target4.editor.height=700
|
||||
target4.editor.width=900
|
||||
target4.editor.x=175
|
||||
target4.editor.y=68
|
||||
target4.height=60
|
||||
target4.name=TrackReader
|
||||
target4.naviview.expanded=true
|
||||
target4.showInterface=false
|
||||
target4.type=ClassTarget
|
||||
target4.typeParameters=
|
||||
target4.width=110
|
||||
target4.x=340
|
||||
target4.y=170
|
||||
target5.editor.height=777
|
||||
target5.editor.width=1071
|
||||
target5.editor.x=209
|
||||
target5.editor.y=23
|
||||
target5.height=60
|
||||
target5.name=MusicOrganizer
|
||||
target5.naviview.expanded=true
|
||||
target5.showInterface=false
|
||||
target5.type=ClassTarget
|
||||
target5.typeParameters=
|
||||
target5.width=140
|
||||
target5.x=530
|
||||
target5.y=100
|
||||
target6.editor.height=700
|
||||
target6.editor.width=900
|
||||
target6.editor.x=271
|
||||
target6.editor.y=23
|
||||
target6.height=60
|
||||
target6.name=Track
|
||||
target6.naviview.expanded=true
|
||||
target6.showInterface=false
|
||||
target6.type=ClassTarget
|
||||
target6.typeParameters=
|
||||
target6.width=110
|
||||
target6.x=460
|
||||
target6.y=270
|
BIN
Semester 1/Programming 1/Java/examples/projects/chapter13/musicplayer/title.jpg
Executable file
BIN
Semester 1/Programming 1/Java/examples/projects/chapter13/musicplayer/title.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 40 KiB |
Reference in New Issue
Block a user