|
@@ -1,5 +1,5 @@
|
|
|
/*
|
|
|
- * UltrasonicHelix is a Java Swing-based GUI frontend for SoundHelix.
|
|
|
+ * MadHelix is a Java Swing-based GUI frontend for SoundHelix.
|
|
|
* Copyright (C) 2018 UltrasonicMadness
|
|
|
*
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
@@ -32,34 +32,41 @@ import java.awt.GridBagLayout;
|
|
|
import java.awt.GridBagConstraints;
|
|
|
import java.awt.Insets;
|
|
|
|
|
|
+// AWT events
|
|
|
+import java.awt.event.ActionEvent;
|
|
|
+import java.awt.event.InputEvent;
|
|
|
+import java.awt.event.KeyEvent;
|
|
|
+
|
|
|
// Swing widgets
|
|
|
+import javax.swing.AbstractAction;
|
|
|
+import javax.swing.ImageIcon;
|
|
|
import javax.swing.JButton;
|
|
|
-import javax.swing.JCheckBox;
|
|
|
import javax.swing.JComboBox;
|
|
|
import javax.swing.JFrame;
|
|
|
import javax.swing.JLabel;
|
|
|
import javax.swing.JOptionPane;
|
|
|
import javax.swing.JPanel;
|
|
|
import javax.swing.JTextField;
|
|
|
+import javax.swing.KeyStroke;
|
|
|
|
|
|
// Other Swing modules
|
|
|
import javax.swing.SwingUtilities;
|
|
|
|
|
|
-// UltrasonicHelix imports
|
|
|
+// MadHelix imports
|
|
|
import org.ultrasonicmadness.madhelix.dialogs.AboutBox;
|
|
|
+import org.ultrasonicmadness.madhelix.dialogs.PrefsDialog;
|
|
|
import org.ultrasonicmadness.madhelix.utils.PathUtils;
|
|
|
|
|
|
-public class MadHelix extends JFrame {
|
|
|
-
|
|
|
+
|
|
|
+public class MadHelix extends JFrame
|
|
|
+{
|
|
|
// SoundHelix
|
|
|
private final File stylesDir = new File(PathUtils.getStylesDirPath()); // Styles directory
|
|
|
- private SongContext currentSong; // Current song being played
|
|
|
- private boolean startHelix = false; // If this is set to true, SoundHelix will be called to play the specified music and it will be set back to false.
|
|
|
- private boolean helixPlaying = false; // Set to true while SoundHelix is playing music, false otherwise.
|
|
|
+ private SongContext currentSong = null; // Current song being played
|
|
|
|
|
|
- // MIDI file output
|
|
|
- private final File midiOutDir = new File(PathUtils.getMidiDirPath()); // MIDI file output directory
|
|
|
- private boolean exportMidi = true;
|
|
|
+ // Preferences
|
|
|
+ private File midiOutDir = new File(PathUtils.getMidiDirPath());
|
|
|
+ private boolean exportingMidi = false;
|
|
|
|
|
|
// Main 3 panels: status, options, controls
|
|
|
private JPanel statusPanel = new JPanel();
|
|
@@ -69,6 +76,7 @@ public class MadHelix extends JFrame {
|
|
|
// Components
|
|
|
private JPanel mainPanel = new JPanel();
|
|
|
private AboutBox aboutBox = new AboutBox(this);
|
|
|
+ private PrefsDialog prefsDialog = new PrefsDialog(this);
|
|
|
|
|
|
// Status labels (spaces included so that the layout works)
|
|
|
private JLabel playStatus = new JLabel(" ");
|
|
@@ -81,35 +89,35 @@ public class MadHelix extends JFrame {
|
|
|
private JTextField songNameEntry = new JTextField(24);
|
|
|
private JLabel styleLabel = new JLabel("Style");
|
|
|
private JComboBox<String> styleChoice = new JComboBox<>();
|
|
|
- private JCheckBox fileExportCheckBox = new JCheckBox("Export files when playing", exportMidi);
|
|
|
+
|
|
|
+ // Icons
|
|
|
+ private ImageIcon playIcon = new ImageIcon(this.getClass()
|
|
|
+ .getResource("/icons/media-playback-start.png"), "Play");
|
|
|
+
|
|
|
+ private ImageIcon stopIcon = new ImageIcon(this.getClass()
|
|
|
+ .getResource("/icons/media-playback-stop.png"), "Stop");
|
|
|
+
|
|
|
+ private ImageIcon prefsIcon = new ImageIcon(this.getClass()
|
|
|
+ .getResource("/icons/preferences-system.png"), "Preferences");
|
|
|
+
|
|
|
+ private ImageIcon aboutIcon = new ImageIcon(this.getClass()
|
|
|
+ .getResource("/icons/help-about.png"), "About MadHelix");
|
|
|
|
|
|
// Control buttons
|
|
|
- private JButton playButton = new JButton("\u25B6");
|
|
|
- private JButton stopButton = new JButton("\u25A0");
|
|
|
- private JButton aboutButton = new JButton("About");
|
|
|
+ private JButton playButton = new JButton(playIcon);
|
|
|
+ private JButton stopButton = new JButton(stopIcon);
|
|
|
+ private JButton prefsButton = new JButton(prefsIcon);
|
|
|
+ private JButton aboutButton = new JButton(aboutIcon);
|
|
|
|
|
|
// SoundHelix thread.
|
|
|
- private Thread soundHelix = new Thread(new Runnable() {
|
|
|
- public void run() {
|
|
|
- // This thread runs as long as UltrasonicHelix does.
|
|
|
- while (true) {
|
|
|
- if (startHelix) {
|
|
|
- // Since this command has been started, set this to false so it isn't started repeatedly.
|
|
|
- startHelix = false;
|
|
|
- playSong();
|
|
|
-
|
|
|
- } else {
|
|
|
- // Without this, the thread does not respond to startHelix being set to true.
|
|
|
- // There is probably a much better way to do this.
|
|
|
- System.out.print("");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private void playSong() {
|
|
|
- helixPlaying = true;
|
|
|
-
|
|
|
+ private Thread soundHelix = null;
|
|
|
+
|
|
|
+ private Thread genSoundHelixThread()
|
|
|
+ {
|
|
|
+ Thread newHelixThread = new Thread(() ->
|
|
|
+ {
|
|
|
setStatus("Loading SoundHelix");
|
|
|
+ this.setTitle("Loading SoundHelix - MadHelix");
|
|
|
|
|
|
// The buttons don't function at this time, so disable them while SoundHelix loads.
|
|
|
setControlsEnabled(false);
|
|
@@ -120,26 +128,33 @@ public class MadHelix extends JFrame {
|
|
|
File styleFile = new File(stylesDir.toString() + File.separator +
|
|
|
styleName + ".xml");
|
|
|
|
|
|
- try {
|
|
|
+ try
|
|
|
+ {
|
|
|
// If a song name is specified, use it, otherwise generate a random number and use it.
|
|
|
- if (songName != null && !songName.equals("")) {
|
|
|
+ if (songName != null && !songName.equals(""))
|
|
|
+ {
|
|
|
currentSong = SongUtils.generateSong(
|
|
|
styleFile.toURI().toURL(), songName);
|
|
|
- } else {
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
// This is the same way SoundHelix uses the random number generator.
|
|
|
currentSong = SongUtils.generateSong(
|
|
|
styleFile.toURI().toURL(), new Random().nextLong());
|
|
|
}
|
|
|
|
|
|
- if (exportMidi) {
|
|
|
-
|
|
|
- try {
|
|
|
+ if (exportingMidi)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
MidiPlayer midiPlayer = (MidiPlayer)currentSong.getPlayer();
|
|
|
|
|
|
midiPlayer.setMidiFilename(midiOutDir + File.separator +
|
|
|
currentSong.getSongName() + "-" + styleName +
|
|
|
".mid");
|
|
|
- } catch (Exception x) {
|
|
|
+ }
|
|
|
+ catch (Exception x)
|
|
|
+ {
|
|
|
JOptionPane.showMessageDialog(null, x.getMessage().toString(),
|
|
|
"Could not set MIDI filename.", JOptionPane.ERROR_MESSAGE);
|
|
|
}
|
|
@@ -149,18 +164,24 @@ public class MadHelix extends JFrame {
|
|
|
setStatus("Playing",
|
|
|
currentSong.getSongName(), "Style", styleName);
|
|
|
|
|
|
+ this.setTitle(currentSong.getSongName() + " - MadHelix");
|
|
|
+
|
|
|
// Now SoundHelix has loaded, enable the buttons again
|
|
|
setControlsEnabled(true);
|
|
|
|
|
|
// Play the song
|
|
|
currentSong.getPlayer().play(currentSong);
|
|
|
- } catch (Exception x) { // In case SoundHelix encounters a problem
|
|
|
+ }
|
|
|
+ catch (Exception x) // In case SoundHelix encounters a problem
|
|
|
+ {
|
|
|
JOptionPane.showMessageDialog(null, x.getMessage().toString(),
|
|
|
"Exception occurred", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
|
// In case an error has occured, enable the buttons again
|
|
|
setControlsEnabled(true);
|
|
|
- } catch (NoClassDefFoundError r) { // In case SoundHelix.jar is not found
|
|
|
+ }
|
|
|
+ catch (NoClassDefFoundError r) // In case SoundHelix.jar is not found
|
|
|
+ {
|
|
|
JOptionPane.showMessageDialog(null,
|
|
|
"Cannot find module " + r.getMessage(), "Error",
|
|
|
JOptionPane.ERROR_MESSAGE);
|
|
@@ -170,19 +191,22 @@ public class MadHelix extends JFrame {
|
|
|
}
|
|
|
|
|
|
// The song has finished at this point, so update variables and status accordingly.
|
|
|
- helixPlaying = false;
|
|
|
setStatus("Ready");
|
|
|
- }
|
|
|
- });
|
|
|
+ this.setTitle("MadHelix");
|
|
|
+ });
|
|
|
+
|
|
|
+ newHelixThread.setDaemon(true); // Closes SoundHelix if MadHelix is closed.
|
|
|
+ return newHelixThread;
|
|
|
+ }
|
|
|
|
|
|
- public MadHelix() {
|
|
|
+ public MadHelix()
|
|
|
+ {
|
|
|
initMadHelix();
|
|
|
}
|
|
|
|
|
|
- private void initMadHelix() {
|
|
|
- // Start SoundHelix thread
|
|
|
- soundHelix.setDaemon(true); // Closes SoundHelix if UltrasonicHelix is closed.
|
|
|
- soundHelix.start();
|
|
|
+ private void initMadHelix()
|
|
|
+ {
|
|
|
+ setUpKeyBindings();
|
|
|
|
|
|
addComponents();
|
|
|
|
|
@@ -191,15 +215,18 @@ public class MadHelix extends JFrame {
|
|
|
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
|
|
this.setResizable(false);
|
|
|
this.pack();
|
|
|
+ this.setLocationRelativeTo(null);
|
|
|
this.setVisible(true);
|
|
|
}
|
|
|
|
|
|
- private void determineStatus() {
|
|
|
+ private void determineStatus()
|
|
|
+ {
|
|
|
// Assume program is ready , then check for errors
|
|
|
setStatus("Ready");
|
|
|
|
|
|
// If no styles are found, disable the buttons and display an alert.
|
|
|
- if (stylesDir.list() == null || stylesDir.list().length == 0) {
|
|
|
+ if (stylesDir.list() == null || stylesDir.list().length == 0)
|
|
|
+ {
|
|
|
setStatus("No styles available.");
|
|
|
setControlsEnabled(false);
|
|
|
|
|
@@ -211,13 +238,58 @@ public class MadHelix extends JFrame {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private void addComponents() {
|
|
|
+ private void setUpKeyBindings()
|
|
|
+ {
|
|
|
+ // Play is CTRL+P or return
|
|
|
+ playButton.getInputMap(JButton.WHEN_IN_FOCUSED_WINDOW)
|
|
|
+ .put(KeyStroke.getKeyStroke(KeyEvent.VK_P,
|
|
|
+ InputEvent.CTRL_DOWN_MASK), "play");
|
|
|
+
|
|
|
+ playButton.getActionMap().put("play", new AbstractAction()
|
|
|
+ {
|
|
|
+ @Override
|
|
|
+ public void actionPerformed(ActionEvent ev)
|
|
|
+ {
|
|
|
+ playSong();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ playButton.getInputMap(JButton.WHEN_IN_FOCUSED_WINDOW)
|
|
|
+ .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "play");
|
|
|
+
|
|
|
+ playButton.getActionMap().put("play", new AbstractAction()
|
|
|
+ {
|
|
|
+ @Override
|
|
|
+ public void actionPerformed(ActionEvent ev)
|
|
|
+ {
|
|
|
+ playSong();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // Stop is CTRL+S
|
|
|
+ stopButton.getInputMap(JButton.WHEN_IN_FOCUSED_WINDOW)
|
|
|
+ .put(KeyStroke.getKeyStroke(KeyEvent.VK_S,
|
|
|
+ InputEvent.CTRL_DOWN_MASK), "stop");
|
|
|
+
|
|
|
+ stopButton.getActionMap().put("stop", new AbstractAction()
|
|
|
+ {
|
|
|
+ @Override
|
|
|
+ public void actionPerformed(ActionEvent ev)
|
|
|
+ {
|
|
|
+ stopSong();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private void addComponents()
|
|
|
+ {
|
|
|
// Set panel layout
|
|
|
mainPanel.setLayout(new GridBagLayout());
|
|
|
|
|
|
// Set up constraints
|
|
|
GridBagConstraints mainConstraints = new GridBagConstraints();
|
|
|
mainConstraints.fill = GridBagConstraints.HORIZONTAL;
|
|
|
+ mainConstraints.weightx = 1;
|
|
|
|
|
|
addStatusLabels();
|
|
|
addOptions();
|
|
@@ -236,7 +308,8 @@ public class MadHelix extends JFrame {
|
|
|
this.add(mainPanel);
|
|
|
}
|
|
|
|
|
|
- private void addStatusLabels() {
|
|
|
+ private void addStatusLabels()
|
|
|
+ {
|
|
|
determineStatus();
|
|
|
|
|
|
// Set up layout
|
|
@@ -264,7 +337,8 @@ public class MadHelix extends JFrame {
|
|
|
|
|
|
// Set status to the four passed strings
|
|
|
private void setStatus(String newPlayStatus, String newPlayStatusInfo,
|
|
|
- String newStyleStatus, String newStyleStatusInfo) {
|
|
|
+ String newStyleStatus, String newStyleStatusInfo)
|
|
|
+ {
|
|
|
|
|
|
// Set all label text to the passed strings.
|
|
|
playStatus.setText(newPlayStatus);
|
|
@@ -274,26 +348,22 @@ public class MadHelix extends JFrame {
|
|
|
}
|
|
|
|
|
|
// Set status to the passed string. The others are reset to blank.
|
|
|
- private void setStatus(String newPlayStatus) {
|
|
|
+ private void setStatus(String newPlayStatus)
|
|
|
+ {
|
|
|
setStatus(newPlayStatus, " ", " ", " ");
|
|
|
}
|
|
|
|
|
|
- private void addOptions() {
|
|
|
+ private void addOptions()
|
|
|
+ {
|
|
|
// Get list of styles for the combo box.
|
|
|
String[] styleList = PathUtils.getStyles();
|
|
|
|
|
|
// Add styles to style choice combo box
|
|
|
- for (String style : styleList) {
|
|
|
+ for (String style : styleList)
|
|
|
+ {
|
|
|
styleChoice.addItem(style);
|
|
|
}
|
|
|
|
|
|
- fileExportCheckBox.addActionListener(ev -> {
|
|
|
- exportMidi = fileExportCheckBox.isSelected();
|
|
|
- });
|
|
|
-
|
|
|
- fileExportCheckBox.setToolTipText("The files are exported to the midi directory " +
|
|
|
- "in the same location as the JAR application.");
|
|
|
-
|
|
|
// Set up layout
|
|
|
optionsPanel.setLayout(new GridBagLayout());
|
|
|
|
|
@@ -303,33 +373,47 @@ public class MadHelix extends JFrame {
|
|
|
|
|
|
// Add song information
|
|
|
constraints.gridy = 0;
|
|
|
+ constraints.weightx = 0;
|
|
|
optionsPanel.add(songNameLabel, constraints);
|
|
|
+ constraints.weightx = 1;
|
|
|
optionsPanel.add(songNameEntry, constraints);
|
|
|
|
|
|
// Add style label
|
|
|
constraints.gridy = 1;
|
|
|
+ constraints.weightx = 0;
|
|
|
optionsPanel.add(styleLabel, constraints);
|
|
|
+ constraints.weightx = 1;
|
|
|
optionsPanel.add(styleChoice, constraints);
|
|
|
-
|
|
|
- // Add file export checkbox
|
|
|
- constraints.gridy = 2;
|
|
|
- constraints.gridwidth = 2;
|
|
|
- optionsPanel.add(fileExportCheckBox, constraints);
|
|
|
}
|
|
|
|
|
|
- private void addControls() {
|
|
|
+ private void addControls()
|
|
|
+ {
|
|
|
+ // Add tooltips to the buttons
|
|
|
+ playButton.setToolTipText("Play");
|
|
|
+ stopButton.setToolTipText("Stop");
|
|
|
+ prefsButton.setToolTipText("Preferences");
|
|
|
+ aboutButton.setToolTipText("About MadHelix");
|
|
|
+
|
|
|
// Set up layout
|
|
|
controlsPanel.setLayout(new GridBagLayout());
|
|
|
|
|
|
- playButton.addActionListener(ev -> {
|
|
|
+ playButton.addActionListener(ev ->
|
|
|
+ {
|
|
|
playSong();
|
|
|
});
|
|
|
|
|
|
- stopButton.addActionListener(ev -> {
|
|
|
+ stopButton.addActionListener(ev ->
|
|
|
+ {
|
|
|
stopSong();
|
|
|
});
|
|
|
|
|
|
- aboutButton.addActionListener(ev -> {
|
|
|
+ prefsButton.addActionListener(ev ->
|
|
|
+ {
|
|
|
+ prefsDialog.setVisible(true);
|
|
|
+ });
|
|
|
+
|
|
|
+ aboutButton.addActionListener(ev ->
|
|
|
+ {
|
|
|
aboutBox.setVisible(true);
|
|
|
});
|
|
|
|
|
@@ -346,40 +430,68 @@ public class MadHelix extends JFrame {
|
|
|
controlsPanel.add(new JPanel(), constraints);
|
|
|
|
|
|
constraints.weightx = 0;
|
|
|
+ controlsPanel.add(prefsButton, constraints);
|
|
|
controlsPanel.add(aboutButton, constraints);
|
|
|
}
|
|
|
|
|
|
- private void setControlsEnabled(boolean enabled) {
|
|
|
+ private void setControlsEnabled(boolean enabled)
|
|
|
+ {
|
|
|
playButton.setEnabled(enabled);
|
|
|
stopButton.setEnabled(enabled);
|
|
|
}
|
|
|
|
|
|
// The current song name entered in the text box. This may be different from the name of the song currently playing.
|
|
|
- private String getCurrentSongName() {
|
|
|
+ private String getCurrentSongName()
|
|
|
+ {
|
|
|
return songNameEntry.getText();
|
|
|
}
|
|
|
|
|
|
// Refer to the comment above regarding song names, the same applies here with styles.
|
|
|
- private String getCurrentStyleName() {
|
|
|
+ private String getCurrentStyleName()
|
|
|
+ {
|
|
|
return styleChoice.getSelectedItem().toString();
|
|
|
}
|
|
|
|
|
|
- private void playSong() {
|
|
|
+ private void playSong()
|
|
|
+ {
|
|
|
stopSong(); // Without this, the next song won't play until the last one is finished.
|
|
|
- startHelix = true; // Signals to the SoundHelix thread to start
|
|
|
+ soundHelix = genSoundHelixThread();
|
|
|
+ soundHelix.start();
|
|
|
}
|
|
|
|
|
|
- private void stopSong() {
|
|
|
- if (helixPlaying) {
|
|
|
+ private void stopSong()
|
|
|
+ {
|
|
|
+ if (currentSong != null)
|
|
|
+ {
|
|
|
currentSong.getPlayer().abortPlay();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public static void main(String[] args) {
|
|
|
- SwingUtilities.invokeLater(new Runnable() {
|
|
|
- public void run() {
|
|
|
- new MadHelix();
|
|
|
- }
|
|
|
+ public boolean isExportingMidi()
|
|
|
+ {
|
|
|
+ return exportingMidi;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setExportingMidi(boolean exportingMidi)
|
|
|
+ {
|
|
|
+ this.exportingMidi = exportingMidi;
|
|
|
+ }
|
|
|
+
|
|
|
+ public File getMidiOutDir()
|
|
|
+ {
|
|
|
+ return midiOutDir;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setMidiOutDir(File midiOutDir)
|
|
|
+ {
|
|
|
+ this.midiOutDir = midiOutDir;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args)
|
|
|
+ {
|
|
|
+ SwingUtilities.invokeLater(() ->
|
|
|
+ {
|
|
|
+ new MadHelix();
|
|
|
});
|
|
|
}
|
|
|
}
|