Reputation: 39
I setVolume to 0.0, and then change the volume bit by bit in the while loop. Yet, the volume jumps from 0.0 to 1.0 ? How can I change the volume smoothly? I tried
public class EngineSound extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
mp3File = new File(metronom);
media = new Media(mp3File.toURI().toURL().toString());
mediaPlayer = new MediaPlayer(media);
mediaView = new MediaView(mediaPlayer);
mediaPlayer.play();
mediaPlayer.setVolume(0.0);
slider = new Slider();
slider.valueProperty().addListener(new ChangeListener<Number>() {
public void changed(ObservableValue<? extends Number> ov, Number old_val, Number new_val) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
mediaPlayer.setVolume(slider.getValue());
}
});
}
});
double count = 1;
while (count != 101) {
for (int i = 0; i < 100000000; i++) {
}
slider.setValue(count / 100);
count++;
System.out.println(mediaPlayer.getVolume());
}
}
}
Upvotes: 3
Views: 6977
Reputation: 159576
There are a few things wrong with your code.
Instead of using EventQueue.invokeLater
to execute on the Swing thread, use Platform.runLater to execute on the JavaFX thread.
Your program only makes use of JavaFX controls, so don't run anything on the Swing thread.
ChangeListener
.Even though using EventQueue.invokeLater
is wrong, in this case you don't even need to Platform.runLater
either as only the JavaFX application thread should be modifying the Slider value anyway. There is a rule you can see in the JavaFX Node documentation:
An application must attach nodes to a Scene, and modify nodes that are already attached to a Scene, on the JavaFX Application Thread.
The loop where you count to one hundred million will just block the application thread resulting in a frozen UI as control will never be returned to the framework to update the UI.
Once you set a value on a UI control, you must return control back to the JavaFX framework to allow the value change to be reflected in the control and to the user.
Try the following code which addresses all of the above issues through the use of Timeline and Binding.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.event.*;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.media.*;
import javafx.stage.Stage;
import javafx.util.Duration;
public class EngineSound extends Application {
private static final String MEDIA_URL =
"http://download.oracle.com/otndocs/products/javafx/oow2010-2.flv";
private static final Duration FADE_DURATION = Duration.seconds(2.0);
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) throws Exception {
final MediaPlayer mediaPlayer = new MediaPlayer(
new Media(
MEDIA_URL
)
);
final MediaView mediaView = new MediaView(mediaPlayer);
HBox layout = new HBox(5);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
layout.getChildren().addAll(
createVolumeControls(mediaPlayer),
mediaView
);
stage.setScene(new Scene(layout, 650, 230));
stage.show();
mediaPlayer.play();
}
public Region createVolumeControls(final MediaPlayer mediaPlayer) {
final Slider volumeSlider = new Slider(0, 1, 0);
volumeSlider.setOrientation(Orientation.VERTICAL);
mediaPlayer.volumeProperty().bindBidirectional(volumeSlider.valueProperty());
final Timeline fadeInTimeline = new Timeline(
new KeyFrame(
FADE_DURATION,
new KeyValue(mediaPlayer.volumeProperty(), 1.0)
)
);
final Timeline fadeOutTimeline = new Timeline(
new KeyFrame(
FADE_DURATION,
new KeyValue(mediaPlayer.volumeProperty(), 0.0)
)
);
Button fadeIn = new Button("Fade In");
fadeIn.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent t) {
fadeInTimeline.play();
}
});
fadeIn.setMaxWidth(Double.MAX_VALUE);
Button fadeOut = new Button("Fade Out");
fadeOut.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent t) {
fadeOutTimeline.play();
}
});
fadeOut.setMaxWidth(Double.MAX_VALUE);
VBox controls = new VBox(5);
controls.getChildren().setAll(
volumeSlider,
fadeIn,
fadeOut
);
controls.setAlignment(Pos.CENTER);
VBox.setVgrow(volumeSlider, Priority.ALWAYS);
controls.disableProperty().bind(
Bindings.or(
Bindings.equal(Timeline.Status.RUNNING, fadeInTimeline.statusProperty()),
Bindings.equal(Timeline.Status.RUNNING, fadeOutTimeline.statusProperty())
)
);
return controls;
}
}
The code controls a Video, but making it do audio only is just a matter of setting the Media URL to an audio only format such as mp3 or aac.
Upvotes: 3