133 lines
3.7 KiB
Java
133 lines
3.7 KiB
Java
|
|
package ibxm;
|
|
|
|
import java.io.*;
|
|
import javax.sound.sampled.*;
|
|
|
|
public class Player {
|
|
private Thread play_thread;
|
|
private IBXM ibxm;
|
|
private Module module;
|
|
private int song_duration, play_position;
|
|
private boolean running, loop;
|
|
private byte[] output_buffer;
|
|
private SourceDataLine output_line;
|
|
|
|
/**
|
|
Simple command-line test player.
|
|
*/
|
|
public static void main( String[] args ) throws Exception {
|
|
if( args.length < 1 ) {
|
|
System.err.println( "Usage: java ibxm.Player <module file>" );
|
|
System.exit( 0 );
|
|
}
|
|
FileInputStream file_input_stream = new FileInputStream( args[ 0 ] );
|
|
Player player = new Player();
|
|
player.set_module( Player.load_module( file_input_stream ) );
|
|
file_input_stream.close();
|
|
player.play();
|
|
}
|
|
|
|
/**
|
|
Decode the data in the specified InputStream into a Module instance.
|
|
@param input an InputStream containing the module file to be decoded.
|
|
@throws IllegalArgumentException if the data is not recognised as a module file.
|
|
*/
|
|
public static Module load_module( InputStream input ) throws IllegalArgumentException, IOException {
|
|
DataInputStream data_input_stream = new DataInputStream( input );
|
|
/* Check if data is in XM format.*/
|
|
byte[] xm_header = new byte[ 60 ];
|
|
data_input_stream.readFully( xm_header );
|
|
if( FastTracker2.is_xm( xm_header ) )
|
|
return FastTracker2.load_xm( xm_header, data_input_stream );
|
|
/* Check if data is in ScreamTracker 3 format.*/
|
|
byte[] s3m_header = new byte[ 96 ];
|
|
System.arraycopy( xm_header, 0, s3m_header, 0, 60 );
|
|
data_input_stream.readFully( s3m_header, 60, 36 );
|
|
if( ScreamTracker3.is_s3m( s3m_header ) )
|
|
return ScreamTracker3.load_s3m( s3m_header, data_input_stream );
|
|
/* Check if data is in ProTracker format.*/
|
|
byte[] mod_header = new byte[ 1084 ];
|
|
System.arraycopy( s3m_header, 0, mod_header, 0, 96 );
|
|
data_input_stream.readFully( mod_header, 96, 988 );
|
|
return ProTracker.load_mod( mod_header, data_input_stream );
|
|
}
|
|
|
|
/**
|
|
Instantiate a new Player.
|
|
*/
|
|
public Player() throws LineUnavailableException {
|
|
ibxm = new IBXM( 48000 );
|
|
set_loop( true );
|
|
output_line = AudioSystem.getSourceDataLine( new AudioFormat( 48000, 16, 2, true, true ) );
|
|
output_buffer = new byte[ 1024 * 4 ];
|
|
}
|
|
|
|
/**
|
|
Set the Module instance to be played.
|
|
*/
|
|
public void set_module( Module m ) {
|
|
if( m != null ) module = m;
|
|
stop();
|
|
ibxm.set_module( module );
|
|
song_duration = ibxm.calculate_song_duration();
|
|
}
|
|
|
|
/**
|
|
If loop is true, playback will continue indefinitely,
|
|
otherwise the module will play through once and stop.
|
|
*/
|
|
public void set_loop( boolean loop ) {
|
|
this.loop = loop;
|
|
}
|
|
|
|
/**
|
|
Open the audio device and begin playback.
|
|
If a module is already playing it will be restarted.
|
|
*/
|
|
public void play() {
|
|
stop();
|
|
play_thread = new Thread( new Driver() );
|
|
play_thread.start();
|
|
}
|
|
|
|
/**
|
|
Stop playback and close the audio device.
|
|
*/
|
|
public void stop() {
|
|
running = false;
|
|
if( play_thread != null ) {
|
|
try {
|
|
play_thread.join();
|
|
} catch( InterruptedException ie ) {}
|
|
}
|
|
}
|
|
|
|
private class Driver implements Runnable {
|
|
@Override
|
|
public void run() {
|
|
if( running ) return;
|
|
try {
|
|
output_line.open();
|
|
output_line.start();
|
|
play_position = 0;
|
|
running = true;
|
|
while( running ) {
|
|
int frames = song_duration - play_position;
|
|
if( frames > 1024 ) frames = 1024;
|
|
ibxm.get_audio( output_buffer, frames );
|
|
output_line.write( output_buffer, 0, frames * 4 );
|
|
play_position += frames;
|
|
if( play_position >= song_duration ) {
|
|
play_position = 0;
|
|
if( !loop ) running = false;
|
|
}
|
|
}
|
|
output_line.drain();
|
|
output_line.close();
|
|
} catch( LineUnavailableException lue ) {
|
|
lue.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
}
|