com.github.mathiewz.slick.ibxm.FastTracker2 Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of modernized-slick Show documentation
Show all versions of modernized-slick Show documentation
The main purpose of this libraryis to modernize and maintain the slick2D library.
The newest version!
package com.github.mathiewz.slick.ibxm;
import java.io.DataInput;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
public class FastTracker2 {
public static boolean is_xm(byte[] header_60_bytes) {
String xm_identifier;
xm_identifier = ascii_text(header_60_bytes, 0, 17);
return xm_identifier.equals("Extended Module: ");
}
public static Module load_xm(byte[] header_60_bytes, DataInput data_input) throws IOException {
int xm_version, song_header_length, sequence_length;
int num_channels, num_patterns, num_instruments, xm_flags, idx;
byte[] structure_header, song_header;
Module module;
if (!is_xm(header_60_bytes)) {
throw new IllegalArgumentException("Not an XM file!");
}
xm_version = unsigned_short_le(header_60_bytes, 58);
if (xm_version != 0x0104) {
throw new IllegalArgumentException("Sorry, XM version " + xm_version + " is not supported!");
}
module = new Module();
module.song_title = ascii_text(header_60_bytes, 17, 20);
structure_header = new byte[4];
data_input.readFully(structure_header);
song_header_length = int_le(structure_header, 0);
song_header = new byte[song_header_length];
data_input.readFully(song_header, 4, song_header_length - 4);
sequence_length = unsigned_short_le(song_header, 4);
module.restart_sequence_index = unsigned_short_le(song_header, 6);
num_channels = unsigned_short_le(song_header, 8);
num_patterns = unsigned_short_le(song_header, 10);
num_instruments = unsigned_short_le(song_header, 12);
xm_flags = unsigned_short_le(song_header, 14);
module.linear_periods = (xm_flags & 0x1) == 0x1;
module.global_volume = 64;
module.channel_gain = IBXM.FP_ONE * 3 / 8;
module.default_speed = unsigned_short_le(song_header, 16);
module.default_tempo = unsigned_short_le(song_header, 18);
module.set_num_channels(num_channels);
for (idx = 0; idx < num_channels; idx++) {
module.set_initial_panning(idx, 128);
}
module.set_sequence_length(sequence_length);
for (idx = 0; idx < sequence_length; idx++) {
module.set_sequence(idx, song_header[20 + idx] & 0xFF);
}
module.set_num_patterns(num_patterns);
for (idx = 0; idx < num_patterns; idx++) {
module.set_pattern(idx, read_xm_pattern(data_input, num_channels));
}
module.set_num_instruments(num_instruments);
for (idx = 1; idx <= num_instruments; idx++) {
module.set_instrument(idx, read_xm_instrument(data_input));
}
return module;
}
private static Pattern read_xm_pattern(DataInput data_input, int num_channels) throws IOException {
int pattern_header_length, packing_type, pattern_data_length;
byte[] structure_header, pattern_header, pattern_data;
Pattern pattern;
structure_header = new byte[4];
data_input.readFully(structure_header);
pattern_header_length = int_le(structure_header, 0);
pattern_header = new byte[pattern_header_length];
data_input.readFully(pattern_header, 4, pattern_header_length - 4);
packing_type = pattern_header[4];
if (packing_type != 0) {
throw new IllegalArgumentException("Pattern packing type " + packing_type + " is not supported!");
}
pattern = new Pattern();
pattern.num_rows = unsigned_short_le(pattern_header, 5);
pattern_data_length = unsigned_short_le(pattern_header, 7);
pattern_data = new byte[pattern_data_length];
data_input.readFully(pattern_data);
pattern.set_pattern_data(pattern_data);
return pattern;
}
private static Instrument read_xm_instrument(DataInput data_input) throws IOException {
int instrument_header_length, num_samples, idx;
int env_tick, env_ampl, env_num_points, flags;
byte[] structure_header, instrument_header, sample_headers;
Instrument instrument;
Envelope envelope;
structure_header = new byte[4];
data_input.readFully(structure_header);
instrument_header_length = int_le(structure_header, 0);
instrument_header = new byte[instrument_header_length];
data_input.readFully(instrument_header, 4, instrument_header_length - 4);
instrument = new Instrument();
instrument.name = ascii_text(instrument_header, 4, 22);
num_samples = unsigned_short_le(instrument_header, 27);
if (num_samples > 0) {
instrument.set_num_samples(num_samples);
for (idx = 0; idx < 96; idx++) {
instrument.set_key_to_sample(idx + 1, instrument_header[33 + idx] & 0xFF);
}
envelope = new Envelope();
env_num_points = instrument_header[225] & 0xFF;
envelope.set_num_points(env_num_points);
for (idx = 0; idx < env_num_points; idx++) {
env_tick = unsigned_short_le(instrument_header, 129 + idx * 4);
env_ampl = unsigned_short_le(instrument_header, 131 + idx * 4);
envelope.set_point(idx, env_tick, env_ampl);
}
envelope.set_sustain_point(instrument_header[227] & 0xFF);
envelope.set_loop_points(instrument_header[228] & 0xFF, instrument_header[229] & 0xFF);
flags = instrument_header[233] & 0xFF;
instrument.volume_envelope_active = (flags & 0x1) == 0x1;
envelope.sustain = (flags & 0x2) == 0x2;
envelope.looped = (flags & 0x4) == 0x4;
instrument.set_volume_envelope(envelope);
envelope = new Envelope();
env_num_points = instrument_header[226] & 0xFF;
envelope.set_num_points(env_num_points);
for (idx = 0; idx < env_num_points; idx++) {
env_tick = unsigned_short_le(instrument_header, 177 + idx * 4);
env_ampl = unsigned_short_le(instrument_header, 179 + idx * 4);
envelope.set_point(idx, env_tick, env_ampl);
}
envelope.set_sustain_point(instrument_header[230] & 0xFF);
envelope.set_loop_points(instrument_header[231] & 0xFF, instrument_header[232] & 0xFF);
flags = instrument_header[234] & 0xFF;
instrument.panning_envelope_active = (flags & 0x1) == 0x1;
envelope.sustain = (flags & 0x2) == 0x2;
envelope.looped = (flags & 0x4) == 0x4;
instrument.set_panning_envelope(envelope);
instrument.vibrato_type = instrument_header[235] & 0xFF;
instrument.vibrato_sweep = instrument_header[236] & 0xFF;
instrument.vibrato_depth = instrument_header[237] & 0xFF;
instrument.vibrato_rate = instrument_header[238] & 0xFF;
instrument.volume_fade_out = unsigned_short_le(instrument_header, 239);
sample_headers = new byte[num_samples * 40];
data_input.readFully(sample_headers);
for (idx = 0; idx < num_samples; idx++) {
instrument.set_sample(idx, read_xm_sample(sample_headers, idx, data_input));
}
}
return instrument;
}
private static Sample read_xm_sample(byte[] sample_headers, int sample_idx, DataInput data_input) throws IOException {
int header_offset, sample_length, loop_start, loop_length;
int flags, in_idx, out_idx, sam, last_sam;
boolean sixteen_bit, ping_pong;
byte[] raw_sample_data;
short[] decoded_sample_data;
Sample sample;
header_offset = sample_idx * 40;
sample = new Sample();
sample_length = int_le(sample_headers, header_offset);
loop_start = int_le(sample_headers, header_offset + 4);
loop_length = int_le(sample_headers, header_offset + 8);
sample.volume = sample_headers[header_offset + 12] & 0xFF;
sample.fine_tune = sample_headers[header_offset + 13];
sample.set_panning = true;
flags = sample_headers[header_offset + 14] & 0xFF;
if ((flags & 0x03) == 0) {
loop_length = 0;
}
ping_pong = (flags & 0x02) == 0x02;
sixteen_bit = (flags & 0x10) == 0x10;
sample.panning = sample_headers[header_offset + 15] & 0xFF;
sample.relative_note = sample_headers[header_offset + 16];
sample.name = ascii_text(sample_headers, header_offset + 18, 22);
raw_sample_data = new byte[sample_length];
data_input.readFully(raw_sample_data);
in_idx = 0;
out_idx = 0;
sam = 0;
last_sam = 0;
if (sixteen_bit) {
decoded_sample_data = new short[sample_length >> 1];
while (in_idx < raw_sample_data.length) {
sam = raw_sample_data[in_idx] & 0xFF;
sam = sam | (raw_sample_data[in_idx + 1] & 0xFF) << 8;
last_sam = last_sam + sam;
decoded_sample_data[out_idx] = (short) last_sam;
in_idx += 2;
out_idx += 1;
}
sample.set_sample_data(decoded_sample_data, loop_start >> 1, loop_length >> 1, ping_pong);
} else {
decoded_sample_data = new short[sample_length];
while (in_idx < raw_sample_data.length) {
sam = raw_sample_data[in_idx] & 0xFF;
last_sam = last_sam + sam;
decoded_sample_data[out_idx] = (short) (last_sam << 8);
in_idx += 1;
out_idx += 1;
}
sample.set_sample_data(decoded_sample_data, loop_start, loop_length, ping_pong);
}
return sample;
}
private static int unsigned_short_le(byte[] buffer, int offset) {
int value;
value = buffer[offset] & 0xFF;
value = value | (buffer[offset + 1] & 0xFF) << 8;
return value;
}
private static int int_le(byte[] buffer, int offset) {
int value;
value = buffer[offset] & 0xFF;
value = value | (buffer[offset + 1] & 0xFF) << 8;
value = value | (buffer[offset + 2] & 0xFF) << 16;
value = value | (buffer[offset + 3] & 0x7F) << 24;
return value;
}
private static String ascii_text(byte[] buffer, int offset, int length) {
int idx, chr;
byte[] string_buffer;
String string;
string_buffer = new byte[length];
for (idx = 0; idx < length; idx++) {
chr = buffer[offset + idx];
if (chr < 32) {
chr = 32;
}
string_buffer[idx] = (byte) chr;
}
try {
string = new String(string_buffer, 0, length, "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
string = "";
}
return string;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy