    001
    002    package ibxm;
    003
    004    import java.io.*;
    005    import javax.sound.sampled.*;
    006
    007    public 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    }