package moppydesk.ui; import java.awt.Component; import moppydesk.inputs.MoppySequencer; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.File; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.sound.midi.Transmitter; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JSlider; import javax.swing.Timer; import moppydesk.*; /** * * @author Sammy1Am */ public final class SequencerControls extends InputPanel implements MoppyStatusConsumer { MoppySequencer seq; MoppyControlWindow controlWindow; MoppyUI app; final JFileChooser sequenceChooser = new JFileChooser(); Timer progressTimer; private boolean isConnected = false; private boolean fileLoaded = false; /** * Creates new form SequencerControls */ public SequencerControls(MoppyUI app, MoppyControlWindow mcw, MoppySequencer newSequencer) { this.seq = newSequencer; this.app = app; this.controlWindow = mcw; initComponents(); progressTimer = new Timer(1000, new ActionListener() { public void actionPerformed(ActionEvent e) { updateProgressDisplay(); } }); } @Override public void loadPreferences() { ResetDrivesCB.setSelected(app.prefs.getBoolean(Constants.PREF_RESET_DRIVES, false)); RepeatCB.setSelected(app.prefs.getBoolean(Constants.PREF_REPEAT_SEQ, false)); DelayResetSpinner.setValue(app.prefs.getInt(Constants.PREF_DELAY_RESET, 0)); DelayResetSpinner.setEnabled(ResetDrivesCB.isSelected()); } @Override public void savePreferences() { app.prefs.putBoolean(Constants.PREF_RESET_DRIVES, ResetDrivesCB.isSelected()); app.prefs.putBoolean(Constants.PREF_REPEAT_SEQ, RepeatCB.isSelected()); app.prefs.putInt(Constants.PREF_DELAY_RESET, (Integer)DelayResetSpinner.getValue()); } private void updateProgressDisplay() { long currentSeconds = seq.getSecondsPosition(); sequenceProgressSlider.setValue((int) (currentSeconds)); String currentPosition = String.format("%d:%02d", TimeUnit.SECONDS.toMinutes(currentSeconds), currentSeconds % 60); String totalPosition = String.format("%d:%02d", TimeUnit.SECONDS.toMinutes(seq.getSecondsLength()), seq.getSecondsLength() % 60); currentPositionLabel.setText(currentPosition); totalPositionLabel.setText(totalPosition); } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jLabel1 = new javax.swing.JLabel(); sequenceNameLabel = new javax.swing.JLabel(); bpmLabel = new javax.swing.JLabel(); jSlider1 = new javax.swing.JSlider(); startButton = new javax.swing.JButton(); stopButton = new javax.swing.JButton(); loadButton = new javax.swing.JButton(); sequenceProgressSlider = new javax.swing.JSlider(); currentPositionLabel = new javax.swing.JLabel(); totalPositionLabel = new javax.swing.JLabel(); RepeatCB = new javax.swing.JCheckBox(); ResetDrivesCB = new javax.swing.JCheckBox(); DelayResetSpinner = new javax.swing.JSpinner(); jLabel2 = new javax.swing.JLabel(); jLabel1.setText("Current Sequence:"); sequenceNameLabel.setText("<None loaded>"); bpmLabel.setText(jSlider1.getValue() + " bpm"); jSlider1.setMajorTickSpacing(10); jSlider1.setMaximum(310); jSlider1.setMinimum(20); jSlider1.setPaintTicks(true); jSlider1.setValue(120); jSlider1.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { public void mouseDragged(java.awt.event.MouseEvent evt) { jSlider1tempoSliderChanged(evt); } }); startButton.setText("Start"); startButton.setEnabled(false); startButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { startButtonClicked(evt); } }); stopButton.setText("Stop/Reset"); stopButton.setToolTipText("Press once to stop sequencer and return to beginning of track. Press again to reset drives."); stopButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { stopButtonstopResetClicked(evt); } }); loadButton.setText("Load Sequence"); loadButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { loadButtonloadSequence(evt); } }); sequenceProgressSlider.setToolTipText(""); sequenceProgressSlider.setValue(0); sequenceProgressSlider.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { sequenceProgressDragged(evt); } }); sequenceProgressSlider.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() { public void mouseDragged(java.awt.event.MouseEvent evt) { sequenceProgressDragged(evt); } }); currentPositionLabel.setText("00:00"); totalPositionLabel.setText("00:00"); RepeatCB.setText("Repeat"); RepeatCB.setToolTipText("Repeats the song when selected."); ResetDrivesCB.setText("Reset Drives"); ResetDrivesCB.setToolTipText("Resets the drives after the end of the current song."); ResetDrivesCB.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { ResetDrivesCBActionPerformed(evt); } }); DelayResetSpinner.setModel(new javax.swing.SpinnerNumberModel(0, 0, 600, 1)); DelayResetSpinner.setToolTipText("Delays resetting the drives after the song ends by a number of seconds."); jLabel2.setText("Delay Reset:"); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(110, 110, 110) .addComponent(bpmLabel) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(10, 10, 10) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jLabel1) .addGap(0, 0, Short.MAX_VALUE)) .addComponent(sequenceNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGap(18, 18, 18) .addComponent(loadButton)) .addGroup(layout.createSequentialGroup() .addGap(10, 10, 10) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(layout.createSequentialGroup() .addComponent(jLabel2) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(DelayResetSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) .addComponent(ResetDrivesCB) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(RepeatCB)) .addGroup(layout.createSequentialGroup() .addComponent(currentPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(4, 4, 4) .addComponent(sequenceProgressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, 376, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(4, 4, 4) .addComponent(totalPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE))) .addGap(0, 0, Short.MAX_VALUE)) .addGroup(layout.createSequentialGroup() .addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, 267, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(18, 18, 18) .addComponent(startButton, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(stopButton))))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(11, 11, 11) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(jLabel1) .addGap(46, 46, 46)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(loadButton) .addComponent(sequenceNameLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(bpmLabel) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED))) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createSequentialGroup() .addGap(8, 8, 8) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(startButton) .addComponent(stopButton)))) .addGap(8, 8, 8) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(RepeatCB) .addComponent(ResetDrivesCB) .addComponent(DelayResetSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel2)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(currentPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(sequenceProgressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(totalPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))) ); }// </editor-fold>//GEN-END:initComponents private void jSlider1tempoSliderChanged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jSlider1tempoSliderChanged JSlider s = (JSlider) evt.getSource(); seq.setTempo(s.getValue()); bpmLabel.setText(s.getValue() + " bpm"); }//GEN-LAST:event_jSlider1tempoSliderChanged private void startButtonClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonClicked if (startButton.getText().equals("Start")) { playSequencer(); } else { pauseSequencer(); } }//GEN-LAST:event_startButtonClicked private void playSequencer() { seq.startSequencer(); seq.setTempo(jSlider1.getValue()); controlWindow.setStatus("Playing!"); startButton.setText("Pause"); } private void pauseSequencer() { seq.stopSequencer(); startButton.setText("Start"); controlWindow.setStatus("...Paused"); } private void stopResetSequencer() { if (seq.isRunning()) { stopSeq(); } else { resetSeq(); } updateProgressDisplay(); // Always update the progress here in case we're not connected but want to reset the sequencer } private void stopSeq(){ controlWindow.setStatus("Stopping..."); seq.stopSequencer(); seq.resetSequencer(); startButton.setText("Start"); controlWindow.setStatus("Stopped."); } private void resetSeq(){ app.rm.reset(); seq.resetSequencer(); controlWindow.setStatus("Reset."); } private void stopButtonstopResetClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonstopResetClicked stopResetSequencer(); }//GEN-LAST:event_stopButtonstopResetClicked private void loadButtonloadSequence(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadButtonloadSequence String previouslyLoaded = app.prefs.get(Constants.PREF_LOADED_SEQ, null); if (previouslyLoaded != null) { sequenceChooser.setCurrentDirectory(new File(previouslyLoaded)); } int returnVal = sequenceChooser.showOpenDialog(this); if (returnVal == JFileChooser.APPROVE_OPTION) { loadSequenceFile(sequenceChooser.getSelectedFile()); } else { //Cancelled } }//GEN-LAST:event_loadButtonloadSequence private void sequenceProgressDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sequenceProgressDragged int seconds = ((JSlider) evt.getSource()).getValue(); seq.setSecondsPosition(seconds); currentPositionLabel.setText(String.format("%d:%02d", TimeUnit.SECONDS.toMinutes(seconds), seconds % 60)); }//GEN-LAST:event_sequenceProgressDragged private void ResetDrivesCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ResetDrivesCBActionPerformed DelayResetSpinner.setEnabled(ResetDrivesCB.isSelected()); }//GEN-LAST:event_ResetDrivesCBActionPerformed @Override public void tempoChanged(int newTempo) { jSlider1.setValue(newTempo); bpmLabel.setText(newTempo + " bpm"); } private void loadSequenceFile(File sequenceFile) { try { stopSeq(); controlWindow.setStatus("Loading file..."); seq.loadFile(sequenceFile.getPath()); sequenceNameLabel.setText(sequenceFile.getName()); sequenceProgressSlider.setMaximum((int) (seq.getSecondsLength())); app.prefs.put(Constants.PREF_LOADED_SEQ, sequenceFile.getPath()); fileLoaded = true; controlWindow.setStatus("Loaded " + sequenceFile.getName()); updateProgressDisplay(); if (isConnected) { startButton.setEnabled(true); } } catch (Exception ex) { Logger.getLogger(MoppyControlWindow.class.getName()).log(Level.SEVERE, null, ex); controlWindow.setStatus("File loading error!"); JOptionPane.showMessageDialog(this.getRootPane(), ex); } } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JSpinner DelayResetSpinner; private javax.swing.JCheckBox RepeatCB; private javax.swing.JCheckBox ResetDrivesCB; private javax.swing.JLabel bpmLabel; private javax.swing.JLabel currentPositionLabel; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JSlider jSlider1; private javax.swing.JButton loadButton; private javax.swing.JLabel sequenceNameLabel; private javax.swing.JSlider sequenceProgressSlider; private javax.swing.JButton startButton; private javax.swing.JButton stopButton; private javax.swing.JLabel totalPositionLabel; // End of variables declaration//GEN-END:variables @Override public Transmitter getTransmitter() { return seq; } @Override public void connected() { progressTimer.start(); isConnected = true; if (fileLoaded) { startButton.setEnabled(true); } } @Override public void disconnected() { startButton.setEnabled(false); pauseSequencer(); isConnected = false; progressTimer.stop(); seq.setReceiver(null); //Clear receiver so there's no connection here. } //MrSolidSnake745: Simple use for the SequenceEnded event //Resets the sequence and drives if ResetDrivesCB is selected once the song has finished @Override public void sequenceEnded() { controlWindow.setStatus("Song has ended."); app.rm.silence(); //In case there are any stuck notes, most likely from pooling, silence all receivers seq.resetSequencer(); startButton.setText("Start"); if(ResetDrivesCB.isSelected()) { int y = ((Integer)DelayResetSpinner.getValue()); if(y > 0) { try { setEnabledAllControls(false); //Don't want users messing with controls while we delay controlWindow.setStatus("Waiting " + y + " seconds before reset..."); Thread.sleep(y * 1000); //Thread is sooooo sleepy... } catch(Exception x){} finally{setEnabledAllControls(true);} } controlWindow.setStatus("Resetting!"); app.rm.reset(); controlWindow.setStatus("Reset."); } if(RepeatCB.isSelected()) { playSequencer(); } } public void setEnabledAllControls(boolean value) { for (Component c : this.getComponents()) {c.setEnabled(value);} } // <editor-fold defaultstate="collapsed" desc="Key Events"> @Override public boolean enterKeyAction(KeyEvent e) { //Simulate clicking start button startButtonClicked(null); return true; } @Override public boolean tabKeyAction(KeyEvent e) { //Simulate clicking stop button stopButtonstopResetClicked(null); return true; } @Override public boolean upKeyAction(KeyEvent e) { //Skip ahead 10 seconds seq.stopSequencer(); long newPosition = seq.getSecondsPosition() + 10; if(newPosition > seq.getSecondsLength()) newPosition = seq.getSecondsLength(); seq.setSecondsPosition(newPosition); seq.startSequencer(); return true; } @Override public boolean downKeyAction(KeyEvent e) { //Go back 10 seconds seq.stopSequencer(); long newPosition = seq.getSecondsPosition() - 10; if(newPosition < 0) newPosition = 0; seq.setSecondsPosition(newPosition); seq.startSequencer(); return true; } // </editor-fold> }