package com.towel.sound;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
/**
* Plays MIDI music. The objective of this class is to easy the manipulation of
* the Sequencer object. Each MidiPlayer will control only one Sequencer. If the
* sequencer is not available, the MidiPlayer will simply not play any
* jgf.sound, not resulting in errors.
*
* @author Vinicius
*/
public class MidiPlayer
{
private Sequencer sequencer;
private boolean paused;
public MidiPlayer()
{
try
{
sequencer = MidiSystem.getSequencer();
sequencer.open();
paused = true;
}
catch (MidiUnavailableException e)
{
sequencer = null;
}
}
/**
* Start playing the given music in loop. This method returns immediatelly.
*
* @param midi The MIDI to be played.
*/
public void play(Sequence midi)
{
play(midi, true);
}
/**
* Start playing the given music. This method returns immediatelly.
*
* @param midi The MIDI to be played.
* @param loop If true, loop the game sound endlessly. Otherwise plays only
* once.
*/
public void play(Sequence midi, boolean loop)
{
if (sequencer == null || midi == null)
return;
try
{
sequencer.setSequence(midi);
sequencer.setMicrosecondPosition(0);
if (loop)
sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
setPaused(false);
}
catch (InvalidMidiDataException e)
{
e.printStackTrace();
}
}
/**
* Stops the music being played, if any.
*/
public void stop()
{
if (sequencer == null || !sequencer.isOpen())
return;
sequencer.stop();
sequencer.setMicrosecondPosition(0);
}
/**
* Closes the device, indicating that the device should now release any
* system resources it is using. Once closed, no jgf.sound will be played
* again.
*/
public void close()
{
if (sequencer == null || !sequencer.isOpen())
return;
sequencer.close();
}
/**
* Pauses the jgf.sound being played.
*
* @param paused True to pause, false to continue.
* @see MidiPlayer#isPaused()
*/
public void setPaused(boolean paused)
{
if (this.paused == paused || sequencer == null)
return;
this.paused = paused;
if (paused)
sequencer.stop();
else
sequencer.start();
}
/**
* Indicate if the MIDI is paused or not.
*
* @return True if the MIDI is paused, false otherwise.
*/
public boolean isPaused()
{
return paused;
}
/**
* Returns the current tempo factor. The default is 1.0.
*
* @return The tempo factor.
* @see MidiPlayer#setTempoFactor(float)
*/
public double getTempoFactor()
{
if (sequencer == null)
return 0;
return sequencer.getTempoFactor();
}
/**
* Obtains the current mute state for a track. The default mute state for
* all tracks which have not been muted is false. In any case where the
* specified track has not been muted, this method should return false. This
* applies if the sequencer does not support muting of tracks, and if the
* specified track index is not valid.
*
* @param track the track number. Tracks in the current sequence are
* numbered from 0 to the number of tracks in the sequence minus
* 1
* @return <code>true</code> if muted, <code>false</code> if not.
*/
public boolean getTrackMute(int track)
{
if (sequencer == null)
return true;
return sequencer.getTrackMute(track);
}
/**
* Obtains the current solo state for a track. The default mute state for
* all tracks which have not been solo'd is false. In any case where the
* specified track has not been solo'd, this method should return false.
* This applies if the sequencer does not support soloing of tracks, and if
* the specified track index is not valid.
*
* @param track the track number. Tracks in the current sequence are
* numbered from 0 to the number of tracks in the sequence minus
* 1.
* @return true if solo'd, false if not.
*/
public boolean getTrackSolo(int track)
{
if (sequencer == null)
return false;
return sequencer.getTrackSolo(track);
}
/**
* Scales the sequencer's actual playback tempo by the factor provided. The
* default is 1.0. A value of 1.0 represents the natural rate (the tempo
* specified in the sequence), 2.0 means twice as fast, etc. The tempo
* factor does not affect the values returned by getTempoInMPQ and
* getTempoInBPM. Those values indicate the tempo prior to scaling. Note
* that the tempo factor cannot be adjusted when external synchronization is
* used. In that situation, setTempoFactor always sets the tempo factor to
* 1.0.
*
* @param factor the requested tempo scalar
*/
public void setTempoFactor(double factor)
{
if (sequencer == null)
return;
sequencer.setTempoFactor((float) factor);
}
/**
* Sets the mute state for a track. This method may fail for a number of
* reasons. For example, the track number specified may not be valid for the
* current sequence, or the sequencer may not support this functionality. An
* application which needs to verify whether this operation succeeded should
* follow this call with a call to getTrackMute.
*
* @param track the track number. Tracks in the current sequence are
* numbered from 0 to the number of tracks in the sequence minus
* 1.
* @param mute the new mute state for the track. true implies the track
* should be muted, false implies the track should be unmuted.
*/
public void setTrackMute(int track, boolean mute)
{
if (sequencer == null)
return;
sequencer.setTrackMute(track, mute);
}
/**
* Sets the solo state for a track. If solo is true only this track and
* other solo'd tracks will jgf.sound. If solo is false then only other
* solo'd tracks will jgf.sound, unless no tracks are solo'd in which case
* all un-muted tracks will jgf.sound. This method may fail for a number of
* reasons. For example, the track number specified may not be valid for the
* current sequence, or the sequencer may not support this functionality. An
* application which needs to verify whether this operation succeeded should
* follow this call with a call to getTrackSolo.
*
* @param track the track number. Tracks in the current sequence are
* numbered from 0 to the number of tracks in the sequence minus
* 1.
* @param solo the new solo state for the track. true implies the track
* should be solo'd, false implies the track should not be
* solo'd.
*/
public void setTrackSolo(int track, boolean solo)
{
if (sequencer == null)
return;
sequencer.setTrackSolo(track, solo);
}
/**
* Indicate if the sequencer was obtained and, therefore, if it's possible
* to play musics with this object.
*
* @return
*/
public boolean isSequencerAvailable()
{
return sequencer != null;
}
}