content: /*!
   *  copyright 2012 abudaan http://abumarkub.net
   *  code licensed under MIT 
   *  http://abumarkub.net/midibridge/license
   * 
   *  
   *  Wrapper around the javax.sound.midi.Sequencer class 
   *  
   */
  package net.abumarkub.midi;
  
  import java.applet.AppletContext;
  import java.io.ByteArrayInputStream;
  import java.io.FileInputStream;
  import java.net.MalformedURLException;
  import java.net.URL;
  import javax.sound.midi.*;
  import netscape.javascript.JSObject;
  import org.apache.commons.codec.binary.Base64;
  
  public class MIDISequencer implements Receiver, MetaEventListener {
  
      private Sequence _sequence;
      private Sequencer _sequencer;
      private Transmitter _transmitter;
      private Transmitter _transmitter2;
      private Receiver _receiver;
      private AppletContext _context;
      private JSObject _jsMIDIEventListener;
      private JSObject _jsMetaEventListener;
      private boolean _hasMetaEventListener;
  
      public MIDISequencer(AppletContext context) {
          _context = context;
  
          try {
              _sequencer = javax.sound.midi.MidiSystem.getSequencer(false);
  
          } catch (MidiUnavailableException e) {
              System.out.println(e);
          }
  
          _receiver = null;
          _transmitter = null;
          _transmitter2 = null;
      }
      
      //sending messages from Java to Javascript is faster via AppletContext then via Live Connect
      private void sendMessageViaContext(String url) {
          try {
              _context.showDocument(new URL(url));
          } catch (MalformedURLException me) {
              System.out.println(me);
          } catch (Exception e) {
              System.out.println(e);
          }
      }
  
      public boolean addEventListener(String id) {
          if (id.equals("midimessage")) {
              if (_transmitter == null) {
                  try {
                      _transmitter = _sequencer.getTransmitter();
                  } catch (MidiUnavailableException e) {
                      System.out.println("Sequencer could not open a transmitter " + e);
                      return false;
                  }
              }
              _transmitter.setReceiver(this);
              return true;
          } else if (id.equals("metamessage")) {
              if (!_hasMetaEventListener) {
                  _hasMetaEventListener = _sequencer.addMetaEventListener(this);
              }
              return true;
          }
          return false;
      }
      
      //@TODO: currently you can connect only 1 eventlistener!
      public boolean addEventListener(String id, JSObject eventListener) {
          
          if (id.equals("midimessage")) {
              if (_transmitter == null) {
                  try {
                      _transmitter = _sequencer.getTransmitter();
                  } catch (MidiUnavailableException e) {
                      System.out.println("Sequencer could not open a transmitter " + e);
                      return false;
                  }
              }
              _jsMIDIEventListener = eventListener;
              _transmitter.setReceiver(this);
              return true;
          } else if (id.equals("metamessage")) {
              if (!_hasMetaEventListener) {
                  _hasMetaEventListener = _sequencer.addMetaEventListener(this);
              }
              _jsMetaEventListener = eventListener;
              return true;
          }
          return false;
      }
  
      public Sequence loadBase64String(String data) {
          _sequence = null;
          byte[] decoded = Base64.decodeBase64(data);
          ByteArrayInputStream input = new ByteArrayInputStream(decoded);
          try {
              _sequence = javax.sound.midi.MidiSystem.getSequence(input);
              loadSequence(_sequence);
  
          } catch (Exception e) {
              System.out.println("loading: " + e);
          }
          return _sequence;
      }
  
      public Sequence playBase64String(String data) {
          loadBase64String(data);
          play();
          return _sequence;
      }
  
      public Sequence loadMidiFile(String url) {
          FileInputStream is = null;
          _sequence = null;
  
          try {
              //System.out.println("loadMidiFile: " + url);
              is = new FileInputStream(url);
              //} catch (FileNotFoundException e) {
          } catch (Exception e) {
              System.out.println(e);
          }
          try {
              _sequence = javax.sound.midi.MidiSystem.getSequence(is);
              loadSequence(_sequence);
  
          } catch (Exception e) {
              System.out.println(e);
          }
          return _sequence;
      }
  
      public Sequence playMidiFile(String url) {
          loadMidiFile(url);
          play();
          return _sequence;
      }
  
      public void close() {
          if (_sequencer.isOpen()) {
              _sequencer.stop();
              _sequencer.close();
          }
          if (_transmitter != null) {
              _transmitter.close();
          }
          if (_transmitter2 != null) {
              _transmitter2.close();
          }
          if (_receiver != null) {
              _receiver.close();
          }
      }
  
      private void loadSequence(Sequence seq) {
          try {
              _sequencer.open();
          } catch (MidiUnavailableException e) {
              System.out.println(e);
          }
          try {
              _sequencer.setSequence(seq);
          } catch (InvalidMidiDataException e) {
              System.out.println(e);
          }
      }
  
      public void send(MidiMessage message, long timeStamp) {
          //System.out.println("MidiMessage: " + message + " " + timeStamp);
          timeStamp = _sequencer.getMicrosecondPosition();
          if (message instanceof ShortMessage) {
              
              ShortMessage tmp = (ShortMessage) message;
              MIDIMessage msg = new MIDIMessage(tmp, timeStamp);
  
              //@TODO: loop over all event listeners, check if they want to be updated via AppletContext or Live Connect, and dispatch to all
              
              if(1 == 2){//just a way of ignoring the AppletContext send method
                  String jsMsg = msg.command + "," + msg.channel + "," + msg.data1 + "," + msg.data2 + "," + timeStamp + ",'" + msg.toString() + "'";            
                  sendMessageViaContext("javascript:midiBridge.onSequencerMIDIData(" + jsMsg + ")");
              }else{//currently sending via Live Connect is preferred
                  Object[] args = {msg};     
                  _jsMIDIEventListener.call("listener",args);
              }
          }
      }
      
      //@TODO: loop over all event listeners, check if they want to be updated via AppletContext or Live Connect, and dispatch to all
      public void meta(MetaMessage meta) {
          if(1 == 2){//just a way of ignoring the AppletContext send method
              StringBuilder jsMsg = new StringBuilder();
              jsMsg.append(meta.getType());
              jsMsg.append(",");
              jsMsg.append(meta.getStatus());
  
              byte[] message = meta.getMessage();
              for(int i = 0, maxi = message.length; i < maxi; i++){
                  jsMsg.append(",");
                  jsMsg.append(message[i]);
              }
  
              sendMessageViaContext("javascript:midiBridge.sequencerMetaData(" + jsMsg.toString() + ")");            
          }else{//currently sending via Live Connect is preferred
              Object[] args = {meta};
              _jsMetaEventListener.call("listener",args);          
          }
         
      }
  
      public Sequence getSequence() {
          return _sequence;
      }
  
      public boolean setDirectOutput(MIDIDevice device) {
  
          System.out.println("setDirectOutput: " + device.deviceName);
  
          if (_receiver != null) {
              _receiver.close();
          }
  
          _receiver = device.getReceiver();
          if (_receiver == null) {
              return false;
          }
  
          if (_transmitter2 == null) {
              try {
                  _transmitter2 = _sequencer.getTransmitter();
              } catch (MidiUnavailableException e) {
                  System.out.println("Sequencer could not open a transmitter " + e);
                  return false;
              }
          }
          _transmitter2.setReceiver(_receiver);
  
          return true;
      }
  
      public void removeDirectOutput() {
          if (_transmitter2 != null) {
              _transmitter2.close();
          }
          if (_receiver != null) {
              _receiver.close();
          }
          _transmitter2 = null;
          _receiver = null;
      }
  
      public boolean hasDirectOutput() {
          return _receiver != null;
      }
  
      //composition:
      public void play() {
          if (_sequencer.isOpen()) {
              _sequencer.start();
          }
      }
  
      public void pause() {
          if (_sequencer.isOpen()) {
              _sequencer.stop();
          }
      }
  
      public void stop() {
          if (_sequencer.isOpen()) {
              _sequencer.setMicrosecondPosition(0L);
              _sequencer.stop();
          }
      }
  
      public Long getMicrosecondPosition() {
          return _sequencer.getMicrosecondPosition();
      }
  
      public void setMicrosecondPosition(int pos) {
          _sequencer.setMicrosecondPosition(Long.parseLong("" + pos));
      }
  
      public float getTempoInBPM() {
          return _sequencer.getTempoInBPM();
      }
  
      public void setTempoInBPM(int bpm) {
          _sequencer.setTempoInBPM(Float.parseFloat("" + bpm));
      }
  
      public float getTempoFactor() {
          return _sequencer.getTempoFactor();
      }
  
      public void setTempoFactor(float factor) {
          _sequencer.setTempoFactor(factor);
      }
  
      public void muteTrack(int index) {
          _sequencer.setTrackMute(index, true);
      }
  
      public void unmuteTrack(int index) {
          _sequencer.setTrackMute(index, false);
      }
  
      //@TODO: implemented this
      public void createNewSequence() {
          //_sequence = new Sequence();
          _sequence.getMicrosecondLength();
          _sequence.getTickLength();
      }
  }
  