Skip to content

Instantly share code, notes, and snippets.

@jewelsea
Created December 1, 2012 23:24
Show Gist options
  • Select an option

  • Save jewelsea/4185890 to your computer and use it in GitHub Desktop.

Select an option

Save jewelsea/4185890 to your computer and use it in GitHub Desktop.
JavaFX metronome with start/stop and tempo controls.
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.Image;
import javafx.scene.layout.HBox;
import javafx.scene.media.AudioClip;
import javafx.scene.text.TextAlignment;
import javafx.stage.Stage;
import javafx.util.Duration;
import javafx.util.StringConverter;
import java.util.Date;
/** Creates a metronome with start/stop and tempo controls */
public class TickTock extends Application {
private static final double INITIAL_TEMPO = 100;
public static void main(String[] args) { launch(args); }
@Override public void start(final Stage stage) {
stage.setTitle("Metronome");
stage.setScene(
new Scene(
new MetronomeView(new Metronome(INITIAL_TEMPO))
)
);
stage.show();
}
}
/** Provides a UI to control a metronome which ticks on pulses generated by a tempo in beats per minute. */
class MetronomeView extends HBox {
private static final double MIN_TEMPO = 40;
private static final double MAX_TEMPO = 180;
public MetronomeView(Metronome metronome) {
super(10);
getChildren().addAll(
createStartStopControl(metronome),
createTempoControl(metronome),
createTempoReadout(metronome)
);
setAlignment(Pos.CENTER);
setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
}
protected Label createTempoReadout(Metronome metronome) {
Label label = new Label();
label.textProperty().bind(Bindings.format("%.0f", metronome.getPulsar().tempoProperty()));
label.setPrefWidth(25);
label.setAlignment(Pos.CENTER_RIGHT);
return label;
}
protected Slider createTempoControl(Metronome metronome) {
final Slider slider = new Slider(MIN_TEMPO, MAX_TEMPO, metronome.getPulsar().getTempo());
slider.valueProperty().bindBidirectional(metronome.getPulsar().tempoProperty());
slider.setMajorTickUnit(1);
slider.setMinorTickCount(0);
slider.setSnapToTicks(true);
return slider;
}
protected ToggleButton createStartStopControl(final Metronome metronome) {
final ToggleButton playControl = new ToggleButton("Start");
playControl.setPrefWidth(60);
playControl.selectedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observableValue, Boolean wasSelected, Boolean selected) {
if (selected) {
metronome.getPulsar().start();
playControl.setText("Stop");
} else {
metronome.getPulsar().stop();
playControl.setText("Start");
}
}
});
return playControl;
}
}
/** ticks according to a tempo in beats per minute controlled by the associated pulsar. */
class Metronome {
private final AudioClip tick = new AudioClip("http://www.denhaku.com/r_box/sr16/sr16perc/losticks.wav");
private final Pulsar pulsar;
public Metronome(final double initialTempo) {
// the first time the audioclip is played, there is a delay before you hear it,
// so play with zero volume now as to make sure it is ready to play when straight away when needed.
tick.play(0);
pulsar = new Pulsar(initialTempo, new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent actionEvent) {
tick.play();
}
});
}
public Pulsar getPulsar() {
return pulsar;
}
}
/** handles events according to a tempo in beats per minute. */
class Pulsar {
private final DoubleProperty tempo = new SimpleDoubleProperty(100);
private final Timeline timeline = new Timeline();
public Pulsar(final double initialTempo, final EventHandler<ActionEvent> pulseHandler) {
timeline.setCycleCount(Animation.INDEFINITE);
timeline.getKeyFrames().setAll(
new KeyFrame(Duration.ZERO, pulseHandler),
new KeyFrame(Duration.minutes(1), null)
);
timeline.rateProperty().bind(tempo);
setTempo(initialTempo);
}
public DoubleProperty tempoProperty() {
return tempo;
}
public double getTempo() {
return tempo.get();
}
public void setTempo(double newTempo) {
tempo.set(newTempo);
}
public void start() {
timeline.play();
}
public void stop() {
timeline.stop();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment