/*
Tiamat - a role-playing game framework
MidiPlayer.java - plays midi files
 
 Copyright  (C) 2004-2005 Hugues Johnson
 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
the GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

package com.huguesjohnson.tiamat.audio;

import java.io.IOException;
import java.io.File;
import java.lang.String;
import java.lang.System;
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.Sequencer;
import com.huguesjohnson.tiamat.audio.AudioPlayer;

/** MidiPlayer - plays midi files
 * @author Hugues Johnson
 */
public class MidiPlayer extends AudioPlayer implements MetaEventListener{
    
/* begin private members */
    //actually plays the midi
    private Sequencer sequencer;
/* end private members */
    
/* begin public methods */
    /* (non-Javadoc)
     * @see com.huguesjohnson.tiamat.audio.AudioPlayer#play(java.lang.String)
     */
    public void play(String path){
        this.loop=0;
        this.trackIndex=0;
        this.trackList=new String[1];
        this.trackList[0]=path;
        this.startSequencer();
    }
    /* (non-Javadoc)
     * @see com.huguesjohnson.tiamat.audio.AudioPlayer#play(java.lang.String, int)
     */
    public void play(String path,int loop){
        this.loop=loop;
        this.trackIndex=0;
        this.trackList=new String[1];
        this.trackList[0]=path;
        this.startSequencer();
    }
    /* (non-Javadoc)
     * @see com.huguesjohnson.tiamat.audio.AudioPlayer#play(java.lang.String[])
     */
    public void play(String[] paths){
        this.loop=0;
        this.trackIndex=0;
        int length=paths.length;
        this.trackList=new String[length];
        System.arraycopy(paths,0,this.trackList,0,length);
        this.startSequencer();
    }
    /* (non-Javadoc)
     * @see com.huguesjohnson.tiamat.audio.AudioPlayer#play(java.lang.String[], int)
     */
    public void play(String[] paths,int loop){
        this.loop=loop;
        this.trackIndex=0;
        int length=paths.length;
        this.trackList=new String[length];
        System.arraycopy(paths,0,this.trackList,0,length);
        this.startSequencer();
    }
    /* (non-Javadoc)
     * @see com.huguesjohnson.tiamat.audio.AudioPlayer#stop()
     */
    public void stop(){
        if(this.sequencer!=null){
            if(this.sequencer.isRunning()){
                this.sequencer.stop();
            }
        }
    }
    /** Trap meta events. Why isn't there a simple way to loop midi? Let me get this straight.. the only way to loop a midi is to:
     * <ul>
     * <li>1) register a MetaEventListener</li>
     * <li>2) override the meta() method.. whatever that does</li>
     * <li>3) look for type 47, of course this is not enumerated anywhere</li>
     * <li>4) manually restart the sequencer</li>
     * Someone please explain to me why this is better than sequencer.setLoop(true)?
     * @param metaMessage I have no idea.
     */
    public void meta(MetaMessage metaMessage){
        if(metaMessage.getType()==47){
            //move to the next track
            this.trackIndex++;
            try{
                //are we at the end of the track list?
                if(this.trackIndex<this.trackList.length){
                    this.sequencer.setSequence(MidiSystem.getSequence(new File(this.trackList[this.trackIndex])));
                    this.sequencer.start();
                } else{ 
                    //are we supposed to loop? (if loop==0 then do not loop)
                    if(this.loop!=0){
                        //go back to the start of the track list
                        this.trackIndex=0;
                        //if loop>0 then decrement number of times we're supposed to loop
                        if(this.loop>0){ this.loop--; }
                        //restart the sequencer
                        this.sequencer.setSequence(MidiSystem.getSequence(new File(this.trackList[0])));
                        this.sequencer.start();                        
                    }
                }
            } catch(InvalidMidiDataException imdx){
                imdx.printStackTrace();                
            } catch(IOException iox){
                iox.printStackTrace();
            }
        }
    }
/* end public methods */
    
/* begin private methods */
    //starts the sequencer
    private void startSequencer(){
        try{
            /* declared final so anonymous MetaEventListener can be added */
            this.sequencer=MidiSystem.getSequencer();
            if(this.sequencer!=null){
                this.sequencer.addMetaEventListener(this);
                this.sequencer.open();
                this.sequencer.setSequence(MidiSystem.getSequence(new File(this.trackList[0])));
                this.sequencer.start();
            } else{
                System.err.println("Can't obtain a midi sequencer!");
            }
        } catch(InvalidMidiDataException imdx){
            imdx.printStackTrace();
        } catch(MidiUnavailableException mux){
            mux.printStackTrace();
        } catch(IOException iox){
            iox.printStackTrace();
        }
    }
/* end private methods */
}