com.github.mathiewz.slick.ibxm.Channel 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;
public class Channel {
public int pattern_loop_row;
private final Module module;
private Instrument instrument;
private Sample sample;
private final int[] global_volume, current_note;
private final boolean linear_periods, fast_volume_slides;
private boolean key_on;
private boolean silent;
private int sample_idx, sample_frac, step, left_gain, right_gain;
private int volume, panning, period, porta_period, key_add;
private int tremolo_speed, tremolo_depth, tremolo_tick, tremolo_wave, tremolo_add;
private int vibrato_speed, vibrato_depth, vibrato_tick, vibrato_wave, vibrato_add;
private int volume_slide_param, portamento_param, retrig_param;
private int volume_envelope_tick, panning_envelope_tick;
private int effect_tick, trigger_tick, fade_out_volume, random_seed;
private final int log_2_sampling_rate;
private int log_2_c2_rate;
private static final int LOG_2_29024 = LogTable.log2(29024);
private static final int LOG_2_1712 = LogTable.log2(1712);
private static final int[] sine_table = new int[] { 0, 24, 49, 74, 97, 120, 141, 161, 180, 197, 212, 224, 235, 244, 250, 253, 255, 253, 250, 244, 235, 224, 212, 197, 180, 161, 141, 120, 97, 74, 49, 24 };
public Channel(Module mod, int sampling_rate, int[] global_vol) {
module = mod;
global_volume = global_vol;
linear_periods = module.linear_periods;
fast_volume_slides = module.fast_volume_slides;
current_note = new int[5];
log_2_sampling_rate = LogTable.log2(sampling_rate);
}
public void reset() {
tremolo_speed = 0;
tremolo_depth = 0;
tremolo_wave = 0;
vibrato_speed = 0;
vibrato_depth = 0;
vibrato_wave = 0;
volume_slide_param = 0;
portamento_param = 0;
retrig_param = 0;
random_seed = 0xABC123;
instrument = module.get_instrument(0);
row(48, 256, 0, 0, 0);
}
public void resample(int[] mixing_buffer, int frame_offset, int frames, int quality) {
if (!silent) {
switch (quality) {
default:
sample.resample_nearest(sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames);
break;
case 1:
sample.resample_linear(sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames);
break;
case 2:
sample.resample_sinc(sample_idx, sample_frac, step, left_gain, right_gain, mixing_buffer, frame_offset, frames);
break;
}
}
}
public void update_sample_idx(int samples) {
sample_frac += step * samples;
sample_idx += sample_frac >> IBXM.FP_SHIFT;
sample_frac &= IBXM.FP_MASK;
}
public void set_volume(int vol) {
if (vol < 0) {
vol = 0;
}
if (vol > 64) {
vol = 64;
}
volume = vol;
}
public void set_panning(int pan) {
if (pan < 0) {
pan = 0;
}
if (pan > 255) {
pan = 255;
}
panning = pan;
}
public void row(int key, int inst_idx, int volume_column, int effect, int effect_param) {
effect = effect & 0xFF;
if (effect >= 0x30) {
/* Effects above 0x30 are internal. */
effect = 0;
}
if (effect == 0x00 && effect_param != 0) {
/* Arpeggio. */
effect = 0x40;
}
if (effect == 0x0E) {
/* Renumber 0x0Ex effect command. */
effect = 0x30 + ((effect_param & 0xF0) >> 4);
effect_param = effect_param & 0x0F;
}
if (effect == 0x21) {
/* Renumber 0x21x effect command. */
effect = 0x40 + ((effect_param & 0xF0) >> 4);
effect_param = effect_param & 0x0F;
}
current_note[0] = key;
current_note[1] = inst_idx;
current_note[2] = volume_column;
current_note[3] = effect;
current_note[4] = effect_param;
effect_tick = 0;
trigger_tick += 1;
update_envelopes();
key_add = 0;
vibrato_add = 0;
tremolo_add = 0;
if (!(effect == 0x3D && effect_param > 0)) {
/* Not note delay. */
trigger(key, inst_idx, volume_column, effect);
/* Handle volume column. */
switch (volume_column & 0xF0) {
case 0x00:
/* Do nothing. */
break;
case 0x60:
/* Volume slide down. */
break;
case 0x70:
/* Volume slide up. */
break;
case 0x80:
/* Fine volume slide down. */
set_volume(volume - (volume_column & 0x0F));
break;
case 0x90:
/* Fine volume slide up. */
set_volume(volume + (volume_column & 0x0F));
break;
case 0xA0:
/* Set vibrato speed. */
set_vibrato_speed(volume_column & 0x0F);
break;
case 0xB0:
/* Vibrato. */
set_vibrato_depth(volume_column & 0x0F);
vibrato();
break;
case 0xC0:
/* Set panning. */
set_panning((volume_column & 0x0F) << 4);
break;
case 0xD0:
/* Panning slide left. */
break;
case 0xE0:
/* Panning slide right. */
break;
case 0xF0:
/* Tone portamento. */
set_portamento_param(volume_column & 0x0F);
break;
default:
/* Set volume. */
set_volume(volume_column - 0x10);
break;
}
}
if (instrument.vibrato_depth > 0) {
auto_vibrato();
}
switch (effect) {
case 0x01:
/* Portmento Up. */
set_portamento_param(effect_param);
portamento_up();
break;
case 0x02:
/* Portamento Down. */
set_portamento_param(effect_param);
portamento_down();
break;
case 0x03:
/* Tone Portamento. */
set_portamento_param(effect_param);
break;
case 0x04:
/* Vibrato. */
set_vibrato_speed((effect_param & 0xF0) >> 4);
set_vibrato_depth(effect_param & 0x0F);
vibrato();
break;
case 0x05:
/* Tone Portamento + Volume Slide. */
set_volume_slide_param(effect_param);
volume_slide();
break;
case 0x06:
/* Vibrato + Volume Slide. */
set_volume_slide_param(effect_param);
vibrato();
volume_slide();
break;
case 0x07:
/* Tremolo. */
set_tremolo_speed((effect_param & 0xF0) >> 4);
set_tremolo_depth(effect_param & 0x0F);
tremolo();
break;
case 0x08:
/* Set Panning. */
set_panning(effect_param);
break;
case 0x09:
/* Set Sample Index. */
set_sample_index(effect_param << 8);
break;
case 0x0A:
/* Volume Slide. */
set_volume_slide_param(effect_param);
volume_slide();
break;
case 0x0B:
/* Pattern Jump. */
break;
case 0x0C:
/* Set volume. */
set_volume(effect_param);
break;
case 0x0D:
/* Pattern Break. */
break;
case 0x0E:
/* Extended Commands (See 0x30-0x3F). */
break;
case 0x0F:
/* Set Speed/Tempo. */
break;
case 0x10:
/* Set Global Volume. */
set_global_volume(effect_param);
break;
case 0x11:
/* global Volume Slide. */
set_volume_slide_param(effect_param);
break;
case 0x14:
/* Key Off */
if (effect_param == 0) {
key_on = false;
}
break;
case 0x15:
/* Set Envelope Tick. */
set_envelope_tick(effect_param);
break;
case 0x19:
/* Panning Slide. */
set_volume_slide_param(effect_param);
break;
case 0x1B:
/* Retrig + Volume Slide. */
set_retrig_param(effect_param);
retrig_volume_slide();
break;
case 0x1D:
/* Tremor. */
set_retrig_param(effect_param);
tremor();
break;
case 0x24:
/* S3M Fine Vibrato. */
set_vibrato_speed((effect_param & 0xF0) >> 4);
set_vibrato_depth(effect_param & 0x0F);
fine_vibrato();
break;
case 0x25:
/* S3M Set Speed. */
break;
case 0x30:
/* Amiga Set Filter. */
break;
case 0x31:
/* Fine Portamento Up. */
set_portamento_param(0xF0 | effect_param);
portamento_up();
break;
case 0x32:
/* Fine Portamento Down. */
set_portamento_param(0xF0 | effect_param);
portamento_down();
break;
case 0x33:
/* Set Glissando Mode. */
break;
case 0x34:
/* Set Vibrato Waveform. */
set_vibrato_wave(effect_param);
break;
case 0x35:
/* Set Fine Tune. */
break;
case 0x36:
/* Pattern Loop. */
break;
case 0x37:
/* Set Tremolo Waveform. */
set_tremolo_wave(effect_param);
break;
case 0x38:
/* Set Panning(Obsolete). */
break;
case 0x39:
/* Retrig. */
set_retrig_param(effect_param);
break;
case 0x3A:
/* Fine Volume Slide Up. */
set_volume_slide_param(effect_param << 4 | 0x0F);
volume_slide();
break;
case 0x3B:
/* Fine Volume Slide Down. */
set_volume_slide_param(0xF0 | effect_param);
volume_slide();
break;
case 0x3C:
/* Note Cut. */
if (effect_param == 0) {
set_volume(0);
}
break;
case 0x3D:
/* Note Delay. */
break;
case 0x3E:
/* Pattern Delay. */
break;
case 0x3F:
/* Invert Loop. */
break;
case 0x40:
/* Arpeggio. */
break;
case 0x41:
/* Extra Fine Porta Up. */
set_portamento_param(0xE0 | effect_param);
portamento_up();
break;
case 0x42:
/* Extra Fine Porta Down. */
set_portamento_param(0xE0 | effect_param);
portamento_down();
break;
}
calculate_amplitude();
calculate_frequency();
}
public void tick() {
int volume_column, effect, effect_param;
volume_column = current_note[2];
effect = current_note[3];
effect_param = current_note[4];
effect_tick += 1;
if (effect == 0x3D && effect_param == effect_tick) {
/* Note delay. */
row(current_note[0], current_note[1], volume_column, 0, 0);
} else {
trigger_tick += 1;
vibrato_tick += 1;
tremolo_tick += 1;
update_envelopes();
key_add = 0;
vibrato_add = 0;
tremolo_add = 0;
if (instrument.vibrato_depth > 0) {
auto_vibrato();
}
switch (volume_column & 0xF0) {
case 0x60:
/* Volume Slide Down. */
set_volume(volume - (volume_column & 0x0F));
break;
case 0x70:
/* Volume Slide Up. */
set_volume(volume + (volume_column & 0x0F));
break;
case 0xB0:
/* Vibrato. */
vibrato();
break;
case 0xD0:
/* Panning Slide Left. */
set_panning(panning - (volume_column & 0x0F));
break;
case 0xE0:
/* Panning Slide Right. */
set_panning(panning + (volume_column & 0x0F));
break;
case 0xF0:
/* Tone Portamento. */
tone_portamento();
break;
}
switch (effect) {
case 0x01:
/* Portamento Up. */
portamento_up();
break;
case 0x02:
/* Portamento Down. */
portamento_down();
break;
case 0x03:
/* Tone Portamento. */
tone_portamento();
break;
case 0x04:
/* Vibrato. */
vibrato();
break;
case 0x05:
/* Tone Portamento + Volume Slide. */
tone_portamento();
volume_slide();
break;
case 0x06:
/* Vibrato + Volume Slide */
vibrato();
volume_slide();
break;
case 0x07:
/* Tremolo. */
tremolo();
break;
case 0x0A:
/* Volume Slide. */
volume_slide();
break;
case 0x11:
/* Global Volume Slide. */
global_volume_slide();
break;
case 0x14:
/* Key off. */
if (effect_tick == effect_param) {
key_on = false;
}
break;
case 0x19:
/* Panning Slide. */
panning_slide();
break;
case 0x1B:
/* Retrig + Volume Slide. */
retrig_volume_slide();
break;
case 0x1D:
/* Tremor. */
tremor();
break;
case 0x24:
/* S3M Fine Vibrato. */
fine_vibrato();
break;
case 0x39:
/* Retrig. */
retrig_volume_slide();
break;
case 0x3C:
/* Note Cut. */
if (effect_tick == effect_param) {
set_volume(0);
}
break;
case 0x40:
/* Arpeggio. */
switch (effect_tick % 3) {
case 1:
key_add = (effect_param & 0xF0) >> 4;
break;
case 2:
key_add = effect_param & 0x0F;
break;
}
break;
}
}
calculate_amplitude();
calculate_frequency();
}
private void set_vibrato_speed(int speed) {
if (speed > 0) {
vibrato_speed = speed;
}
}
private void set_vibrato_depth(int depth) {
if (depth > 0) {
vibrato_depth = depth;
}
}
private void set_vibrato_wave(int wave) {
if (wave < 0 || wave > 7) {
wave = 0;
}
vibrato_wave = wave;
}
private void set_tremolo_speed(int speed) {
if (speed > 0) {
tremolo_speed = speed;
}
}
private void set_tremolo_depth(int depth) {
if (depth > 0) {
tremolo_depth = depth;
}
}
private void set_tremolo_wave(int wave) {
if (wave < 0 || wave > 7) {
wave = 0;
}
tremolo_wave = wave;
}
private void vibrato() {
int vibrato_phase;
vibrato_phase = vibrato_tick * vibrato_speed;
vibrato_add += waveform(vibrato_phase, vibrato_wave) * vibrato_depth >> 5;
}
private void fine_vibrato() {
int vibrato_phase;
vibrato_phase = vibrato_tick * vibrato_speed;
vibrato_add += waveform(vibrato_phase, vibrato_wave) * vibrato_depth >> 7;
}
private void tremolo() {
int tremolo_phase;
tremolo_phase = tremolo_tick * tremolo_speed;
tremolo_add += waveform(tremolo_phase, tremolo_wave) * tremolo_depth >> 6;
}
private void set_portamento_param(int param) {
if (param != 0) {
portamento_param = param;
}
}
private void tone_portamento() {
int new_period;
if (porta_period < period) {
new_period = period - (portamento_param << 2);
if (new_period < porta_period) {
new_period = porta_period;
}
set_period(new_period);
}
if (porta_period > period) {
new_period = period + (portamento_param << 2);
if (new_period > porta_period) {
new_period = porta_period;
}
set_period(new_period);
}
}
private void portamento_up() {
if ((portamento_param & 0xF0) == 0xE0) {
/* Extra-fine porta. */
if (effect_tick == 0) {
set_period(period - (portamento_param & 0x0F));
}
} else if ((portamento_param & 0xF0) == 0xF0) {
/* Fine porta. */
if (effect_tick == 0) {
set_period(period - ((portamento_param & 0x0F) << 2));
}
} else {
/* Normal porta. */
if (effect_tick > 0) {
set_period(period - (portamento_param << 2));
}
}
}
private void portamento_down() {
if ((portamento_param & 0xF0) == 0xE0) {
/* Extra-fine porta. */
if (effect_tick == 0) {
set_period(period + (portamento_param & 0x0F));
}
} else if ((portamento_param & 0xF0) == 0xF0) {
/* Fine porta. */
if (effect_tick == 0) {
set_period(period + ((portamento_param & 0x0F) << 2));
}
} else {
/* Normal porta. */
if (effect_tick > 0) {
set_period(period + (portamento_param << 2));
}
}
}
private void set_period(int p) {
if (p < 32) {
p = 32;
}
if (p > 32768) {
p = 32768;
}
period = p;
}
private void set_global_volume(int vol) {
if (vol < 0) {
vol = 0;
}
if (vol > 64) {
vol = 64;
}
global_volume[0] = vol;
}
private void set_volume_slide_param(int param) {
if (param != 0) {
volume_slide_param = param;
}
}
private void global_volume_slide() {
int up, down;
up = (volume_slide_param & 0xF0) >> 4;
down = volume_slide_param & 0x0F;
set_global_volume(global_volume[0] + up - down);
}
private void volume_slide() {
int up, down;
up = (volume_slide_param & 0xF0) >> 4;
down = volume_slide_param & 0x0F;
if (down == 0x0F && up > 0) {
/* Fine slide up. */
if (effect_tick == 0) {
set_volume(volume + up);
}
} else if (up == 0x0F && down > 0) {
/* Fine slide down. */
if (effect_tick == 0) {
set_volume(volume - down);
}
} else {
/* Normal slide. */
if (effect_tick > 0 || fast_volume_slides) {
set_volume(volume + up - down);
}
}
}
private void panning_slide() {
int left, right;
left = (volume_slide_param & 0xF0) >> 4;
right = volume_slide_param & 0x0F;
set_panning(panning - left + right);
}
private void set_retrig_param(int param) {
if (param != 0) {
retrig_param = param;
}
}
private void tremor() {
int on_ticks, cycle_length, cycle_index;
on_ticks = ((retrig_param & 0xF0) >> 4) + 1;
cycle_length = on_ticks + (retrig_param & 0x0F) + 1;
cycle_index = trigger_tick % cycle_length;
if (cycle_index >= on_ticks) {
tremolo_add = -64;
}
}
private void retrig_volume_slide() {
int retrig_volume, retrig_tick;
retrig_volume = (retrig_param & 0xF0) >> 4;
retrig_tick = retrig_param & 0x0F;
if (retrig_tick > 0 && trigger_tick % retrig_tick == 0) {
set_sample_index(0);
switch (retrig_volume) {
case 0x01:
set_volume(volume - 1);
break;
case 0x02:
set_volume(volume - 2);
break;
case 0x03:
set_volume(volume - 4);
break;
case 0x04:
set_volume(volume - 8);
break;
case 0x05:
set_volume(volume - 16);
break;
case 0x06:
set_volume(volume - volume / 3);
break;
case 0x07:
set_volume(volume / 2);
break;
case 0x09:
set_volume(volume + 1);
break;
case 0x0A:
set_volume(volume + 2);
break;
case 0x0B:
set_volume(volume + 4);
break;
case 0x0C:
set_volume(volume + 8);
break;
case 0x0D:
set_volume(volume + 16);
break;
case 0x0E:
set_volume(volume + volume / 2);
break;
case 0x0F:
set_volume(volume * 2);
break;
}
}
}
private void set_sample_index(int index) {
if (index < 0) {
index = 0;
}
sample_idx = index;
sample_frac = 0;
}
private void set_envelope_tick(int tick) {
volume_envelope_tick = tick;
panning_envelope_tick = tick;
}
private void trigger(int key, int instrument_idx, int volume_column, int effect) {
if (instrument_idx > 0) {
instrument = module.get_instrument(instrument_idx);
sample = instrument.get_sample_from_key(key);
set_volume(sample.volume);
if (sample.set_panning) {
set_panning(sample.panning);
}
log_2_c2_rate = LogTable.log2(sample.c2_rate);
set_envelope_tick(0);
fade_out_volume = 32768;
key_on = true;
}
if (key > 0) {
if (key < 97) {
porta_period = key_to_period(key);
if (effect != 0x03 && effect != 0x05) {
if ((volume_column & 0xF0) != 0xF0) {
/* Not portamento. */
trigger_tick = 0;
if (vibrato_wave < 4) {
vibrato_tick = 0;
}
if (tremolo_wave < 4) {
tremolo_tick = 0;
}
set_period(porta_period);
set_sample_index(0);
}
}
} else {
/* Key off. */
key_on = false;
}
}
}
private void update_envelopes() {
Envelope envelope;
if (instrument.volume_envelope_active) {
if (!key_on) {
fade_out_volume -= instrument.volume_fade_out & 0xFFFF;
if (fade_out_volume < 0) {
fade_out_volume = 0;
}
}
envelope = instrument.get_volume_envelope();
volume_envelope_tick = envelope.next_tick(volume_envelope_tick, key_on);
}
if (instrument.panning_envelope_active) {
envelope = instrument.get_panning_envelope();
panning_envelope_tick = envelope.next_tick(panning_envelope_tick, key_on);
}
}
private void auto_vibrato() {
int sweep, depth, rate;
sweep = instrument.vibrato_sweep & 0xFF;
depth = instrument.vibrato_depth & 0x0F;
rate = instrument.vibrato_rate & 0x3F;
if (trigger_tick < sweep) {
depth = depth * trigger_tick / sweep;
}
vibrato_add += waveform(trigger_tick * rate, 0) * depth >> 9;
}
private int waveform(int phase, int wform) {
int amplitude;
amplitude = 0;
switch (wform & 0x3) {
case 0:
/* Sine. */
if ((phase & 0x20) == 0) {
amplitude = sine_table[phase & 0x1F];
} else {
amplitude = -sine_table[phase & 0x1F];
}
break;
case 1:
/* Saw. */
if ((phase & 0x20) == 0) {
amplitude = (phase & 0x1F) << 3;
} else {
amplitude = ((phase & 0x1F) << 3) - 255;
}
break;
case 2:
/* Square. */
if ((phase & 0x20) == 0) {
amplitude = 255;
} else {
amplitude = -255;
}
break;
case 3:
/* Random. */
amplitude = (random_seed >> 15) - 255;
random_seed = random_seed * 65 + 17 & 0xFFFFFF;
break;
}
return amplitude;
}
private int key_to_period(int key) {
int log_2_period, period_out;
key = key + sample.relative_note;
if (linear_periods) {
period_out = 7744 - key * 64;
} else {
log_2_period = LOG_2_29024 - (key << IBXM.FP_SHIFT) / 12;
period_out = LogTable.raise2(log_2_period) >> IBXM.FP_SHIFT;
}
return period_out;
}
private void calculate_amplitude() {
int envelope_volume, tremolo_volume, amplitude;
int envelope_panning, mixer_panning, panning_range;
Envelope envelope;
envelope_volume = 0;
if (instrument.volume_envelope_active) {
envelope = instrument.get_volume_envelope();
envelope_volume = envelope.calculate_ampl(volume_envelope_tick);
} else {
if (key_on) {
envelope_volume = 64;
}
}
tremolo_volume = volume + tremolo_add;
if (tremolo_volume < 0) {
tremolo_volume = 0;
}
if (tremolo_volume > 64) {
tremolo_volume = 64;
}
amplitude = tremolo_volume << IBXM.FP_SHIFT - 6;
amplitude = amplitude * envelope_volume >> 6;
amplitude = amplitude * fade_out_volume >> 15;
amplitude = amplitude * global_volume[0] >> 6;
amplitude = amplitude * module.channel_gain >> IBXM.FP_SHIFT;
silent = sample.has_finished(sample_idx);
if (amplitude <= 0) {
silent = true;
} else {
envelope_panning = 32;
if (instrument.panning_envelope_active) {
envelope = instrument.get_panning_envelope();
envelope_panning = envelope.calculate_ampl(panning_envelope_tick);
}
mixer_panning = (panning & 0xFF) << IBXM.FP_SHIFT - 8;
panning_range = IBXM.FP_ONE - mixer_panning;
if (panning_range > mixer_panning) {
panning_range = mixer_panning;
}
mixer_panning = mixer_panning + (panning_range * (envelope_panning - 32) >> 5);
left_gain = amplitude * (IBXM.FP_ONE - mixer_panning) >> IBXM.FP_SHIFT;
right_gain = amplitude * mixer_panning >> IBXM.FP_SHIFT;
}
}
private void calculate_frequency() {
int vibrato_period, log_2_freq;
vibrato_period = period + vibrato_add;
if (vibrato_period < 32) {
vibrato_period = 32;
}
if (vibrato_period > 32768) {
vibrato_period = 32768;
}
if (linear_periods) {
log_2_freq = log_2_c2_rate + (4608 - vibrato_period << IBXM.FP_SHIFT) / 768;
} else {
log_2_freq = log_2_c2_rate + LOG_2_1712 - LogTable.log2(vibrato_period);
}
log_2_freq += ((key_add << 7) + sample.fine_tune << IBXM.FP_SHIFT) / 1536;
step = LogTable.raise2(log_2_freq - log_2_sampling_rate);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy