package ch.retorte.intervalmusiccompositor.ui.bpm;
import ch.retorte.intervalmusiccompositor.audiofile.IAudioFile;
import ch.retorte.intervalmusiccompositor.commons.MessageFormatBundle;
import ch.retorte.intervalmusiccompositor.messagebus.DebugMessage;
import ch.retorte.intervalmusiccompositor.spi.MusicListControl;
import ch.retorte.intervalmusiccompositor.spi.messagebus.MessageProducer;
import ch.retorte.intervalmusiccompositor.ui.mainscreen.MainScreenController;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.stage.Stage;
import java.io.IOException;
import java.text.DecimalFormat;
/**
* Allows to pre-hear a track and determine its BPM (beats per minute) via tapping.
*/
public class BpmWindow {
//---- Static
private static final String LAYOUT_FILE = "/layouts/BpmWindow.fxml";
//---- FX Widgets
@FXML
private Button playButton;
@FXML
private Button stopButton;
@FXML
private Label bpm;
@FXML
private Button tapButton;
@FXML
private Spinner<Integer> selectedBPMSpinner;
@FXML
private Button cancelButton;
@FXML
private Button okButton;
//---- Fields
private Parent parent;
private final MessageFormatBundle messageFormatBundle;
private final MusicListControl musicListControl;
private MessageProducer messageProducer;
private final IAudioFile audioFile;
private final int index;
private SimpleIntegerProperty tappedBpm = new SimpleIntegerProperty(0);
private SimpleStringProperty tappedBpmString = new SimpleStringProperty();
private SimpleIntegerProperty selectedBpm = new SimpleIntegerProperty(0);
private Tap tap;
//---- Constructor
public BpmWindow(MessageFormatBundle messageFormatBundle, MusicListControl musicListControl, MessageProducer messageProducer, IAudioFile audioFile, int index) {
this.messageFormatBundle = messageFormatBundle;
this.musicListControl = musicListControl;
this.messageProducer = messageProducer;
this.audioFile = audioFile;
this.index = index;
loadFXML();
initialize();
}
//---- Methods
private void loadFXML() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(LAYOUT_FILE), messageFormatBundle.getBundle());
fxmlLoader.setController(this);
try {
parent = fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public void show() {
Stage stage = new Stage();
stage.setTitle(messageFormatBundle.getString("ui.determine_bpm.title"));
stage.setScene(new Scene(parent));
stage.setResizable(true);
stage.show();
}
private void initialize() {
initializeMusicControl();
initializeBpmIndicator();
initializeTap();
initializeTapButton();
initializeSelectedBpmSpinner();
initializeButtons();
initializeFieldsWithExistingBpmValue();
}
private void initializeMusicControl() {
playButton.setOnAction(event -> {
playTrack(index);
addDebugMessage("Pressed playback button for track " + audioFile.getDisplayName());
});
stopButton.setOnAction(event -> {
stopPlayingMusic();
addDebugMessage("Pressed stop playback button for track " + audioFile.getDisplayName());
});
}
private void stopPlayingMusic() {
musicListControl.stopMusic();
}
private void initializeBpmIndicator() {
tappedBpm.addListener((observable, oldValue, newValue) -> tappedBpmString.set(String.valueOf(newValue)));
bpm.textProperty().bind(tappedBpmString);
}
private void initializeTap() {
tap = new Tap(messageProducer, messageFormatBundle);
}
private void initializeTapButton() {
tapButton.setOnAction(event -> tap());
}
private void tap() {
TapEvent tapEvent = this.tap.tap();
tappedBpm.setValue(tapEvent.getBpm());
if (tapEvent.isConverged()) {
selectedBpm.setValue(tapEvent.getBpm());
selectedBPMSpinner.getValueFactory().setValue(tapEvent.getBpm());
}
}
private void addDebugMessage(String message) {
messageProducer.send(new DebugMessage(this, message));
}
private void initializeSelectedBpmSpinner() {
selectedBPMSpinner.valueProperty().addListener((observable, oldValue, newValue) -> {
selectedBpm.setValue(newValue);
messageProducer.send(new DebugMessage(BpmWindow.this, "Changed contents of field 'selectedBpm' from: '" + oldValue + "' to '" + newValue + "'"));
});
}
private void initializeButtons() {
cancelButton.setOnAction(event -> close());
okButton.setOnAction(event -> {
updateBpmWith(index, selectedBpm.get());
close();
});
}
private void initializeFieldsWithExistingBpmValue() {
if (audioFile != null && audioFile.hasBpm()) {
tappedBpm.setValue(audioFile.getBpm());
selectedBpm.setValue(audioFile.getBpm());
}
}
protected void playTrack(int index) {
getMusicListControl().playMusicTrack(index);
}
protected void updateBpmWith(int index, int bpm) {
getMusicListControl().setMusicBpm(index, bpm);
}
MusicListControl getMusicListControl() {
return musicListControl;
}
private void close() {
stopPlayingMusic();
parent.getScene().getWindow().hide();
}
}