001
002package ibxm;
003
004import java.io.*;
005import javax.sound.sampled.*;
006
007public class Player {
008    private Thread play_thread;
009    private IBXM ibxm;
010    private Module module;
011    private int song_duration, play_position;
012    private boolean running, loop;
013    private byte[] output_buffer;
014    private SourceDataLine output_line;
015
016    /**
017        Simple command-line test player.
018    */
019    public static void main( String[] args ) throws Exception {
020        if( args.length < 1 ) {
021            System.err.println( "Usage: java ibxm.Player <module file>" );
022            System.exit( 0 );
023        }
024        FileInputStream file_input_stream = new FileInputStream( args[ 0 ] );
025        Player player = new Player();
026        player.set_module( Player.load_module( file_input_stream ) );
027        file_input_stream.close();
028        player.play();
029    }
030    
031    /**
032        Decode the data in the specified InputStream into a Module instance.
033        @param input an InputStream containing the module file to be decoded.
034        @throws IllegalArgumentException if the data is not recognised as a module file.
035    */
036    public static Module load_module( InputStream input ) throws IllegalArgumentException, IOException {
037        DataInputStream data_input_stream = new DataInputStream( input );
038        /* Check if data is in XM format.*/
039        byte[] xm_header = new byte[ 60 ];
040        data_input_stream.readFully( xm_header );
041        if( FastTracker2.is_xm( xm_header ) )
042            return FastTracker2.load_xm( xm_header, data_input_stream );
043        /* Check if data is in ScreamTracker 3 format.*/    
044        byte[] s3m_header = new byte[ 96 ];
045        System.arraycopy( xm_header, 0, s3m_header, 0, 60 );
046        data_input_stream.readFully( s3m_header, 60, 36 );
047        if( ScreamTracker3.is_s3m( s3m_header ) )
048            return ScreamTracker3.load_s3m( s3m_header, data_input_stream );
049        /* Check if data is in ProTracker format.*/
050        byte[] mod_header = new byte[ 1084 ];
051        System.arraycopy( s3m_header, 0, mod_header, 0, 96 );
052        data_input_stream.readFully( mod_header, 96, 988 );
053            return ProTracker.load_mod( mod_header, data_input_stream );
054    }
055
056    /**
057        Instantiate a new Player.
058    */
059    public Player() throws LineUnavailableException {
060        ibxm = new IBXM( 48000 );
061        set_loop( true );
062        output_line = AudioSystem.getSourceDataLine( new AudioFormat( 48000, 16, 2, true, true ) );
063        output_buffer = new byte[ 1024 * 4 ];
064    }
065
066    /**
067        Set the Module instance to be played.
068    */
069    public void set_module( Module m ) {
070        if( m != null ) module = m;
071        stop();
072        ibxm.set_module( module );
073        song_duration = ibxm.calculate_song_duration();
074    }
075    
076    /**
077        If loop is true, playback will continue indefinitely,
078        otherwise the module will play through once and stop.
079    */
080    public void set_loop( boolean loop ) {
081        this.loop = loop;
082    }
083    
084    /**
085        Open the audio device and begin playback.
086        If a module is already playing it will be restarted.
087    */
088    public void play() {
089        stop();
090        play_thread = new Thread( new Driver() );
091        play_thread.start();
092    }
093    
094    /**
095        Stop playback and close the audio device.
096    */
097    public void stop() {
098        running = false;
099        if( play_thread != null ) {
100            try {
101                play_thread.join();
102            } catch( InterruptedException ie ) {}
103        }
104    }
105    
106    private class Driver implements Runnable {
107        public void run() {
108            if( running ) return;
109            try {
110                output_line.open();
111                output_line.start();
112                play_position = 0;
113                running = true;
114                while( running ) {
115                    int frames = song_duration - play_position;
116                    if( frames > 1024 ) frames = 1024;       
117                    ibxm.get_audio( output_buffer, frames );
118                    output_line.write( output_buffer, 0, frames * 4 );
119                    play_position += frames;
120                    if( play_position >= song_duration ) {
121                        play_position = 0;
122                        if( !loop ) running = false;
123                    }
124                }
125                output_line.drain();
126                output_line.close();
127            } catch( LineUnavailableException lue ) {
128                lue.printStackTrace();
129            }
130        }
131    }
132}