import java.io.*;
import javax.sound.midi.*;

public class MidiSong implements MetaEventListener {
    // MIDI meta-event constant used to signal the
    // end of a track
    public static final int END_OF_TRACK = 47;

    private Sequencer sequencer;
    private Sequence song;
    private boolean loop;

    // Driver to play midi files
    public static void main(String[] args) {
        MidiSong midi =
            new MidiSong("sounds/bsg.midi");
    }

    public MidiSong(String filename) {
        try {
            song = MidiSystem.getSequence(
                getClass().getResource(filename));
            sequencer = MidiSystem.getSequencer();
            sequencer.setSequence(song);
            sequencer.open();
            sequencer.addMetaEventListener(this);
            sequencer.start();
        } catch (InvalidMidiDataException e) {
            System.out.println("Bad midi file: "
                + filename);
            System.exit(1);
        } catch (MidiUnavailableException e) {
            System.out.println("No sequencer available");
            System.exit(1);
        } catch (IOException e) {
            System.out.println("Could not read: "
                + filename);
            System.exit(1);
        }
        displayMidiInfo(filename);
    }

    private void displayMidiInfo(String filename) {
        System.out.println("Midi File: " + filename);
        System.out.println("  Timing resolution: "
            + song.getResolution());
        System.out.println("  Number of ticks: "
            + song.getTickLength());
        System.out.println("  Number of tracks: "
            + song.getTracks().length);
        System.out.println("  Number of patches: "
            + song.getPatchList().length);
        long lengthMillis =
            song.getMicrosecondLength() / 1000L;
        System.out.println("  Duration: "
            + (lengthMillis / 1000.0) + " secs");
    }

    // Called by the sound system when a meta event occurs
    public void meta(MetaMessage event) {
        if (event.getType() == END_OF_TRACK) {
            sleep(300); // let buffer clear?
            close();
            System.exit(0);
        }
    }

    // Close the sequencer.
    public void close() {
        if (sequencer.isOpen()) {
            sequencer.close();
        }
    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch(InterruptedException e) {
            System.out.println("Sleep Interrupted");
        }
    }
}

