import javax.sound.midi.*;
import java.io.*;


public class Midi {

 static { Subr.def("Midi", "getSynthesizer", "get-synthesizer", 0); }
 public static Synthesizer getSynthesizer() throws Exception {
  Synthesizer syn = MidiSystem.getSynthesizer();
  syn.open();
  return syn;
 }

 static { Subr.def("Midi", "getSequencer", "get-sequencer", 0); }
 public static Sequencer getSequencer() throws Exception {
  Sequencer seqr = MidiSystem.getSequencer();
  seqr.open();
  return seqr;
 }

 static { Subr.def("Midi", "setReceiver", "set-receiver", 2); }
 public static Symbol setReceiver(Sequencer seqr, Synthesizer synth) throws Exception {
  seqr.getTransmitter().setReceiver(synth.getReceiver());
  return Symbol.intern("ok");
 }

 static { Subr.def("Midi", "setSequence", "set-sequence", 2); }
 public static Symbol setSequence(Sequencer seqr, Sequence seq) throws Exception {
  seqr.setSequence(seq);
  return Symbol.intern("ok");
 }

 static { Subr.def("Midi", "startSequencer", "start-sequencer", 1); }
 public static Symbol startSequencer(Sequencer seqr) throws Exception {
  seqr.start();
  while(seqr.isRunning()) {
   if (Eval.interrupted) {
    seqr.stop(); seqr.setTickPosition(0);
    Eval.checkInterrupt();
   }
   Thread.sleep(100);
  }
  seqr.setTickPosition(0);
  return Symbol.intern("done");
 }

 static { Subr.def("Midi", "newSequence", "sequence", 1); }
 public static Sequence newSequence(Number q) throws Exception {
  return new Sequence(Sequence.PPQ, q.intValue());
 }

 static { Subr.def("Midi", "createTrack", "create-track", 1); }
 public static Track createTrack(Sequence seq) {
  return seq.createTrack();
 }

 static { Subr.def("Midi", "newShortMessage", "short-message", 0); }
 public static ShortMessage newShortMessage() {
  return new ShortMessage();
 }

 static { Subr.def("Midi", "setMessage", "set-message", 5); }
 public static Symbol setMessage(ShortMessage msg, Symbol cmd, Number ch, Number p, Number v) throws Exception {
  String name = cmd.name;
  int cmd_val;
  if (name.equals("note-on")) {
   cmd_val = ShortMessage.NOTE_ON;
  } else if (name.equals("note-off")) {
   cmd_val = ShortMessage.NOTE_OFF;
  } else if (name.equals("program-change")) {
   cmd_val = ShortMessage.PROGRAM_CHANGE;
  } else if (name.equals("channel-pressure")) {
   cmd_val = ShortMessage.CHANNEL_PRESSURE;
  } else if (name.equals("pitch-bend")) {
   cmd_val = ShortMessage.PITCH_BEND;
  } else if (name.equals("poly-pressure")) {
   cmd_val = ShortMessage.POLY_PRESSURE;
  } else if (name.equals("control-change")) {
   cmd_val = ShortMessage.CONTROL_CHANGE;
  } else {
   throw Eval.error("unknown command:" + name);
  }
  msg.setMessage(cmd_val, ch.intValue(), p.intValue(), v.intValue());
  return Symbol.intern("ok");
 }

 static { Subr.def("Midi", "midiEvent", "midi-event", 2); }
 public static MidiEvent midiEvent(MidiMessage msg, Number t) {
  return new MidiEvent(msg, t.longValue());
 }

 static { Subr.def("Midi", "addEvent", "add-event", 2); }
 public static Symbol addEvent(Track trk, MidiEvent evt) {
  trk.add(evt);
  return Symbol.intern("ok");
 }

 static { Subr.def("Midi", "setTempoMessage", "set-tempo-message", 1); }
 public static MetaMessage setTempoMessage(Number t) throws Exception {
  MetaMessage mmsg = new MetaMessage();
  int tempo = t.intValue();
  int l = 60*1000000/tempo;
  mmsg.setMessage(0x51,
                  new byte[]{(byte)(l/65536), (byte)(l%65536/256), (byte)(l%256)},
                  3);
  return mmsg;
 }

 static { Subr.def("Midi", "getSequence", "get-sequence", 1); }
 public static Sequence getSequence(String url)  throws Exception {
    File midiFile;
    try {
      midiFile = new File(url);
    } catch (Exception e) {
      throw Eval.error("cannot read midi file");
    }
    if (midiFile == null)
      throw Eval.error("cannot read midi file");
    return MidiSystem.getSequence(midiFile);
  }

 static { Subr.def("Midi", "writeMidi", "write-midi", 2); }
 public static Symbol writeMidi(Sequence seq, String url) throws Exception {
   MidiSystem.write(seq, 1, new java.io.File(url));
   return Symbol.intern("done");
 }

 static { Subr.def("Midi", "closeDevice", "close-device", 1); }
 public static Symbol closeDevice(MidiDevice device) {
  device.close();
  return Symbol.intern("ok");
 }

 static void init() {
  IO.loadFromJarFile("mml.lsp");
 }
}
