    001    package ibxm;
    002
    003    import java.io.*;
    004
    005    public class ProTracker {
    006        public static boolean is_mod( byte[] header_1084_bytes ) {
    007            boolean is_mod;
    008            is_mod = false;
    009            if( calculate_num_channels( header_1084_bytes ) > 0 ) {
    010                is_mod = true;
    011            }
    012            return is_mod;
    013        }
    014
    015        public static Module load_mod( byte[] header_1084_bytes, DataInput data_input ) throws IOException {
    016            int num_channels, channel_idx, panning;
    017            int sequence_length, restart_idx, sequence_idx;
    018            int num_patterns, pattern_idx, instrument_idx;
    019            Module module;
    020            num_channels = calculate_num_channels( header_1084_bytes );
    021            if( num_channels < 1 ) {
    022                throw new IllegalArgumentException( "ProTracker: Unrecognised module format!" );
    023            }
    024            module = new Module();
    025            module.song_title = ascii_text( header_1084_bytes, 0, 20 );
    026            module.pal = ( num_channels == 4 );
    027            module.global_volume = 64;
    028            module.channel_gain = IBXM.FP_ONE * 3 / 8;
    029            module.default_speed = 6;
    030            module.default_tempo = 125;
    031            module.set_num_channels( num_channels );
    032            for( channel_idx = 0; channel_idx < num_channels; channel_idx++ ) {
    033                panning = 64;
    034                if( ( channel_idx & 0x03 ) == 0x01 || ( channel_idx & 0x03 ) == 0x02 ) {
    035                    panning = 192;
    036                }
    037                module.set_initial_panning( channel_idx, panning );
    038            }
    039            sequence_length = header_1084_bytes[ 950 ] & 0x7F;
    040            restart_idx = header_1084_bytes[ 951 ] & 0x7F;
    041            if( restart_idx >= sequence_length ) {
    042                restart_idx = 0;
    043            }
    044            module.restart_sequence_index = restart_idx;
    045            module.set_sequence_length( sequence_length );
    046            for( sequence_idx = 0; sequence_idx < sequence_length; sequence_idx++ ) {
    047                module.set_sequence( sequence_idx, header_1084_bytes[ 952 + sequence_idx ] & 0x7F );
    048            }
    049            num_patterns = calculate_num_patterns( header_1084_bytes );
    050            module.set_num_patterns( num_patterns );
    051            for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
    052                module.set_pattern( pattern_idx, read_mod_pattern( data_input, num_channels ) );
    053            }
    054            module.set_num_instruments( 31 );
    055            for( instrument_idx = 1; instrument_idx <= 31; instrument_idx++ ) {
    056                module.set_instrument( instrument_idx, read_mod_instrument( header_1084_bytes, instrument_idx, data_input ) );
    057            }
    058            return module;
    059        }
    060
    061        private static int calculate_num_patterns( byte[] module_header ) {
    062            int num_patterns, order_entry, pattern_idx;
    063            num_patterns = 0;
    064            for( pattern_idx = 0; pattern_idx < 128; pattern_idx++ ) {
    065                order_entry = module_header[ 952 + pattern_idx ] & 0x7F;
    066                if( order_entry >= num_patterns ) {
    067                    num_patterns = order_entry + 1;
    068                }
    069            }
    070            return num_patterns;
    071        }
    072
    073        private static int calculate_num_channels( byte[] module_header ) {
    074            int num_channels;
    075            switch( ( module_header[ 1082 ] << 8 ) | module_header[ 1083 ] ) {
    076                case 0x4b2e: /* M.K. */
    077                case 0x4b21: /* M!K! */
    078                case 0x542e: /* N.T. */
    079                case 0x5434: /* FLT4 */
    080                    num_channels = 4;
    081                    break;
    082                case 0x484e: /* xCHN */
    083                    num_channels = module_header[ 1080 ] - 48;
    084                    break;
    085                case 0x4348: /* xxCH */
    086                    num_channels = ( ( module_header[ 1080 ] - 48 ) * 10 ) + ( module_header[ 1081 ] - 48 );
    087                    break;
    088                default:
    089                    /* Not recognised. */
    090                    num_channels = 0;
    091                    break;
    092            }
    093            return num_channels;
    094        }
    095
    096        private static Pattern read_mod_pattern( DataInput data_input, int num_channels ) throws IOException {
    097            int input_idx, output_idx;
    098            int period, instrument, effect, effect_param;
    099            Pattern pattern;
    100            byte[] input_pattern_data, output_pattern_data;
    101            pattern = new Pattern();
    102            pattern.num_rows = 64;
    103            input_pattern_data = new byte[ 64 * num_channels * 4 ];
    104            output_pattern_data = new byte[ 64 * num_channels * 5 ];
    105            data_input.readFully( input_pattern_data );
    106            input_idx = 0;
    107            output_idx = 0;
    108            while( input_idx < input_pattern_data.length ) {
    109                period = ( input_pattern_data[ input_idx ] & 0x0F ) << 8;
    110                period = period | ( input_pattern_data[ input_idx + 1 ] & 0xFF );
    111                output_pattern_data[ output_idx ] = to_key( period );
    112                instrument = input_pattern_data[ input_idx ] & 0x10;
    113                instrument = instrument | ( ( input_pattern_data[ input_idx + 2 ] & 0xF0 ) >> 4 );
    114                output_pattern_data[ output_idx + 1 ] = ( byte ) instrument;
    115                effect = input_pattern_data[ input_idx + 2 ] & 0x0F;
    116                effect_param = input_pattern_data[ input_idx + 3 ] & 0xFF;
    117                if( effect == 0x01 && effect_param == 0 ) {
    118                    /* Portamento up of zero has no effect. */
    119                    effect = 0;
    120                }
    121                if( effect == 0x02 && effect_param == 0 ) {
    122                    /* Portamento down of zero has no effect. */
    123                    effect = 0;
    124                }
    125                if( effect == 0x08 && num_channels == 4 ) {
    126                    /* Some Amiga mods use effect 0x08 for reasons other than panning.*/
    127                    effect = 0;
    128                    effect_param = 0;
    129                }
    130                if( effect == 0x0A && effect_param == 0 ) {
    131                    /* Volume slide of zero has no effect.*/
    132                    effect = 0;
    133                }
    134                if( effect == 0x05 && effect_param == 0 ) {
    135                    /* Porta + Volume slide of zero has no effect.*/
    136                    effect = 0x03;
    137                }
    138                if( effect == 0x06 && effect_param == 0 ) {
    139                    /* Vibrato + Volume slide of zero has no effect.*/
    140                    effect = 0x04;
    141                }
    142                output_pattern_data[ output_idx + 3 ] = ( byte ) effect;
    143                output_pattern_data[ output_idx + 4 ] = ( byte ) effect_param;
    144                input_idx += 4;
    145                output_idx += 5;
    146            }
    147            pattern.set_pattern_data( output_pattern_data );
    148            return pattern;
    149        }
    150
    151        private static Instrument read_mod_instrument( byte[] mod_header, int idx, DataInput data_input ) throws IOException  {
    152            int header_offset, sample_data_length;
    153            int loop_start, loop_length, sample_idx, fine_tune;
    154            Instrument instrument;
    155            Sample sample;
    156            byte[] raw_sample_data;
    157            short[] sample_data;
    158            header_offset = ( idx - 1 ) * 30 + 20;
    159            instrument = new Instrument();
    160            instrument.name = ascii_text( mod_header, header_offset, 22 );
    161            sample = new Sample();
    162            sample_data_length = unsigned_short_be( mod_header, header_offset + 22 ) << 1;
    163            fine_tune = mod_header[ header_offset + 24 ] & 0x0F;
    164            if( fine_tune > 7 ) {
    165                fine_tune -= 16;
    166            }
    167            sample.transpose = ( fine_tune << IBXM.FP_SHIFT ) / 96;
    168            sample.volume = mod_header[ header_offset + 25 ] & 0x7F;
    169            loop_start = unsigned_short_be( mod_header, header_offset + 26 ) << 1;
    170            loop_length = unsigned_short_be( mod_header, header_offset + 28 ) << 1;
    171            if( loop_length < 4 ) {
    172                loop_length = 0;
    173            }
    174            raw_sample_data = new byte[ sample_data_length ];
    175            sample_data = new short[ sample_data_length ];
    176            try {
    177                data_input.readFully( raw_sample_data );
    178            } catch( EOFException e ) {
    179                System.out.println( "ProTracker: Instrument " + idx + " has samples missing." );
    180            }
    181            for( sample_idx = 0; sample_idx < raw_sample_data.length; sample_idx++ ) {
    182                sample_data[ sample_idx ] = ( short ) ( raw_sample_data[ sample_idx ] << 8 );
    183            }
    184            sample.set_sample_data( sample_data, loop_start, loop_length, false );
    185            instrument.set_num_samples( 1 );
    186            instrument.set_sample( 0, sample );
    187            return instrument;
    188        }
    189
    190        private static byte to_key( int period ) {
    191            int oct, key;
    192            if( period < 32 ) {
    193                key = 0;
    194            } else {
    195                oct = LogTable.log_2( 7256 ) - LogTable.log_2( period );
    196                if( oct < 0 ) {
    197                    key = 0;
    198                } else {
    199                    key = oct * 12;
    200                    key = key >> ( IBXM.FP_SHIFT - 1 );
    201                    key = ( key >> 1 ) + ( key & 1 );
    202                }
    203            }
    204            return ( byte ) key;
    205        }
    206
    207        private static int unsigned_short_be( byte[] buf, int offset ) {
    208            int value;
    209            value = ( buf[ offset ] & 0xFF ) << 8;
    210            value = value | ( buf[ offset + 1 ] & 0xFF );
    211            return value;
    212        }
    213
    214        private static String ascii_text( byte[] buffer, int offset, int length ) {
    215            int idx, chr;
    216            byte[] string_buffer;
    217            String string;
    218            string_buffer = new byte[ length ];
    219            for( idx = 0; idx < length; idx++ ) {
    220                chr = buffer[ offset + idx ];
    221                if( chr < 32 ) {
    222                    chr = 32;
    223                }
    224                string_buffer[ idx ] = ( byte ) chr;
    225            }
    226            try {
    227                string = new String( string_buffer, 0, length, "ISO-8859-1" );
    228            } catch( UnsupportedEncodingException e ) {
    229                string = "";
    230            }
    231            return string;
    232        }
    233    }
    234