Created
December 1, 2012 23:24
-
-
Save jewelsea/4185890 to your computer and use it in GitHub Desktop.
JavaFX metronome with start/stop and tempo controls.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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