
// PlayMidi.java
// Andrew Davison, April 2005, ad@fivedots.coe.psu.ac.th

/* Načte MIDI-sekvenci a jednou ji přehraje. Aplikace PlayMidi 
   při přehrávání spí. Vzbudí ji až meta-událost end-of-track
   spuštěná voláním metody meta(), která způsobí ukončení programu.

   Změny 6. srpna 2004
     - přidáno volání synthesizer.open() do metody initSequencer()

*/

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


public class PlayMidi implements MetaEventListener 
{
  // konstanta meta události midi pro oznámení konce stopy 
  private static final int END_OF_TRACK = 47;

  private final static String SOUND_DIR = "Sounds/";


  private Sequencer sequencer;
  private Synthesizer synthesizer;
  private Sequence seq = null;
  private String filename;

  private DecimalFormat df; 


  public PlayMidi(String fnm) 
  {
    df = new DecimalFormat("0.#");  // 1 dm

    filename = SOUND_DIR + fnm;
    initSequencer();
    loadMidi(filename);
    play();

    // posečkej, až zvuk dohraje; jeho délku odhadujeme max. na 10 minut!
    System.out.println("Waiting");
    try { 
      Thread.sleep(600000);   // 10 minut v milisekundách
    } 
    catch(InterruptedException e) 
    { System.out.println("Spánek byl přerušen"); }
  }  // konec metody: PlayMidi()


  private void initSequencer() 
  /* Připraví MIDI-sekvencer, naslouchač meta-událostí sekvenceru
     a jeho syntezátor. */
  {
    try {
      sequencer = MidiSystem.getSequencer();

      if (sequencer == null) {
        System.out.println("Nemohu získat sekvencer");
        System.exit(0);
      }

      sequencer.open();
      sequencer.addMetaEventListener(this);

      // sekvencer možná není totožný se syntezátorem
      // takže napoj sekvencer --> syntezátor (k tomu je třeba J2SE 1.5)
      if (!(sequencer instanceof Synthesizer)) {
        System.out.println("Spojování sekvenceru se syntezátorem");
        synthesizer = MidiSystem.getSynthesizer();
	    synthesizer.open();  // nový
        Receiver synthReceiver = synthesizer.getReceiver();
        Transmitter seqTransmitter = sequencer.getTransmitter();
        seqTransmitter.setReceiver(synthReceiver);  
      }
      else 
        synthesizer = (Synthesizer) sequencer;   
           // v tomto programu syntezátor nepoužíváme, 
           // takže jej nemusíme ukládat do globální proměnné
    }
    catch (MidiUnavailableException e){
      System.out.println("Žádný sekvencer není k dispozici");
      System.exit(0);
    }
  } // konec metody: initSequencer()


  private void loadMidi(String fnm)
  { 
    try {
      seq = MidiSystem.getSequence( getClass().getResource(fnm) );
      double duration = ((double) seq.getMicrosecondLength()) / 1000000;
      System.out.println("Délka: " +  df.format(duration) + " sekund");
    }
    catch (InvalidMidiDataException e) {
      System.out.println("Nečitelný/nepodporovaný midi-soubor: " + fnm);
      System.exit(0);
    }
    catch (IOException e) {
      System.out.println("Nemohu přečíst soubor: " + fnm);
      System.exit(0);
    }
    catch (Exception e) {
      System.out.println("Problém s " + fnm);
      System.exit(0);
    }
  } // konec metody: loadMidi()



  private void play()
  { 
    if ((sequencer != null) && (seq != null)) {
      try {
        sequencer.setSequence(seq);  // umísti MIDI do sekvenceru
        sequencer.start();   // spusť přehrávání it
      }
      catch (InvalidMidiDataException e) {
        System.out.println("Poškozený/neplatný soubor midi: " + filename);
        System.exit(0);
      }
    }
  } // konec metody: play()



  public void meta(MetaMessage event) 
  /* Tuto metodu spouští meta-události. 
     Meta-událost konec-stopy signalizuje, že sekvence skončila
  */
  { if (event.getType() == END_OF_TRACK) {
      System.out.println("Končím...");
      close();
      System.exit(0);    // v J2SE 1.4.2 nebo dříve nutné
    }
  } // konec metody: meta()


  private void close()
  // Uzavře sekvencer a syntezátor
  {
    if (sequencer != null) {
      if (sequencer.isRunning())
        sequencer.stop();
       
      sequencer.removeMetaEventListener(this);
      sequencer.close();

      if (synthesizer != null)
        synthesizer.close();
    }
  }  // konec metody: close()



  // ----------------------------------------------

  public static void main(String[] args) 
  {
    if (args.length != 1) {
      System.out.println("Použití: java PlayMidi <soubor midi>");
      System.exit(0);
    }
    new PlayMidi(args[0]);
    System.exit(0);    // do J2SE 1.4.2. musíme program ukončit explicitně
  } // konec metody: main()

} // konec třídy: PlayMidi
