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
/*
* 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
* 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;
// 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.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;
// 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
{
// SoundHelix
private final File stylesDir = new File(PathUtils.getStylesDirPath()); // Styles directory
private SongContext currentSong = null; // Current song being played
// Preferences
private File midiOutDir = new File(PathUtils.getMidiDirPath());
private boolean exportingMidi = false;
// 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);
private PrefsDialog prefsDialog = new PrefsDialog(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<>();
// 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(playIcon);
private JButton stopButton = new JButton(stopIcon);
private JButton prefsButton = new JButton(prefsIcon);
private JButton aboutButton = new JButton(aboutIcon);
// SoundHelix thread.
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);
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 (exportingMidi)
{
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);
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
{
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.
setStatus("Ready");
this.setTitle("MadHelix");
});
newHelixThread.setDaemon(true); // Closes SoundHelix if MadHelix is closed.
return newHelixThread;
}
public MadHelix()
{
initMadHelix();
}
private void initMadHelix()
{
setUpKeyBindings();
addComponents();
// Set up main window
this.setTitle("MadHelix");
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.setResizable(false);
this.pack();
this.setLocationRelativeTo(null);
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 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();
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);
}
// 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;
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);
}
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 ->
{
playSong();
});
stopButton.addActionListener(ev ->
{
stopSong();
});
prefsButton.addActionListener(ev ->
{
prefsDialog.setVisible(true);
});
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(prefsButton, constraints);
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.
soundHelix = genSoundHelixThread();
soundHelix.start();
}
private void stopSong()
{
if (currentSong != null)
{
currentSong.getPlayer().abortPlay();
}
}
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();
});
}
}