com.github.mathiewz.slick.ibxm.IBXM 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.nio.ByteOrder;
public class IBXM {
public static final String VERSION = "com.github.mathiewz.slick.ibxm alpha 45 (c)2006 [email protected]";
public static final int FP_SHIFT = 15;
public static final int FP_ONE = 1 << FP_SHIFT;
public static final int FP_MASK = FP_ONE - 1;
private final int sampling_rate;
private int resampling_quality;
private final int volume_ramp_length;
private int tick_length_samples, current_tick_samples;
private final int[] mixing_buffer, volume_ramp_buffer;
private Module module;
private Channel[] channels;
private final int[] global_volume, note;
private int current_sequence_index, next_sequence_index;
private int current_row, next_row;
private int tick_counter, ticks_per_row;
private int pattern_loop_count, pattern_loop_channel;
public IBXM(int sample_rate) {
System.out.println(VERSION);
if (sample_rate < 8000) {
sample_rate = 8000;
}
sampling_rate = sample_rate;
volume_ramp_length = sampling_rate >> 10;
volume_ramp_buffer = new int[volume_ramp_length * 2];
mixing_buffer = new int[sampling_rate / 6];
global_volume = new int[1];
note = new int[5];
set_module(new Module());
set_resampling_quality(1);
}
public void set_module(Module m) {
int channel_idx;
module = m;
channels = new Channel[module.get_num_channels()];
for (channel_idx = 0; channel_idx < channels.length; channel_idx++) {
channels[channel_idx] = new Channel(module, sampling_rate, global_volume);
}
set_sequence_index(0, 0);
}
public void set_resampling_quality(int quality) {
resampling_quality = quality;
}
public int calculate_song_duration() {
int song_duration;
set_sequence_index(0, 0);
next_tick();
song_duration = tick_length_samples;
while (!next_tick()) {
song_duration += tick_length_samples;
}
set_sequence_index(0, 0);
return song_duration;
}
public void set_sequence_index(int sequence_index, int row) {
int channel_idx;
global_volume[0] = 64;
for (channel_idx = 0; channel_idx < channels.length; channel_idx++) {
channels[channel_idx].reset();
channels[channel_idx].set_panning(module.get_initial_panning(channel_idx));
}
set_global_volume(module.global_volume);
set_speed(6);
set_speed(module.default_speed);
set_tempo(125);
set_tempo(module.default_tempo);
pattern_loop_count = -1;
next_sequence_index = sequence_index;
next_row = row;
tick_counter = 0;
current_tick_samples = tick_length_samples;
}
public void seek(int sample_position) {
set_sequence_index(0, 0);
next_tick();
while (sample_position > tick_length_samples) {
sample_position -= tick_length_samples;
next_tick();
}
next_tick();
current_tick_samples = sample_position;
}
public void get_audio(byte[] output_buffer, int frames) {
boolean bigEndian = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN);
int output_idx, mix_idx, mix_end, count, amplitude;
output_idx = 0;
while (frames > 0) {
count = tick_length_samples - current_tick_samples;
if (count > frames) {
count = frames;
}
mix_idx = current_tick_samples << 1;
mix_end = mix_idx + (count << 1) - 1;
while (mix_idx <= mix_end) {
amplitude = mixing_buffer[mix_idx];
if (amplitude > 32767) {
amplitude = 32767;
}
if (amplitude < -32768) {
amplitude = -32768;
}
if (bigEndian) {
output_buffer[output_idx] = (byte) (amplitude >> 8);
output_buffer[output_idx + 1] = (byte) (amplitude & 0xFF);
} else {
output_buffer[output_idx] = (byte) (amplitude & 0xFF);
output_buffer[output_idx + 1] = (byte) (amplitude >> 8);
}
output_idx += 2;
mix_idx += 1;
}
current_tick_samples = mix_idx >> 1;
frames -= count;
if (frames > 0) {
next_tick();
mix_tick();
current_tick_samples = 0;
}
}
}
private void mix_tick() {
int channel_idx, mix_idx, mix_len;
mix_idx = 0;
mix_len = tick_length_samples + volume_ramp_length << 1;
while (mix_idx < mix_len) {
mixing_buffer[mix_idx] = 0;
mix_idx += 1;
}
for (channel_idx = 0; channel_idx < channels.length; channel_idx++) {
mix_len = tick_length_samples + volume_ramp_length;
channels[channel_idx].resample(mixing_buffer, 0, mix_len, resampling_quality);
}
volume_ramp();
}
private boolean next_tick() {
int channel_idx;
boolean song_end;
for (channel_idx = 0; channel_idx < channels.length; channel_idx++) {
channels[channel_idx].update_sample_idx(tick_length_samples);
}
tick_counter -= 1;
if (tick_counter <= 0) {
tick_counter = ticks_per_row;
song_end = next_row();
} else {
for (channel_idx = 0; channel_idx < channels.length; channel_idx++) {
channels[channel_idx].tick();
}
song_end = false;
}
return song_end;
}
private boolean next_row() {
int channel_idx, effect, effect_param;
boolean song_end;
Pattern pattern;
song_end = false;
if (next_sequence_index < 0) {
/* Bad next sequence index. */
next_sequence_index = 0;
next_row = 0;
}
if (next_sequence_index >= module.get_sequence_length()) {
/* End of sequence. */
song_end = true;
next_sequence_index = module.restart_sequence_index;
if (next_sequence_index < 0) {
next_sequence_index = 0;
}
if (next_sequence_index >= module.get_sequence_length()) {
next_sequence_index = 0;
}
next_row = 0;
}
if (next_sequence_index < current_sequence_index) {
/* Jump to previous pattern. */
song_end = true;
}
if (next_sequence_index == current_sequence_index) {
if (next_row <= current_row) {
if (pattern_loop_count < 0) {
/* Jump to previous row in the same pattern, but not a pattern loop. */
song_end = true;
}
}
}
current_sequence_index = next_sequence_index;
pattern = module.get_pattern_from_sequence(current_sequence_index);
if (next_row < 0 || next_row >= pattern.num_rows) {
/* Bad next row. */
next_row = 0;
}
current_row = next_row;
next_row = current_row + 1;
if (next_row >= pattern.num_rows) {
next_sequence_index = current_sequence_index + 1;
next_row = 0;
}
for (channel_idx = 0; channel_idx < channels.length; channel_idx++) {
pattern.get_note(note, current_row * channels.length + channel_idx);
effect = note[3];
effect_param = note[4];
channels[channel_idx].row(note[0], note[1], note[2], effect, effect_param);
switch (effect) {
case 0x0B:
/* Pattern Jump. */
if (pattern_loop_count < 0) {
next_sequence_index = effect_param;
next_row = 0;
}
break;
case 0x0D:
/* Pattern Break. */
if (pattern_loop_count < 0) {
next_sequence_index = current_sequence_index + 1;
next_row = (effect_param >> 4) * 10 + (effect_param & 0x0F);
}
break;
case 0x0E:
/* Extended. */
switch (effect_param & 0xF0) {
case 0x60:
/* Pattern loop. */
if ((effect_param & 0x0F) == 0) {
/* Set loop marker on this channel. */
channels[channel_idx].pattern_loop_row = current_row;
}
if (channels[channel_idx].pattern_loop_row < current_row) {
/* Marker and parameter are valid. Begin looping. */
if (pattern_loop_count < 0) {
/* Not already looping, begin. */
pattern_loop_count = effect_param & 0x0F;
pattern_loop_channel = channel_idx;
}
if (pattern_loop_channel == channel_idx) {
/* Loop in progress on this channel. Next iteration. */
if (pattern_loop_count == 0) {
/* Loop finished. */
/* Invalidate current marker. */
channels[channel_idx].pattern_loop_row = current_row + 1;
} else {
/* Count must be higher than zero. */
/* Loop and cancel any breaks on this row. */
next_row = channels[channel_idx].pattern_loop_row;
next_sequence_index = current_sequence_index;
}
pattern_loop_count -= 1;
}
}
break;
case 0xE0:
/* Pattern delay. */
tick_counter += ticks_per_row * (effect_param & 0x0F);
break;
}
break;
case 0x0F:
/* Set Speed/Tempo. */
if (effect_param < 32) {
set_speed(effect_param);
tick_counter = ticks_per_row;
} else {
set_tempo(effect_param);
}
break;
case 0x25:
/* S3M Set Speed. */
set_speed(effect_param);
tick_counter = ticks_per_row;
break;
}
}
return song_end;
}
private void set_global_volume(int volume) {
if (volume < 0) {
volume = 0;
}
if (volume > 64) {
volume = 64;
}
global_volume[0] = volume;
}
private void set_speed(int speed) {
if (speed > 0 && speed < 256) {
ticks_per_row = speed;
}
}
private void set_tempo(int bpm) {
if (bpm > 31 && bpm < 256) {
tick_length_samples = sampling_rate * 5 / (bpm * 2);
}
}
private void volume_ramp() {
int ramp_idx, next_idx, ramp_end;
int volume_ramp_delta, volume, sample;
sample = 0;
volume_ramp_delta = FP_ONE / volume_ramp_length;
volume = 0;
ramp_idx = 0;
next_idx = 2 * tick_length_samples;
ramp_end = volume_ramp_length * 2 - 1;
while (ramp_idx <= ramp_end) {
sample = volume_ramp_buffer[ramp_idx] * (FP_ONE - volume) >> FP_SHIFT;
mixing_buffer[ramp_idx] = sample + (mixing_buffer[ramp_idx] * volume >> FP_SHIFT);
volume_ramp_buffer[ramp_idx] = mixing_buffer[next_idx + ramp_idx];
sample = volume_ramp_buffer[ramp_idx + 1] * (FP_ONE - volume) >> FP_SHIFT;
mixing_buffer[ramp_idx + 1] = sample + (mixing_buffer[ramp_idx + 1] * volume >> FP_SHIFT);
volume_ramp_buffer[ramp_idx + 1] = mixing_buffer[next_idx + ramp_idx + 1];
volume += volume_ramp_delta;
ramp_idx += 2;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy