com.github.mathiewz.slick.ibxm.ProTracker 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.EOFException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
public class ProTracker {
public static boolean isMod(byte[] header_1084_bytes) {
return calculate_num_channels(header_1084_bytes) > 0;
}
public static Module load_mod(byte[] header_1084_bytes, DataInput data_input) throws IOException {
int num_channels, channel_idx, panning;
int sequence_length, restart_idx, sequence_idx;
int num_patterns, pattern_idx, instrument_idx;
Module module;
num_channels = calculate_num_channels(header_1084_bytes);
if (num_channels < 1) {
throw new IllegalArgumentException("ProTracker: Unrecognised module format!");
}
module = new Module();
module.song_title = ascii_text(header_1084_bytes, 0, 20);
module.global_volume = 64;
module.channel_gain = IBXM.FP_ONE * 3 / 8;
module.default_speed = 6;
module.default_tempo = 125;
module.set_num_channels(num_channels);
for (channel_idx = 0; channel_idx < num_channels; channel_idx++) {
panning = 64;
if ((channel_idx & 0x03) == 0x01 || (channel_idx & 0x03) == 0x02) {
panning = 192;
}
module.set_initial_panning(channel_idx, panning);
}
sequence_length = header_1084_bytes[950] & 0x7F;
restart_idx = header_1084_bytes[951] & 0x7F;
if (restart_idx >= sequence_length) {
restart_idx = 0;
}
module.restart_sequence_index = restart_idx;
module.set_sequence_length(sequence_length);
for (sequence_idx = 0; sequence_idx < sequence_length; sequence_idx++) {
module.set_sequence(sequence_idx, header_1084_bytes[952 + sequence_idx] & 0x7F);
}
num_patterns = calculate_num_patterns(header_1084_bytes);
module.set_num_patterns(num_patterns);
for (pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++) {
module.set_pattern(pattern_idx, read_mod_pattern(data_input, num_channels));
}
module.set_num_instruments(31);
for (instrument_idx = 1; instrument_idx <= 31; instrument_idx++) {
module.set_instrument(instrument_idx, read_mod_instrument(header_1084_bytes, instrument_idx, data_input));
}
return module;
}
private static int calculate_num_patterns(byte[] module_header) {
int num_patterns, order_entry, pattern_idx;
num_patterns = 0;
for (pattern_idx = 0; pattern_idx < 128; pattern_idx++) {
order_entry = module_header[952 + pattern_idx] & 0x7F;
if (order_entry >= num_patterns) {
num_patterns = order_entry + 1;
}
}
return num_patterns;
}
private static int calculate_num_channels(byte[] module_header) {
int num_channels;
switch (module_header[1082] << 8 | module_header[1083]) {
case 0x4b2e: /* M.K. */
case 0x4b21: /* M!K! */
case 0x542e: /* N.T. */
case 0x5434: /* FLT4 */
num_channels = 4;
break;
case 0x484e: /* xCHN */
num_channels = module_header[1080] - 48;
break;
case 0x4348: /* xxCH */
num_channels = (module_header[1080] - 48) * 10 + module_header[1081] - 48;
break;
default:
/* Not recognised. */
num_channels = 0;
break;
}
return num_channels;
}
private static Pattern read_mod_pattern(DataInput data_input, int num_channels) throws IOException {
int input_idx, output_idx;
int period, instrument, effect, effect_param;
Pattern pattern;
byte[] input_pattern_data, output_pattern_data;
pattern = new Pattern();
pattern.num_rows = 64;
input_pattern_data = new byte[64 * num_channels * 4];
output_pattern_data = new byte[64 * num_channels * 5];
data_input.readFully(input_pattern_data);
input_idx = 0;
output_idx = 0;
while (input_idx < input_pattern_data.length) {
period = (input_pattern_data[input_idx] & 0x0F) << 8;
period = period | input_pattern_data[input_idx + 1] & 0xFF;
output_pattern_data[output_idx] = to_key(period);
instrument = input_pattern_data[input_idx] & 0x10;
instrument = instrument | (input_pattern_data[input_idx + 2] & 0xF0) >> 4;
output_pattern_data[output_idx + 1] = (byte) instrument;
effect = input_pattern_data[input_idx + 2] & 0x0F;
effect_param = input_pattern_data[input_idx + 3] & 0xFF;
if (effect == 0x08 && num_channels == 4) {
/* Some Amiga mods use effect 0x08 for reasons other than panning. */
effect = 0;
effect_param = 0;
}
if (effect == 0x0A && effect_param == 0) {
/* Volume slide of zero has no effect. */
effect = 0;
}
if (effect == 0x05 && effect_param == 0) {
/* Porta + Volume slide of zero has no effect. */
effect = 0x03;
}
if (effect == 0x06 && effect_param == 0) {
/* Vibrato + Volume slide of zero has no effect. */
effect = 0x04;
}
output_pattern_data[output_idx + 3] = (byte) effect;
output_pattern_data[output_idx + 4] = (byte) effect_param;
input_idx += 4;
output_idx += 5;
}
pattern.set_pattern_data(output_pattern_data);
return pattern;
}
private static Instrument read_mod_instrument(byte[] mod_header, int idx, DataInput data_input) throws IOException {
int header_offset, sample_data_length;
int loop_start, loop_length, sample_idx;
Instrument instrument;
Sample sample;
byte[] raw_sample_data;
short[] sample_data;
header_offset = (idx - 1) * 30 + 20;
instrument = new Instrument();
instrument.name = ascii_text(mod_header, header_offset, 22);
sample = new Sample();
sample.c2_rate = 8287;
sample_data_length = unsigned_short_be(mod_header, header_offset + 22) << 1;
sample.fine_tune = (mod_header[header_offset + 24] & 0x0F) << 4;
if (sample.fine_tune > 127) {
sample.fine_tune -= 256;
}
sample.volume = mod_header[header_offset + 25] & 0x7F;
loop_start = unsigned_short_be(mod_header, header_offset + 26) << 1;
loop_length = unsigned_short_be(mod_header, header_offset + 28) << 1;
if (loop_length < 4) {
loop_length = 0;
}
raw_sample_data = new byte[sample_data_length];
sample_data = new short[sample_data_length];
try {
data_input.readFully(raw_sample_data);
} catch (EOFException e) {
System.out.println("ProTracker: Instrument " + idx + " has samples missing.");
}
for (sample_idx = 0; sample_idx < raw_sample_data.length; sample_idx++) {
sample_data[sample_idx] = (short) (raw_sample_data[sample_idx] << 8);
}
sample.set_sample_data(sample_data, loop_start, loop_length, false);
instrument.set_num_samples(1);
instrument.set_sample(0, sample);
return instrument;
}
private static byte to_key(int period) {
int oct, key;
if (period < 32) {
key = 0;
} else {
oct = LogTable.log2(7256) - LogTable.log2(period);
if (oct < 0) {
key = 0;
} else {
key = oct * 12;
key = key >> IBXM.FP_SHIFT - 1;
key = (key >> 1) + (key & 1);
}
}
return (byte) key;
}
private static int unsigned_short_be(byte[] buf, int offset) {
int value;
value = (buf[offset] & 0xFF) << 8;
value = value | buf[offset + 1] & 0xFF;
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