diff --git a/src/javax/microedition/media/javazoom/jl/player/MPEGPlayer.java b/src/javax/microedition/media/javazoom/jl/player/MPEGPlayer.java index 298d46e2..87496e92 100644 --- a/src/javax/microedition/media/javazoom/jl/player/MPEGPlayer.java +++ b/src/javax/microedition/media/javazoom/jl/player/MPEGPlayer.java @@ -29,6 +29,7 @@ import javazoom.jl.decoder.JavaLayerException; import javazoom.jl.decoder.SampleBuffer; + import javax.microedition.media.Player; import org.recompile.mobile.Mobile; /** @@ -187,13 +188,7 @@ public boolean play(int frames) throws JavaLayerException, InterruptedException synchronized (this) { /* Only set complete flag if not entering a new loop */ - if(loopCount == 0) { complete = (!closed); } - else - { - loopCount--; - complete = false; - reset(); - } + complete = (!closed); } } } @@ -232,16 +227,6 @@ public void reset() synchronized (dataStream) { dataStream.notifyAll(); } Mobile.log(Mobile.LOG_DEBUG, MPEGPlayer.class.getPackage().getName() + "." + MPEGPlayer.class.getSimpleName() + ": " + "reset done"); - - /* If looping has been set as more than 0, consume a count, and begin playing again. */ - if(loopCount > 0) - { - try - { - loopCount--; - play(Integer.MAX_VALUE); - } catch (Exception e) { Mobile.log(Mobile.LOG_ERROR, MPEGPlayer.class.getPackage().getName() + "." + MPEGPlayer.class.getSimpleName() + ": " + "MPEGPlayer: Failed to loop player:"+e.getMessage()); } - } } public int getBitrate() { @@ -430,13 +415,13 @@ public long getDuration() { double duration = 0; - try { duration = (double) ((data.length * 8 * 1000D) / getBitrate()); } - catch (Exception e){ Mobile.log(Mobile.LOG_ERROR, MPEGPlayer.class.getPackage().getName() + "." + MPEGPlayer.class.getSimpleName() + ": " + "Couldn't get duration:" + e.getMessage()); return 0;} + try { duration = (double) ((data.length * 8 * 1_000D) / getBitrate()); } + catch (Exception e){ Mobile.log(Mobile.LOG_ERROR, MPEGPlayer.class.getPackage().getName() + "." + MPEGPlayer.class.getSimpleName() + ": " + "Couldn't get duration:" + e.getMessage()); return Player.TIME_UNKNOWN;} return (long) duration; } - public void loop(int count) + public void setLoopCount(int count) { if(count == -1) { loopCount = Integer.MAX_VALUE; } // Loop "indefinitely" else { loopCount = count; } @@ -447,4 +432,9 @@ public boolean isRunning() if(!paused && !closed && !reset) { return true; } else { return false; } } + + // Looping is handled by PlatformPlayer + public int getLoopCount() { return loopCount; } + + public void decreaseLoopCount() { loopCount--; } } diff --git a/src/org/recompile/mobile/PlatformPlayer.java b/src/org/recompile/mobile/PlatformPlayer.java index 380d5a4a..d28cfc71 100644 --- a/src/org/recompile/mobile/PlatformPlayer.java +++ b/src/org/recompile/mobile/PlatformPlayer.java @@ -659,7 +659,14 @@ public void realize() public void prefetch() { state = Player.PREFETCHED; } public void start() - { + { + /* + * The fact these null checks have to be littered on MP3Player gives me a bad feeling... + * Maybe it's because it doesn't at all work like wav and midi players, which are + * integrated into Java Sound and don't need a thread object to play non-blocking. + */ + if(mp3Player == null) { return; } + try { playerThread = new Thread(() -> @@ -667,11 +674,26 @@ public void start() try { if(getMediaTime() >= getDuration()) { setMediaTime(0); } - mp3Player.play(); + mp3Player.play(); // This is thread-blocking, so the code below only executes after this has finished. + + /* + * Check if mp3Player is still valid and exit early, since this thread can be + * interrupted and the player can also be closed abruptly. + */ + if (mp3Player == null) { return; } - //mp3Player.play(); is thread-blocking, so having this directly after it is safe. if (!Thread.currentThread().isInterrupted()) { + if(mp3Player.getLoopCount() > 0) + { + while(mp3Player.getLoopCount() > 0) + { + mp3Player.decreaseLoopCount(); + mp3Player.reset(); + mp3Player.play(); + } + } + // After all loops (if any) are done, notify END_OF_MEDIA notifyListeners(PlayerListener.END_OF_MEDIA, getMediaTime()); state = Player.PREFETCHED; } @@ -684,11 +706,11 @@ public void start() state = Player.STARTED; notifyListeners(PlayerListener.STARTED, getMediaTime()); } catch (Exception e) { Mobile.log(Mobile.LOG_ERROR, PlatformPlayer.class.getPackage().getName() + "." + PlatformPlayer.class.getSimpleName() + ": " + "Couldn't start mpeg player:" + e.getMessage()); } - } public void stop() { + if(mp3Player == null) { return; } mp3Player.stop(); if (playerThread != null && playerThread.isAlive()) { playerThread.interrupt(); } @@ -714,8 +736,8 @@ public void setLoopCount(int count) * it appears that count = 1 means no loop at all, at least based * on Gameloft games that set effects and some music with count = 1 */ - if(count == Clip.LOOP_CONTINUOUSLY) { mp3Player.loop(count); } - else { mp3Player.loop(count-1); } + if(count == Clip.LOOP_CONTINUOUSLY) { mp3Player.setLoopCount(count); } + else { mp3Player.setLoopCount(count-1); } } public long setMediaTime(long now)