package moppydesk.inputs;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
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.Transmitter;
import moppydesk.MoppyStatusConsumer;
import moppydesk.outputs.ReceiverMarshaller;
/**
*
* @author Sammy1Am
*/
public class MoppySequencer implements MetaEventListener, Transmitter{
Sequencer sequencer;
Sequence currentSequence = null;
Transmitter sequenceTransmitter; // Because we'll be piping all the messages to a common receiver, we'll only need one of these
ArrayList<MoppyStatusConsumer> listeners = new ArrayList<MoppyStatusConsumer>(1);
ReceiverMarshaller rm;
public MoppySequencer(ReceiverMarshaller rmIn) throws MidiUnavailableException {
sequencer = MidiSystem.getSequencer(false);
sequencer.open();
sequencer.addMetaEventListener(this);
sequenceTransmitter = sequencer.getTransmitter(); // This method creates a new transmitter each time it's called!
rm = rmIn;
}
public void loadFile(String filePath) throws InvalidMidiDataException, IOException, MidiUnavailableException {
sequencer.stop();
Sequence sequence = MidiSystem.getSequence(new File(filePath));
sequencer.setSequence(sequence);
System.out.println("Loaded sequence with "+(sequence.getTracks().length-1)+" MIDI tracks.");
currentSequence = sequence;
}
public void startSequencer(){
rm.sequenceStarting();
sequencer.start();
}
public boolean isRunning(){
return sequencer.isRunning();
}
public void stopSequencer(){
if (sequencer.isOpen()){
rm.sequenceStopping();
sequencer.stop();
}
}
public void resetSequencer(){
if (sequencer.isOpen()){
rm.sequenceStopping();
sequencer.stop();
sequencer.setTickPosition(0);
}
}
public void setTempo(float newTempo){
sequencer.setTempoInBPM(newTempo);
}
public long getSecondsLength(){
return sequencer.getMicrosecondLength()/1000000;
}
public long getSecondsPosition(){
return sequencer.getMicrosecondPosition()/1000000;
}
public void setSecondsPosition(long seconds){
sequencer.setMicrosecondPosition(seconds*1000000);
}
public void addListener(MoppyStatusConsumer newListener){
listeners.add(newListener);
}
public void removeListener(MoppyStatusConsumer oldListener){
listeners.remove(oldListener);
}
public void closeSequencer(){
stopSequencer();
sequencer.close();
}
public void meta(MetaMessage meta) {
if (meta.getType() == 81){
int uSecondsPerQN = 0;
uSecondsPerQN |= meta.getData()[0] & 0xFF;
uSecondsPerQN <<= 8;
uSecondsPerQN |= meta.getData()[1] & 0xFF;
uSecondsPerQN <<= 8;
uSecondsPerQN |= meta.getData()[2] & 0xFF;
int newTempo = 60000000/uSecondsPerQN;
sequencer.setTempoInBPM(newTempo);
for (MoppyStatusConsumer c : listeners){
c.tempoChanged(newTempo);
}
System.out.println("Tempo changed to: " + newTempo);
}
else if (meta.getType() == 47) {
//MrSolidSnake745: Exposing sequence stopping event to receivers
//The end of a sequence is the same as it stopping
rm.sequenceStopping();
//MrSolidSnake745: Exposing end of sequence event to status consumers
for (MoppyStatusConsumer c : listeners) { c.sequenceEnded(); }
System.out.println("End of current sequence");
}
}
public void setReceiver(Receiver receiver) {
sequenceTransmitter.setReceiver(receiver);
}
public Receiver getReceiver() {
return sequenceTransmitter.getReceiver();
}
public void close() {
closeSequencer();
}
}