package com.nerdscentral.audio.midi;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import javax.sound.midi.ControllerEventListener;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiDevice.Info;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.ShortMessage;
import javax.sound.midi.Transmitter;
import com.nerdscentral.sython.SFPL_RuntimeException;
public class MidiPlayer implements MetaEventListener, java.lang.AutoCloseable, ControllerEventListener
{
// Midi meta event
public static final int END_OF_TRACK_MESSAGE = 47;
private Sequencer sequencer;
private boolean loop;
private boolean paused;
private MidiDevice synth;
static List<MidiDevice> devices = new LinkedList<>();
static
{
Thread jvmShutdownHook = new Thread(new Runnable()
{
@Override
public void run()
{
System.out.println("In hook"); //$NON-NLS-1$
for (MidiDevice dev : devices)
{
if (dev.isOpen())
{
System.out.println("Closing: " + dev); //$NON-NLS-1$
}
}
}
});
Runtime.getRuntime().addShutdownHook(jvmShutdownHook);
}
private void init() throws MidiUnavailableException
{
descrDevice();
sequencer.open();
sequencer.addMetaEventListener(this);
int[] conts = { ShortMessage.NOTE_ON, ShortMessage.NOTE_OFF };
int[] added = sequencer.addControllerEventListener(this, conts);
System.out.println(Messages.getString("MidiPlayer.8") + added.length + Messages.getString("MidiPlayer.9")); //$NON-NLS-1$ //$NON-NLS-2$
devices.add(sequencer);
}
/**
* Creates a new MidiPlayer object.
*/
public MidiPlayer()
{
try
{
sequencer = MidiSystem.getSequencer();
init();
}
catch (MidiUnavailableException ex)
{
sequencer = null;
}
}
private void descrDevice()
{
Info deviceInfo = sequencer.getDeviceInfo();
System.out.println(Messages.getString("MidiPlayer.2") + deviceInfo.getName() + Messages.getString("MidiPlayer.3") + deviceInfo.getDescription()); //$NON-NLS-1$ //$NON-NLS-2$
for (Receiver r : sequencer.getReceivers())
{
System.out.println(Messages.getString("MidiPlayer.4") + r); //$NON-NLS-1$
}
for (Transmitter t : sequencer.getTransmitters())
{
System.out.println(Messages.getString("MidiPlayer.5") + t); //$NON-NLS-1$
}
}
/**
* Creates a new MidiPlayer object.
*
* @throws SFPL_RuntimeException
*/
@SuppressWarnings("resource")
public MidiPlayer(int sequencerNo, int synthNo) throws SFPL_RuntimeException
{
try
{
Info[] infos = MidiSystem.getMidiDeviceInfo();
MidiDevice dev = MidiSystem.getMidiDevice(infos[sequencerNo]);
if (dev instanceof Sequencer)
{
sequencer = (Sequencer) dev;
}
else
{
throw new SFPL_RuntimeException(
Messages.getString("MidiPlayer.0") + sequencerNo + Messages.getString("MidiPlayer.1")); //$NON-NLS-1$ //$NON-NLS-2$
}
dev = MidiSystem.getMidiDevice(infos[synthNo]);
synth = dev;
synth.open();
Receiver synthReceiver = synth.getReceiver();
Transmitter seqTransmitter = sequencer.getTransmitter();
seqTransmitter.setReceiver(synthReceiver);
init();
}
catch (MidiUnavailableException ex)
{
sequencer = null;
}
}
/**
* Plays a sequence, optionally looping. This method returns immediately. The sequence is not played if it is invalid.
*/
public void play(Sequence sequence, boolean loopIn)
{
if (sequencer != null && sequence != null && sequencer.isOpen())
{
try
{
sequencer.setSequence(sequence);
sequencer.start();
this.loop = loopIn;
}
catch (InvalidMidiDataException ex)
{
ex.printStackTrace();
}
}
}
public void play(Sequence sequence)
{
play(sequence, false);
}
public void playSolo(Sequence sequence, int controlTrack, int voiceTrack)
{
if (sequencer != null && sequence != null && sequencer.isOpen())
{
try
{
sequencer.setSequence(sequence);
int nt = sequence.getTracks().length;
for (int i = 0; i < nt; ++i)
{
if (i == controlTrack || i == voiceTrack)
{
System.out.println(Messages.getString("MidiPlayer.11") + i + Messages.getString("MidiPlayer.12")); //$NON-NLS-1$ //$NON-NLS-2$
sequencer.setTrackMute(i, false);
sequencer.setTrackSolo(i, true);
}
else
{
System.out.println(Messages.getString("MidiPlayer.13") + i + Messages.getString("MidiPlayer.14")); //$NON-NLS-1$ //$NON-NLS-2$
sequencer.setTrackMute(i, true);
sequencer.setTrackSolo(i, false);
}
}
sequencer.start();
this.loop = false;
}
catch (InvalidMidiDataException ex)
{
ex.printStackTrace();
}
}
}
public void manual(Sequence sequence) throws IOException
{
if (sequencer != null && sequence != null && sequencer.isOpen())
{
try
{
sequencer.setSequence(sequence);
System.out.println(Messages.getString("MidiPlayer.6")); //$NON-NLS-1$
System.in.read();
System.out.println("Playing"); //$NON-NLS-1$
sequencer.start();
System.out.println(Messages.getString("MidiPlayer.7")); //$NON-NLS-1$
System.in.read();
if (sequencer.isRunning())
{
sequencer.stop();
}
System.out.println("Stopped"); //$NON-NLS-1$
}
catch (InvalidMidiDataException ex)
{
ex.printStackTrace();
}
}
}
/**
* This method is called by the sound system when a meta event occurs. In this case, when the end-of-track meta event is
* received, the sequence is restarted if looping is on.
*/
@Override
public void meta(MetaMessage event)
{
if (event.getType() == END_OF_TRACK_MESSAGE)
{
System.out.println("End Of Track"); //$NON-NLS-1$
if (sequencer != null && sequencer.isOpen() && loop)
{
sequencer.start();
}
}
}
public void waitFor() throws InterruptedException
{
while (sequencer.isRunning())
{
Thread.sleep(1000);
System.out.print('.');
}
sequencer.stop();
}
/**
* Stops the sequencer and resets its position to 0.
*/
public void stop()
{
if (sequencer != null && sequencer.isOpen())
{
sequencer.stop();
sequencer.setMicrosecondPosition(0);
}
}
/**
* Closes the sequencer.
*/
@Override
public void close()
{
if (sequencer != null && sequencer.isOpen())
{
sequencer.close();
}
}
/**
* Sets the paused state. Music may not immediately pause.
*/
public void setPaused(boolean pausedIn)
{
if (this.paused != pausedIn && sequencer != null && sequencer.isOpen())
{
this.paused = pausedIn;
if (pausedIn)
{
sequencer.stop();
}
else
{
sequencer.start();
}
}
}
/**
* Returns the paused state.
*/
public boolean isPaused()
{
return paused;
}
@Override
public void controlChange(ShortMessage event)
{
System.out.println(Messages.getString("MidiPlayer.10")); //$NON-NLS-1$
if (event.getCommand() == ShortMessage.NOTE_ON)
{
System.out.println("Note On " + event.getData1()); //$NON-NLS-1$
}
else
{
System.out.println("Note Off " + event.getData2()); //$NON-NLS-1$
}
}
}