Reputation: 25
I'm still fairly new to threading, and I'm working with javaFX for the first time, so this could be a double whammy! My main issue is with the threading, I believe. I am wanting to crossfade two mediaplayer audios on the skip button. So, I believe that I created a thread to crossfade the audios over two seconds, and then I should end the thread. That's what I "think" I'm doing. However, when the program runs, it usually freezes after the second skip. Most of the code I'm using for javaFX was from tutorials and works as intended before I tried the crossfade. Any advice will be put to great use, TIA! CrossFade class:
import javafx.scene.media.MediaPlayer;
public class CrossFade extends Thread
{
private MediaPlayer mp1;
private MediaPlayer mp2;
private double currentVol;
public CrossFade(MediaPlayer mp1, MediaPlayer mp2)
{
this.mp1 = mp1;
this.mp2 = mp2;
currentVol = mp1.getVolume();
System.out.println("Vol: " + currentVol);
}
@Override
public void run() {
mp2.setVolume(0);
mp2.play();
//20 increments in volume, every 100ms
for (int i = 0; i < 20; i ++)
{
mp1.setVolume(mp1.getVolume()-currentVol/20);
mp2.setVolume(mp2.getVolume()+currentVol/20);
try
{
//sleep 100 ms
Thread.sleep(100);
}
catch (InterruptedException e)
{
System.out.println("Unable to sleep on xFade");
e.printStackTrace();
}
}
mp1.stop();
//this.interrupt();
}
}
Audio class:
import javafx.scene.media.MediaPlayer;
public class Audio{
public static void crossfade(MediaPlayer mp1, MediaPlayer mp2)
{
Thread xFade = new Thread(new CrossFade(mp1,mp2));
xFade.start();
}
}
code for the skip button:
skip.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent actionEvent) {
final MediaPlayer curPlayer = mediaView.getMediaPlayer();
MediaPlayer nextPlayer = players.get((players.indexOf(curPlayer) + 1) % players.size());
mediaView.setMediaPlayer(nextPlayer);
curPlayer.currentTimeProperty().removeListener(progressChangeListener);
//calls the crossfade in audio class (supposed to start that thread)
Audio.crossfade(curPlayer,nextPlayer);
//these were the "old" stop and play calls to the media
//curPlayer.stop();
//nextPlayer.play();
}
});
Upvotes: 0
Views: 1790
Reputation: 209330
In general, you shouldn't change anything that's part of the UI from outside the FX Application Thread. I haven't worked much with MediaPlayers, but I believe they play by the same rules.
You can probably simplify this a lot by using the Animation API. A PauseTransition
can be used to manage each pause, and you can "play" these in sequence using a SequentialTransition
. This will basically manage all the threading for you. Something like (this is not tested...)
public class Audio{
public static void crossfade(MediaPlayer mp1, MediaPlayer mp2)
{
double currentVol = mp1.getVolume();
mp2.setVolume(0);
mp2.play();
SequentialTransition crossfade = new SequentialTransition();
for (int i=0; i<20 i++) {
PauseTransition pause = new PauseTransition(Duration.millis(100));
pause.setOnFinished(event -> {
mp1.setVolume(mp1.getVolume()-currentVol/20);
mp2.setVolume(mp2.getVolume()+currentVol/20);
});
crossfade.getChildren().add(pause);
}
crossfade.setOnFinished(event -> mp1.stop());
crossfade.play();
}
}
Of course, if you're going to use the Animation API, then you may as well use it in a way that does everything "smoothly", instead of in discrete steps:
public class Audio {
public static void crossfade(MediaPlayer mp1, MediaPlayer mp2) {
final double currentVolume = mp1.getVolume();
mp2.setVolume(0);
mp2.play();
Timeline crossfade = new Timeline(new KeyFrame(Duration.seconds(2),
new KeyValue(mp1.volumeProperty(), 0),
new KeyValue(mp2.volumeProperty(), currentVolume)));
crossfade.setOnFinished(event -> mp1.stop());
crossfade.play();
}
}
Update: If you are still using JavaFX 2.2, replace the lambda expression (crossfade.setOnFinished(event -> mp1.stop());
) with an inner class:
crossfade.setOnFinished(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
mp1.stop();
}
});
You will also need to declare mp1
as final
:
public static void crossfade(final MediaPlayer mp1, final MediaPlayer mp2) { ... }
Upvotes: 2