This repository contains MadHelix itself, along with compiled libraries with its dependencies.
Clone
HTTPS:
git clone https://vervis.peers.community/repos/ZEkyo
SSH:
git clone USERNAME@vervis.peers.community:ZEkyo
Branches
Tags
MadHelix.java
/*
* UltrasonicHelix 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
* it under the terms of the GNU General Public License version 3 only,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ultrasonicmadness.madhelix;
// Assorted imports
import java.io.File;
import java.net.URL;
import java.util.Random;
// SoundHelix
import com.soundhelix.component.player.impl.MidiPlayer;
import com.soundhelix.misc.SongContext;
import com.soundhelix.util.SongUtils;
// AWT widgets
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
// Swing widgets
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;
// Other Swing modules
import javax.swing.SwingUtilities;
// UltrasonicHelix imports
import org.ultrasonicmadness.madhelix.dialogs.AboutBox;
import org.ultrasonicmadness.madhelix.utils.PathUtils;
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.
// MIDI file output
private final File midiOutDir = new File(PathUtils.getMidiDirPath()); // MIDI file output directory
private boolean exportMidi = true;
// Main 3 panels: status, options, controls
private JPanel statusPanel = new JPanel();
private JPanel optionsPanel = new JPanel();
private JPanel controlsPanel = new JPanel();
// Components
private JPanel mainPanel = new JPanel();
private AboutBox aboutBox = new AboutBox(this);
// Status labels (spaces included so that the layout works)
private JLabel playStatus = new JLabel(" ");
private JLabel playStatusInfo = new JLabel(" ");
private JLabel styleStatus = new JLabel(" ");
private JLabel styleStatusInfo = new JLabel(" ");
// Labels and data entry fields
private JLabel songNameLabel = new JLabel("Song name");
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);
// Control buttons
private JButton playButton = new JButton("\u25B6");
private JButton stopButton = new JButton("\u25A0");
private JButton aboutButton = new JButton("About");
// 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;
setStatus("Loading SoundHelix");
// The buttons don't function at this time, so disable them while SoundHelix loads.
setControlsEnabled(false);
String styleName = getCurrentStyleName();
String songName = getCurrentSongName();
File styleFile = new File(stylesDir.toString() + File.separator +
styleName + ".xml");
try {
// If a song name is specified, use it, otherwise generate a random number and use it.
if (songName != null && !songName.equals("")) {
currentSong = SongUtils.generateSong(
styleFile.toURI().toURL(), songName);
} else {
// This is the same way SoundHelix uses the random number generator.
currentSong = SongUtils.generateSong(
styleFile.toURI().toURL(), new Random().nextLong());
}
if (exportMidi) {
try {
MidiPlayer midiPlayer = (MidiPlayer)currentSong.getPlayer();
midiPlayer.setMidiFilename(midiOutDir + File.separator +
currentSong.getSongName() + "-" + styleName +
".mid");
} catch (Exception x) {
JOptionPane.showMessageDialog(null, x.getMessage().toString(),
"Could not set MIDI filename.", JOptionPane.ERROR_MESSAGE);
}
}
// Update song and style information
setStatus("Playing",
currentSong.getSongName(), "Style", styleName);
// 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
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
JOptionPane.showMessageDialog(null,
"Cannot find module " + r.getMessage(), "Error",
JOptionPane.ERROR_MESSAGE);
// In case an error has occured, enable the buttons again
setControlsEnabled(true);
}
// The song has finished at this point, so update variables and status accordingly.
helixPlaying = false;
setStatus("Ready");
}
});
public MadHelix() {
initMadHelix();
}
private void initMadHelix() {
// Start SoundHelix thread
soundHelix.setDaemon(true); // Closes SoundHelix if UltrasonicHelix is closed.
soundHelix.start();
addComponents();
// Set up main window
this.setTitle("MadHelix");
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setVisible(true);
}
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) {
setStatus("No styles available.");
setControlsEnabled(false);
JOptionPane.showMessageDialog(null,
"No .xml files found in the styles directory",
"Error",
JOptionPane.ERROR_MESSAGE
);
}
}
private void addComponents() {
// Set panel layout
mainPanel.setLayout(new GridBagLayout());
// Set up constraints
GridBagConstraints mainConstraints = new GridBagConstraints();
mainConstraints.fill = GridBagConstraints.HORIZONTAL;
addStatusLabels();
addOptions();
addControls();
// Add panels
mainConstraints.gridy = 0;
mainPanel.add(statusPanel, mainConstraints);
mainConstraints.gridy = 1;
mainPanel.add(controlsPanel, mainConstraints);
mainConstraints.gridy = 2;
mainPanel.add(optionsPanel, mainConstraints);
this.add(mainPanel);
}
private void addStatusLabels() {
determineStatus();
// Set up layout
statusPanel.setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.insets = new Insets(2, 2, 2, 2);
constraints.fill = GridBagConstraints.HORIZONTAL;
// Add the labels to the panel
constraints.gridy = 0;
constraints.weightx = 0;
statusPanel.add(playStatus, constraints);
constraints.weightx = 1;
statusPanel.add(playStatusInfo, constraints);
constraints.gridy = 1;
constraints.weightx = 0;
statusPanel.add(styleStatus, constraints);
constraints.weightx = 1;
statusPanel.add(styleStatusInfo, constraints);
}
// Set status to the four passed strings
private void setStatus(String newPlayStatus, String newPlayStatusInfo,
String newStyleStatus, String newStyleStatusInfo) {
// Set all label text to the passed strings.
playStatus.setText(newPlayStatus);
playStatusInfo.setText(newPlayStatusInfo);
styleStatus.setText(newStyleStatus);
styleStatusInfo.setText(newStyleStatusInfo);
}
// Set status to the passed string. The others are reset to blank.
private void setStatus(String newPlayStatus) {
setStatus(newPlayStatus, " ", " ", " ");
}
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) {
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());
GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.insets = new Insets(2, 2, 2, 2);
// Add song information
constraints.gridy = 0;
optionsPanel.add(songNameLabel, constraints);
optionsPanel.add(songNameEntry, constraints);
// Add style label
constraints.gridy = 1;
optionsPanel.add(styleLabel, constraints);
optionsPanel.add(styleChoice, constraints);
// Add file export checkbox
constraints.gridy = 2;
constraints.gridwidth = 2;
optionsPanel.add(fileExportCheckBox, constraints);
}
private void addControls() {
// Set up layout
controlsPanel.setLayout(new GridBagLayout());
playButton.addActionListener(ev -> {
playSong();
});
stopButton.addActionListener(ev -> {
stopSong();
});
aboutButton.addActionListener(ev -> {
aboutBox.setVisible(true);
});
GridBagConstraints constraints = new GridBagConstraints();
constraints.insets = new Insets(2, 2, 2, 2);
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 0;
controlsPanel.add(playButton, constraints);
controlsPanel.add(stopButton, constraints);
// Add a blank panel to separate player controls and the about button
constraints.weightx = 1;
controlsPanel.add(new JPanel(), constraints);
constraints.weightx = 0;
controlsPanel.add(aboutButton, constraints);
}
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() {
return songNameEntry.getText();
}
// Refer to the comment above regarding song names, the same applies here with styles.
private String getCurrentStyleName() {
return styleChoice.getSelectedItem().toString();
}
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
}
private void stopSong() {
if (helixPlaying) {
currentSong.getPlayer().abortPlay();
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MadHelix();
}
});
}
}