diff --git a/pom.xml b/pom.xml
index e87628c..67b7411 100644
--- a/pom.xml
+++ b/pom.xml
@@ -85,6 +85,12 @@
1.5
compile
+
+ uk.co.caprica
+ vlcj
+ 3.10.1
+ compile
+
junit
junit
diff --git a/src/main/java/musicplayer/SwingUIModule.java b/src/main/java/musicplayer/SwingUIModule.java
index f9377bc..570461d 100644
--- a/src/main/java/musicplayer/SwingUIModule.java
+++ b/src/main/java/musicplayer/SwingUIModule.java
@@ -9,6 +9,7 @@ import musicplayer.library.ILibrary;
import musicplayer.library.JTreeLibrary;
import musicplayer.player.GStreamerPlayer;
import musicplayer.player.IPlayer;
+import musicplayer.player.VLCPlayer;
import musicplayer.playlist.IPlaylist;
import musicplayer.playlist.JTablePlaylist;
import musicplayer.swingui.PlayerGUI;
@@ -17,7 +18,7 @@ class SwingUIModule extends AbstractModule {
@Override
protected void configure() {
- bind(IPlayer.class).to(GStreamerPlayer.class).in(Singleton.class);
+ bind(IPlayer.class).to(VLCPlayer.class).in(Singleton.class);
bind(IDatabase.class).to(HibernateDatabase.class).in(Singleton.class);
bind(IPlaylist.class).to(JTablePlaylist.class).in(Singleton.class);
bind(ILibrary.class).to(JTreeLibrary.class).in(Singleton.class);
diff --git a/src/main/java/musicplayer/player/GStreamerPlayer.java b/src/main/java/musicplayer/player/GStreamerPlayer.java
index 13b0f8b..374105e 100644
--- a/src/main/java/musicplayer/player/GStreamerPlayer.java
+++ b/src/main/java/musicplayer/player/GStreamerPlayer.java
@@ -5,6 +5,7 @@ import musicplayer.StartPlayingException;
import musicplayer.callbacks.PlayerCallbackInterface;
import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist;
+import musicplayer.util.ConfigManager;
import org.gstreamer.ClockTime;
import org.gstreamer.ElementFactory;
import org.gstreamer.Gst;
@@ -73,7 +74,7 @@ public class GStreamerPlayer implements IPlayer{
}, "seekbar");
seekBarUpdater.start();
}
- currentVolume = getVolume();
+ setVolume(ConfigManager.getLastVolume());
}
private void resetSeek(){
diff --git a/src/main/java/musicplayer/player/VLCPlayer.java b/src/main/java/musicplayer/player/VLCPlayer.java
new file mode 100644
index 0000000..eac5e0a
--- /dev/null
+++ b/src/main/java/musicplayer/player/VLCPlayer.java
@@ -0,0 +1,203 @@
+package musicplayer.player;
+
+import com.google.inject.Inject;
+import musicplayer.StartPlayingException;
+import musicplayer.callbacks.PlayerCallbackInterface;
+import musicplayer.model.Song;
+import musicplayer.playlist.IPlaylist;
+import musicplayer.util.ConfigManager;
+import uk.co.caprica.vlcj.discovery.NativeDiscovery;
+import uk.co.caprica.vlcj.player.MediaPlayer;
+import uk.co.caprica.vlcj.player.MediaPlayerEventAdapter;
+import uk.co.caprica.vlcj.player.MediaPlayerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Optional;
+
+public class VLCPlayer implements IPlayer {
+
+ private ArrayList libvlcArgs = new ArrayList<>(Collections.singletonList("--vout=dummy"));
+ private MediaPlayer mediaPlayer;
+
+ private Song currentSong;
+ private final PlayerCallbackInterface callbackInterface;
+ private int currentVolume = 100;
+ private final IPlaylist playlist;
+ private boolean repeatMode = false; // Repeat the current song?
+
+ /**
+ * Manages VLC based playback operations.
+ * @param callbackInterface Interface on which UI updates can be called.
+ * @param playlist Playlist containing music to play.
+ */
+ @Inject
+ public VLCPlayer(PlayerCallbackInterface callbackInterface, IPlaylist playlist){
+ this(callbackInterface, playlist, false);
+ }
+
+ /**
+ * Manages VLC based playback operations.
+ * @param callbackInterface Interface on which UI updates can be called.
+ * @param playlist Playlist containing music to play.
+ */
+ VLCPlayer(PlayerCallbackInterface callbackInterface, IPlaylist playlist, boolean testMode){
+ new NativeDiscovery().discover();
+ if(testMode)
+ addTestModeFlags();
+ this.callbackInterface = callbackInterface;
+ this.playlist = playlist;
+ MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory(libvlcArgs.toArray(new String[libvlcArgs.size()]));
+ mediaPlayer = mediaPlayerFactory.newHeadlessMediaPlayer();
+ mediaPlayer.addMediaPlayerEventListener(new InternalEventAdapter());
+ Thread hookShutdown = new Thread(() -> {
+ // Cleanly dispose of the media player instance and any associated native resources
+ mediaPlayer.stop();
+ mediaPlayer.release();
+ // Cleanly dispose of the media player factory and any associated native resources
+ mediaPlayerFactory.release();
+ });
+ Runtime.getRuntime().addShutdownHook(hookShutdown);
+ setVolume(ConfigManager.getLastVolume());
+ }
+
+ private void addTestModeFlags(){
+ libvlcArgs.add("--aout=dummy");
+ }
+
+ @Override
+ public void play() throws StartPlayingException {
+ playSong(playlist.getActive());
+ }
+
+ @Override
+ public void playSong(Optional inputSong) throws StartPlayingException {
+ if(inputSong.isPresent()) {
+ playSong(inputSong.get());
+ }
+ }
+
+ private void playSong(Song inputSong) throws StartPlayingException {
+ resetSeek();
+ if(inputSong == currentSong && mediaPlayer.getTime() > 0 && mediaPlayer.getTime() <= mediaPlayer.getLength()) { // Song ~should~ already be loaded, unpause instead?
+ mediaPlayer.play();
+ return;
+ }
+ currentSong = inputSong;
+ playlist.setPlayingSong(currentSong);
+ File songFile = currentSong.getSongFile();
+ if (!songFile.exists()) {
+ playlist.delete(currentSong);
+ next();
+ return;
+ }
+ boolean startedPlaying = mediaPlayer.startMedia(inputSong.getSongFile().getAbsolutePath());
+ mediaPlayer.setVolume(currentVolume);
+ if(!startedPlaying)
+ throw new StartPlayingException(inputSong);
+ }
+
+ private void resetSeek(){
+ if(callbackInterface != null)
+ callbackInterface.setSeekBarPosition(0);
+ }
+
+ @Override
+ public void stop() {
+ resetSeek();
+ mediaPlayer.stop();
+ }
+
+ @Override
+ public void resume() {
+ mediaPlayer.setPause(false);
+ }
+
+ @Override
+ public void pause() {
+ mediaPlayer.setPause(true);
+ if (isPlaying()) {
+ //noinspection StatementWithEmptyBody
+ do { // Wait to be paused
+ } while (isPlaying());
+ }
+ }
+
+ @Override
+ public void next() throws StartPlayingException {
+ playSong(playlist.getNext(currentSong));
+ }
+
+ @Override
+ public void previous() throws StartPlayingException {
+ playSong(playlist.getPrevious(currentSong));
+ }
+
+ @Override
+ public Song getCurrentSong() {
+ return currentSong;
+ }
+
+ @Override
+ public int currentSongPosition() {
+ return (int)(mediaPlayer.getTime() / 1000);
+ }
+
+ @Override
+ public void setVolume(int volume) {
+ currentVolume = volume;
+ mediaPlayer.setVolume(volume);
+ }
+
+ @Override
+ public int getVolume() {
+ return (mediaPlayer.getVolume() >= 0) ? mediaPlayer.getVolume() : currentVolume;
+ }
+
+ @Override
+ public void seek(int position) {
+ mediaPlayer.setTime(position * 1000);
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return mediaPlayer.isPlaying();
+ }
+
+ @Override
+ public void setRepeat(boolean repeatMode) {
+ this.repeatMode = repeatMode;
+ }
+
+ @Override
+ public boolean isRepeating() {
+ return repeatMode;
+ }
+
+ private class InternalEventAdapter extends MediaPlayerEventAdapter{
+ @Override
+ public void finished(MediaPlayer mediaPlayer) {
+ try {
+ if (repeatMode)
+ playSong(currentSong);
+ else
+ playSong(playlist.getNext(currentSong));
+ } catch (StartPlayingException ex){
+ ex.printStackTrace();
+ }
+ }
+
+ @Override
+ public void playing(MediaPlayer mediaPlayer) {
+ if(callbackInterface != null)
+ callbackInterface.setSeekBarDuration((int) (mediaPlayer.getLength() / 1000));
+ }
+
+ @Override
+ public void timeChanged(MediaPlayer mediaPlayer, long newTime) {
+ if(callbackInterface != null)
+ callbackInterface.setSeekBarPosition((int)(newTime / 1000));
+ }
+ }
+}
diff --git a/src/main/java/musicplayer/swingui/PlayerGUI.java b/src/main/java/musicplayer/swingui/PlayerGUI.java
index b68e4bf..405984f 100644
--- a/src/main/java/musicplayer/swingui/PlayerGUI.java
+++ b/src/main/java/musicplayer/swingui/PlayerGUI.java
@@ -85,7 +85,10 @@ public class PlayerGUI extends JPanel implements PlayerCallbackInterface {
* @param seconds New length of seekBar.
*/
public void setSeekBarDuration(int seconds) {
- SwingUtilities.invokeLater(() -> seekBar.setMaximum(seconds));
+ SwingUtilities.invokeLater(() -> {
+ if (!seeking.get())
+ seekBar.setMaximum(seconds);
+ });
}
private void showError(StartPlayingException ex) {
diff --git a/src/test/java/musicplayer/player/GStreamerPlayerTest.java b/src/test/java/musicplayer/player/GStreamerPlayerTest.java
index e61fb5b..5640629 100644
--- a/src/test/java/musicplayer/player/GStreamerPlayerTest.java
+++ b/src/test/java/musicplayer/player/GStreamerPlayerTest.java
@@ -4,6 +4,7 @@ import musicplayer.library.ILibrary;
import musicplayer.model.Song;
import musicplayer.playlist.IPlaylist;
import musicplayer.playlist.JTablePlaylist;
+import musicplayer.util.ConfigManager;
import org.junit.Before;
import org.junit.Test;
@@ -67,7 +68,7 @@ public class GStreamerPlayerTest {
@Test
public void testSetVolume() throws Exception {
- assertEquals(100, player.getVolume());
+ assertEquals(ConfigManager.getLastVolume(), player.getVolume());
int newVolume = 25;
player.setVolume(25);
assertEquals(newVolume, player.getVolume());
diff --git a/src/test/java/musicplayer/player/VLCPlayerTest.java b/src/test/java/musicplayer/player/VLCPlayerTest.java
new file mode 100644
index 0000000..db22d3e
--- /dev/null
+++ b/src/test/java/musicplayer/player/VLCPlayerTest.java
@@ -0,0 +1,77 @@
+package musicplayer.player;
+
+import musicplayer.library.ILibrary;
+import musicplayer.model.Song;
+import musicplayer.playlist.IPlaylist;
+import musicplayer.playlist.JTablePlaylist;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.Optional;
+
+import static org.junit.Assert.*;
+
+public class VLCPlayerTest {
+
+ VLCPlayer player;
+ IPlaylist playlist;
+ Song sampleSong = new Song(ILibrary.autoParse(
+ new File(VLCPlayerTest.class.getResource("/sample.mp3").getFile()).toPath()).get());
+
+ @Before
+ public void setUp() throws Exception {
+ playlist = new JTablePlaylist();
+ player = new VLCPlayer(null, playlist, true);
+ }
+
+ @Test
+ public void testPlayEmptyPlaylist() throws Exception {
+ player.play();
+ assertFalse(player.isPlaying());
+ }
+
+
+ @Test
+ public void testPlay() throws Exception {
+ playlist.addSong(sampleSong);
+ player.play();
+ assertTrue(player.isPlaying());
+ }
+
+ @Test
+ public void testPlaySong() throws Exception {
+ player.playSong(Optional.of(sampleSong));
+ assertTrue(player.isPlaying());
+ assertEquals(sampleSong, player.getCurrentSong());
+ }
+
+ @Test
+ public void testStop() throws Exception {
+ player.playSong(Optional.of(sampleSong));
+ assertTrue(player.isPlaying());
+ player.stop();
+ assertFalse(player.isPlaying());
+ }
+
+ @Test
+ public void testResume() throws Exception {
+ }
+
+ @Test
+ public void testPause() throws Exception {
+ player.playSong(Optional.of(sampleSong));
+ assertTrue(player.isPlaying());
+ player.pause();
+ assertFalse(player.isPlaying());
+ }
+
+ @Test
+ public void testSeek() throws Exception {
+ player.playSong(Optional.of(sampleSong));
+ assertTrue(player.isPlaying());
+ player.seek(100);
+ System.out.println(player.currentSongPosition());
+ assertTrue(player.currentSongPosition() >= 100);
+ }
+}
\ No newline at end of file