I'm writing an application that will use the JLayer
library to play an mp3 file. Since the player does not have any methods to pause the playback - play()
does not return until the song is finished - I decided to put it in a thread and simply control that thread. This is possible because one can execute play(1)
which plays a single frame of the song. Putting this in a loop will thus play the entire song smoothly.
What I have done is the following:
- Create a
Thread
classMp3PlayerThread
that can be used to play a song in a different thread. - Create a wrapping class
Mp3Player
that holds a thread object and controls thevolatile
flags inside theThread
.
I built the object Mp3Player
such that it should be thread safe. I.e., pass an instance of Mp3Player
around. To do this I made all the control methods synchronized
.
I am wondering if this code can be made more solid without using volatile
variables? I have always learned that you should not use the volatile
keyword if possible.
Mp3PlayerThread
public class Mp3PlayerThread extends Thread
{
public volatile boolean playing = false;
public volatile boolean abort = false;
private Player player;
private String path;
public Mp3PlayerThread(String file)
{
this.path = file;
}
public void run()
{
// http://stackoverflow.com/questions/7313657/should-you-synchronize-the-run-method-why-or-why-not
try
{
FileInputStream fis = new FileInputStream(this.path);
BufferedInputStream bis = new BufferedInputStream(fis);
this.player = new Player(bis);
while (!abort)
{
if (playing)
player.play(1);
}
Printer.debugMessage(this.getClass(), "end of file or stopped");
} catch (FileNotFoundException | JavaLayerException e)
{
e.printStackTrace();
}
}
}
Mp3Player
public class Mp3Player
{
private Mp3PlayerThread playerThread;
private String song;
public Mp3Player(String filename)
{
song = filename;
}
/**
* Initializes the playerThread with the given song, sets the flag to true
* and starts playing the song.
*/
public synchronized void play()
{
playerThread = new Mp3PlayerThread(song);
playerThread.playing = true;
playerThread.start();
}
/**
* Pauses the execution of the playerThread.
*/
public synchronized void pause()
{
Printer.debugMessage(this.getClass(), "paused");
playerThread.playing = false;
}
public synchronized void resume()
{
Printer.debugMessage(this.getClass(), "resuming");
playerThread.playing = true;
}
public synchronized void stop()
{
Printer.debugMessage(this.getClass(), "stopping");
playerThread.abort = true;
playerThread = null; // Needed or not?
}
}