base mod created
This commit is contained in:
946
build/tmp/recompileMc/sources/ibxm/Channel.java
Normal file
946
build/tmp/recompileMc/sources/ibxm/Channel.java
Normal file
@@ -0,0 +1,946 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Channel {
|
||||
public int pattern_loop_row;
|
||||
|
||||
private Module module;
|
||||
private Instrument instrument;
|
||||
private Sample sample;
|
||||
private int[] global_volume, current_note;
|
||||
private boolean linear_periods, fast_volume_slides, key_on, silent;
|
||||
private int sample_idx, sample_frac, step, left_gain, right_gain;
|
||||
@SuppressWarnings("unused") //Forge
|
||||
private int volume, panning, fine_tune, 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 int log_2_sampling_rate;
|
||||
private static final int LOG_2_29024 = LogTable.log_2( 29024 );
|
||||
private static final int LOG_2_8287 = LogTable.log_2( 8287 );
|
||||
private static final int LOG_2_8363 = LogTable.log_2( 8363 );
|
||||
private static final int LOG_2_1712 = LogTable.log_2( 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.log_2( 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 );
|
||||
}
|
||||
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 octave, log_2_period, period_out;
|
||||
octave = ( key << IBXM.FP_SHIFT ) / 12 + sample.transpose;
|
||||
if( linear_periods ) {
|
||||
period_out = 7744 - ( octave * 768 >> IBXM.FP_SHIFT );
|
||||
} else {
|
||||
log_2_period = LOG_2_29024 - octave;
|
||||
period_out = LogTable.raise_2( log_2_period );
|
||||
period_out = period_out >> ( IBXM.FP_SHIFT - 1 );
|
||||
period_out = ( period_out >> 1 ) + ( period_out & 1 );
|
||||
}
|
||||
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_8363 + ( 4608 - vibrato_period << IBXM.FP_SHIFT ) / 768;
|
||||
} else {
|
||||
log_2_freq = module.pal ? LOG_2_8287 : LOG_2_8363;
|
||||
log_2_freq = log_2_freq + LOG_2_1712 - LogTable.log_2( vibrato_period );
|
||||
}
|
||||
log_2_freq += ( key_add << IBXM.FP_SHIFT ) / 12;
|
||||
step = LogTable.raise_2( log_2_freq - log_2_sampling_rate );
|
||||
}
|
||||
}
|
||||
110
build/tmp/recompileMc/sources/ibxm/Envelope.java
Normal file
110
build/tmp/recompileMc/sources/ibxm/Envelope.java
Normal file
@@ -0,0 +1,110 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Envelope {
|
||||
public boolean sustain, looped;
|
||||
private int sustain_tick, loop_start_tick, loop_end_tick;
|
||||
private int[] ticks, ampls;
|
||||
|
||||
public Envelope() {
|
||||
set_num_points( 1 );
|
||||
}
|
||||
|
||||
public void set_num_points( int num_points ) {
|
||||
//int point; Forge: Unused
|
||||
if( num_points <= 0 ) {
|
||||
num_points = 1;
|
||||
}
|
||||
ticks = new int[ num_points ];
|
||||
ampls = new int[ num_points ];
|
||||
set_point( 0, 0, 0, false );
|
||||
}
|
||||
|
||||
/* When you set a point, all subsequent points are reset. */
|
||||
public void set_point( int point, int tick, int ampl, boolean delta ) {
|
||||
if( point >= 0 && point < ticks.length ) {
|
||||
if( point == 0 ) {
|
||||
tick = 0;
|
||||
}
|
||||
if( point > 0 ) {
|
||||
if( delta ) tick += ticks[ point - 1 ];
|
||||
if( tick <= ticks[ point - 1 ] ) {
|
||||
System.out.println( "Envelope: Point not valid (" + tick + " <= " + ticks[ point - 1 ] + ")");
|
||||
tick = ticks[ point - 1 ] + 1;
|
||||
}
|
||||
}
|
||||
ticks[ point ] = tick;
|
||||
ampls[ point ] = ampl;
|
||||
point += 1;
|
||||
while( point < ticks.length ) {
|
||||
ticks[ point ] = ticks[ point - 1 ] + 1;
|
||||
ampls[ point ] = 0;
|
||||
point += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void set_sustain_point( int point ) {
|
||||
if( point < 0 ) {
|
||||
point = 0;
|
||||
}
|
||||
if( point >= ticks.length ) {
|
||||
point = ticks.length - 1;
|
||||
}
|
||||
sustain_tick = ticks[ point ];
|
||||
}
|
||||
|
||||
public void set_loop_points( int start, int end ) {
|
||||
if( start < 0 ) {
|
||||
start = 0;
|
||||
}
|
||||
if( start >= ticks.length ) {
|
||||
start = ticks.length - 1;
|
||||
}
|
||||
if( end < start || end >= ticks.length ) {
|
||||
end = start;
|
||||
}
|
||||
loop_start_tick = ticks[ start ];
|
||||
loop_end_tick = ticks[ end ];
|
||||
}
|
||||
|
||||
public int next_tick( int tick, boolean key_on ) {
|
||||
tick = tick + 1;
|
||||
if( looped && tick >= loop_end_tick ) {
|
||||
tick = loop_start_tick;
|
||||
}
|
||||
if( sustain && key_on && tick >= sustain_tick ) {
|
||||
tick = sustain_tick;
|
||||
}
|
||||
return tick;
|
||||
}
|
||||
|
||||
public int calculate_ampl( int tick ) {
|
||||
int idx, point, delta_t, delta_a, ampl;
|
||||
ampl = ampls[ ticks.length - 1 ];
|
||||
if( tick < ticks[ ticks.length - 1 ] ) {
|
||||
point = 0;
|
||||
for( idx = 1; idx < ticks.length; idx++ ) {
|
||||
if( ticks[ idx ] <= tick ) {
|
||||
point = idx;
|
||||
}
|
||||
}
|
||||
delta_t = ticks[ point + 1 ] - ticks[ point ];
|
||||
delta_a = ampls[ point + 1 ] - ampls[ point ];
|
||||
ampl = ( delta_a << IBXM.FP_SHIFT ) / delta_t;
|
||||
ampl = ampl * ( tick - ticks[ point ] ) >> IBXM.FP_SHIFT;
|
||||
ampl = ampl + ampls[ point ];
|
||||
}
|
||||
return ampl;
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
int idx, tick;
|
||||
for( idx = 0; idx < ticks.length; idx++ ) {
|
||||
System.out.println( ticks[ idx ] + ", " + ampls[ idx ] );
|
||||
}
|
||||
for( tick = 0; tick < 222; tick++ ) {
|
||||
System.out.print( calculate_ampl( tick ) + ", " );
|
||||
}
|
||||
}
|
||||
}
|
||||
256
build/tmp/recompileMc/sources/ibxm/FastTracker2.java
Normal file
256
build/tmp/recompileMc/sources/ibxm/FastTracker2.java
Normal file
@@ -0,0 +1,256 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
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;
|
||||
boolean delta_env;
|
||||
String tracker_name;
|
||||
Instrument instrument;
|
||||
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 );
|
||||
tracker_name = ascii_text( header_60_bytes, 38, 20 );
|
||||
delta_env = tracker_name.startsWith( "DigiBooster Pro" );
|
||||
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++ ) {
|
||||
try {
|
||||
instrument = read_xm_instrument( data_input, delta_env );
|
||||
module.set_instrument( idx, instrument );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "Instrument " + idx + " is missing!" );
|
||||
}
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static Pattern read_xm_pattern( DataInput data_input, int num_channels ) throws IOException {
|
||||
@SuppressWarnings("unused") //Forge
|
||||
int pattern_header_length, packing_type, num_rows, 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, boolean delta_env ) 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, delta_env );
|
||||
}
|
||||
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, delta_env );
|
||||
}
|
||||
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;
|
||||
int fine_tune, relative_note;
|
||||
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;
|
||||
fine_tune = sample_headers[ header_offset + 13 ];
|
||||
fine_tune = ( fine_tune << IBXM.FP_SHIFT ) / 1536;
|
||||
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;
|
||||
relative_note = sample_headers[ header_offset + 16 ];
|
||||
relative_note = ( relative_note << IBXM.FP_SHIFT ) / 12;
|
||||
sample.transpose = relative_note + fine_tune;
|
||||
sample.name = ascii_text( sample_headers, header_offset + 18, 22 );
|
||||
raw_sample_data = new byte[ sample_length ];
|
||||
try {
|
||||
data_input.readFully( raw_sample_data );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "Sample has been truncated!" );
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
342
build/tmp/recompileMc/sources/ibxm/IBXM.java
Normal file
342
build/tmp/recompileMc/sources/ibxm/IBXM.java
Normal file
@@ -0,0 +1,342 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class IBXM {
|
||||
public static final String VERSION = "ibxm alpha 51 (c)2008 mumart@gmail.com";
|
||||
|
||||
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 int sampling_rate, resampling_quality, volume_ramp_length;
|
||||
private int tick_length_samples, current_tick_samples;
|
||||
private int[] mixing_buffer, volume_ramp_buffer;
|
||||
|
||||
private Module module;
|
||||
private Channel[] channels;
|
||||
private 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 ) {
|
||||
|
||||
/** MODIFIED 13 Oct 2009 by Paul Lamb **/
|
||||
// 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;
|
||||
clear_vol_ramp_buffer();
|
||||
}
|
||||
|
||||
public void seek( int sample_position ) {
|
||||
//int idx; Forge, Unused
|
||||
set_sequence_index( 0, 0 );
|
||||
next_tick();
|
||||
while( sample_position > tick_length_samples ) {
|
||||
sample_position -= tick_length_samples;
|
||||
next_tick();
|
||||
}
|
||||
mix_tick();
|
||||
current_tick_samples = sample_position;
|
||||
}
|
||||
|
||||
public void get_audio( byte[] output_buffer, int frames ) {
|
||||
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;
|
||||
}
|
||||
output_buffer[ output_idx ] = ( byte ) ( amplitude >> 8 );
|
||||
output_buffer[ output_idx + 1 ] = ( byte ) ( amplitude & 0xFF );
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private void clear_vol_ramp_buffer() {
|
||||
int ramp_idx, ramp_end;
|
||||
ramp_idx = 0;
|
||||
ramp_end = volume_ramp_length * 2 - 1;
|
||||
while( ramp_idx <= ramp_end ) {
|
||||
volume_ramp_buffer[ ramp_idx ] = 0;
|
||||
ramp_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
90
build/tmp/recompileMc/sources/ibxm/Instrument.java
Normal file
90
build/tmp/recompileMc/sources/ibxm/Instrument.java
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Instrument {
|
||||
public String name;
|
||||
public int vibrato_type, vibrato_sweep;
|
||||
public int vibrato_depth, vibrato_rate;
|
||||
public boolean volume_envelope_active, panning_envelope_active;
|
||||
public int volume_fade_out;
|
||||
|
||||
private Envelope volume_envelope, panning_envelope;
|
||||
private int[] key_to_sample;
|
||||
private Sample[] samples;
|
||||
|
||||
public Instrument() {
|
||||
name = "";
|
||||
set_volume_envelope( new Envelope() );
|
||||
set_panning_envelope( new Envelope() );
|
||||
key_to_sample = new int[ 96 ];
|
||||
set_num_samples( 1 );
|
||||
}
|
||||
|
||||
public Envelope get_volume_envelope() {
|
||||
return volume_envelope;
|
||||
}
|
||||
|
||||
public void set_volume_envelope( Envelope envelope ) {
|
||||
if( envelope != null ) {
|
||||
volume_envelope = envelope;
|
||||
}
|
||||
}
|
||||
|
||||
public Envelope get_panning_envelope() {
|
||||
return panning_envelope;
|
||||
}
|
||||
|
||||
public void set_panning_envelope( Envelope envelope ) {
|
||||
if( envelope != null ) {
|
||||
panning_envelope = envelope;
|
||||
}
|
||||
}
|
||||
|
||||
public Sample get_sample_from_key( int key ) {
|
||||
int sample_idx;
|
||||
sample_idx = 0;
|
||||
if( key > 0 && key <= key_to_sample.length ) {
|
||||
sample_idx = key_to_sample[ key - 1 ];
|
||||
}
|
||||
return get_sample( sample_idx );
|
||||
}
|
||||
|
||||
public void set_key_to_sample( int key, int sample ) {
|
||||
if( key > 0 && key <= key_to_sample.length ) {
|
||||
key_to_sample[ key - 1 ] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_samples() {
|
||||
return samples.length;
|
||||
}
|
||||
|
||||
public void set_num_samples( int num_samples ) {
|
||||
if( num_samples < 1 ) {
|
||||
num_samples = 1;
|
||||
}
|
||||
samples = new Sample[ num_samples ];
|
||||
set_sample( 0, null );
|
||||
}
|
||||
|
||||
public Sample get_sample( int sample_index ) {
|
||||
Sample sample;
|
||||
sample = null;
|
||||
if( sample_index >= 0 && sample_index < samples.length ) {
|
||||
sample = samples[ sample_index ];
|
||||
}
|
||||
if( sample == null ) {
|
||||
sample = samples[ 0 ];
|
||||
}
|
||||
return sample;
|
||||
}
|
||||
|
||||
public void set_sample( int sample_index, Sample sample ) {
|
||||
if( sample_index >= 0 && sample_index < samples.length ) {
|
||||
samples[ sample_index ] = sample;
|
||||
}
|
||||
if( samples[ 0 ] == null ) {
|
||||
samples[ 0 ] = new Sample();
|
||||
}
|
||||
}
|
||||
}
|
||||
90
build/tmp/recompileMc/sources/ibxm/LogTable.java
Normal file
90
build/tmp/recompileMc/sources/ibxm/LogTable.java
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
/*
|
||||
Base-2 Log and Exp functions, using linear-interpolated tables.
|
||||
*/
|
||||
public class LogTable {
|
||||
private static final int TABLE_SHIFT = 7; // 128 points (+1 for interp)
|
||||
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - TABLE_SHIFT;
|
||||
private static final int INTERP_MASK = ( 1 << INTERP_SHIFT ) - 1;
|
||||
|
||||
private static final int[] exp_2_table = {
|
||||
32768, 32945, 33124, 33304, 33485, 33667, 33850, 34033,
|
||||
34218, 34404, 34591, 34779, 34968, 35157, 35348, 35540,
|
||||
35733, 35927, 36122, 36319, 36516, 36714, 36913, 37114,
|
||||
37315, 37518, 37722, 37926, 38132, 38339, 38548, 38757,
|
||||
38967, 39179, 39392, 39606, 39821, 40037, 40254, 40473,
|
||||
40693, 40914, 41136, 41359, 41584, 41810, 42037, 42265,
|
||||
42494, 42725, 42957, 43190, 43425, 43661, 43898, 44136,
|
||||
44376, 44617, 44859, 45103, 45347, 45594, 45841, 46090,
|
||||
46340, 46592, 46845, 47099, 47355, 47612, 47871, 48131,
|
||||
48392, 48655, 48919, 49185, 49452, 49720, 49990, 50262,
|
||||
50535, 50809, 51085, 51362, 51641, 51922, 52204, 52487,
|
||||
52772, 53059, 53347, 53636, 53928, 54220, 54515, 54811,
|
||||
55108, 55408, 55709, 56011, 56315, 56621, 56928, 57238,
|
||||
57548, 57861, 58175, 58491, 58809, 59128, 59449, 59772,
|
||||
60096, 60423, 60751, 61081, 61412, 61746, 62081, 62418,
|
||||
62757, 63098, 63440, 63785, 64131, 64479, 64830, 65182,
|
||||
65536
|
||||
};
|
||||
|
||||
private static final int[] log_2_table = {
|
||||
0, 367, 732, 1095, 1454, 1811, 2165, 2517,
|
||||
2865, 3212, 3556, 3897, 4236, 4572, 4906, 5238,
|
||||
5568, 5895, 6220, 6542, 6863, 7181, 7497, 7812,
|
||||
8124, 8434, 8742, 9048, 9352, 9654, 9954, 10252,
|
||||
10548, 10843, 11136, 11427, 11716, 12003, 12289, 12573,
|
||||
12855, 13136, 13414, 13692, 13967, 14241, 14514, 14785,
|
||||
15054, 15322, 15588, 15853, 16117, 16378, 16639, 16898,
|
||||
17156, 17412, 17667, 17920, 18172, 18423, 18673, 18921,
|
||||
19168, 19413, 19657, 19900, 20142, 20383, 20622, 20860,
|
||||
21097, 21333, 21568, 21801, 22034, 22265, 22495, 22724,
|
||||
22952, 23178, 23404, 23628, 23852, 24074, 24296, 24516,
|
||||
24736, 24954, 25171, 25388, 25603, 25817, 26031, 26243,
|
||||
26455, 26665, 26875, 27084, 27292, 27499, 27705, 27910,
|
||||
28114, 28317, 28520, 28721, 28922, 29122, 29321, 29519,
|
||||
29716, 29913, 30109, 30304, 30498, 30691, 30884, 31076,
|
||||
31267, 31457, 31646, 31835, 32023, 32210, 32397, 32582,
|
||||
32768
|
||||
};
|
||||
|
||||
/*
|
||||
Calculate log-base-2 of x (non-fixed-point).
|
||||
A fixed point value is returned.
|
||||
*/
|
||||
public static int log_2( int x ) {
|
||||
int shift;
|
||||
/* Scale x to range 1.0 <= x < 2.0 */
|
||||
shift = IBXM.FP_SHIFT;
|
||||
while( x < IBXM.FP_ONE ) {
|
||||
x <<= 1;
|
||||
shift--;
|
||||
}
|
||||
while( x >= ( IBXM.FP_ONE << 1 ) ) {
|
||||
x >>= 1;
|
||||
shift++;
|
||||
}
|
||||
return ( IBXM.FP_ONE * shift ) + eval_table( log_2_table, x - IBXM.FP_ONE );
|
||||
}
|
||||
|
||||
/*
|
||||
Raise 2 to the power x (fixed point).
|
||||
A fixed point value is returned.
|
||||
*/
|
||||
public static int raise_2( int x ) {
|
||||
int y;
|
||||
y = eval_table( exp_2_table, x & IBXM.FP_MASK ) << IBXM.FP_SHIFT;
|
||||
return y >> IBXM.FP_SHIFT - ( x >> IBXM.FP_SHIFT );
|
||||
}
|
||||
|
||||
private static int eval_table( int[] table, int x ) {
|
||||
int table_idx, table_frac, c, m, y;
|
||||
table_idx = x >> INTERP_SHIFT;
|
||||
table_frac = x & INTERP_MASK;
|
||||
c = table[ table_idx ];
|
||||
m = table[ table_idx + 1 ] - c;
|
||||
y = ( m * table_frac >> INTERP_SHIFT ) + c;
|
||||
return y >> 15 - IBXM.FP_SHIFT;
|
||||
}
|
||||
}
|
||||
137
build/tmp/recompileMc/sources/ibxm/Module.java
Normal file
137
build/tmp/recompileMc/sources/ibxm/Module.java
Normal file
@@ -0,0 +1,137 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Module {
|
||||
public String song_title;
|
||||
public boolean linear_periods, fast_volume_slides, pal;
|
||||
public int global_volume, channel_gain;
|
||||
public int default_speed, default_tempo;
|
||||
public int restart_sequence_index;
|
||||
|
||||
private int[] initial_panning, sequence;
|
||||
private Pattern[] patterns;
|
||||
private Instrument[] instruments;
|
||||
|
||||
private Pattern default_pattern;
|
||||
private Instrument default_instrument;
|
||||
|
||||
public Module() {
|
||||
song_title = IBXM.VERSION;
|
||||
set_num_channels( 1 );
|
||||
set_sequence_length( 1 );
|
||||
set_num_patterns( 0 );
|
||||
set_num_instruments( 0 );
|
||||
default_pattern = new Pattern();
|
||||
default_instrument = new Instrument();
|
||||
}
|
||||
|
||||
public int get_num_channels() {
|
||||
return initial_panning.length;
|
||||
}
|
||||
|
||||
public void set_num_channels( int num_channels ) {
|
||||
if( num_channels < 1 ) {
|
||||
num_channels = 1;
|
||||
}
|
||||
initial_panning = new int[ num_channels ];
|
||||
}
|
||||
|
||||
public int get_initial_panning( int channel ) {
|
||||
int panning;
|
||||
panning = 128;
|
||||
if( channel >= 0 && channel < initial_panning.length ) {
|
||||
panning = initial_panning[ channel ];
|
||||
}
|
||||
return panning;
|
||||
}
|
||||
|
||||
public void set_initial_panning( int channel, int panning ) {
|
||||
if( channel >= 0 && channel < initial_panning.length ) {
|
||||
initial_panning[ channel ] = panning;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_sequence_length() {
|
||||
return sequence.length;
|
||||
}
|
||||
|
||||
public void set_sequence_length( int sequence_length ) {
|
||||
if( sequence_length < 0 ) {
|
||||
sequence_length = 0;
|
||||
}
|
||||
sequence = new int[ sequence_length ];
|
||||
}
|
||||
|
||||
public void set_sequence( int sequence_index, int pattern_index ) {
|
||||
if( sequence_index >= 0 && sequence_index < sequence.length ) {
|
||||
sequence[ sequence_index ] = pattern_index;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_patterns() {
|
||||
return patterns.length;
|
||||
}
|
||||
|
||||
public void set_num_patterns( int num_patterns ) {
|
||||
if( num_patterns < 0 ) {
|
||||
num_patterns = 0;
|
||||
}
|
||||
patterns = new Pattern[ num_patterns ];
|
||||
}
|
||||
|
||||
public Pattern get_pattern_from_sequence( int sequence_index ) {
|
||||
Pattern pattern;
|
||||
pattern = default_pattern;
|
||||
if( sequence_index >= 0 && sequence_index < sequence.length ) {
|
||||
pattern = get_pattern( sequence[ sequence_index ] );
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public Pattern get_pattern( int pattern_index ) {
|
||||
Pattern pattern;
|
||||
pattern = null;
|
||||
if( pattern_index >= 0 && pattern_index < patterns.length ) {
|
||||
pattern = patterns[ pattern_index ];
|
||||
}
|
||||
if( pattern == null ) {
|
||||
pattern = default_pattern;
|
||||
}
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public void set_pattern( int pattern_index, Pattern pattern ) {
|
||||
if( pattern_index >= 0 && pattern_index < patterns.length ) {
|
||||
patterns[ pattern_index ] = pattern;
|
||||
}
|
||||
}
|
||||
|
||||
public int get_num_instruments() {
|
||||
return instruments.length;
|
||||
}
|
||||
|
||||
public void set_num_instruments( int num_instruments ) {
|
||||
if( num_instruments < 0 ) {
|
||||
num_instruments = 0;
|
||||
}
|
||||
instruments = new Instrument[ num_instruments ];
|
||||
}
|
||||
|
||||
public Instrument get_instrument( int instrument_index ) {
|
||||
Instrument instrument;
|
||||
instrument = null;
|
||||
if( instrument_index > 0 && instrument_index <= instruments.length ) {
|
||||
instrument = instruments[ instrument_index - 1 ];
|
||||
}
|
||||
if( instrument == null ) {
|
||||
instrument = default_instrument;
|
||||
}
|
||||
return instrument;
|
||||
}
|
||||
|
||||
public void set_instrument( int instrument_index, Instrument instrument ) {
|
||||
if( instrument_index > 0 && instrument_index <= instruments.length ) {
|
||||
instruments[ instrument_index - 1 ] = instrument;
|
||||
}
|
||||
}
|
||||
}
|
||||
60
build/tmp/recompileMc/sources/ibxm/Pattern.java
Normal file
60
build/tmp/recompileMc/sources/ibxm/Pattern.java
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Pattern {
|
||||
public int num_rows;
|
||||
|
||||
private int data_offset, note_index;
|
||||
private byte[] pattern_data;
|
||||
|
||||
public Pattern() {
|
||||
num_rows = 1;
|
||||
set_pattern_data( new byte[ 0 ] );
|
||||
}
|
||||
|
||||
public void set_pattern_data( byte[] data ) {
|
||||
if( data != null ) {
|
||||
pattern_data = data;
|
||||
}
|
||||
data_offset = 0;
|
||||
note_index = 0;
|
||||
}
|
||||
|
||||
public void get_note( int[] note, int index ) {
|
||||
if( index < note_index ) {
|
||||
note_index = 0;
|
||||
data_offset = 0;
|
||||
}
|
||||
while( note_index <= index ) {
|
||||
data_offset = next_note( data_offset, note );
|
||||
note_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public int next_note( int data_offset, int[] note ) {
|
||||
int bitmask, field;
|
||||
if( data_offset < 0 ) {
|
||||
data_offset = pattern_data.length;
|
||||
}
|
||||
bitmask = 0x80;
|
||||
if( data_offset < pattern_data.length ) {
|
||||
bitmask = pattern_data[ data_offset ] & 0xFF;
|
||||
}
|
||||
if( ( bitmask & 0x80 ) == 0x80 ) {
|
||||
data_offset += 1;
|
||||
} else {
|
||||
bitmask = 0x1F;
|
||||
}
|
||||
for( field = 0; field < 5; field++ ) {
|
||||
note[ field ] = 0;
|
||||
if( ( bitmask & 0x01 ) == 0x01 ) {
|
||||
if( data_offset < pattern_data.length ) {
|
||||
note[ field ] = pattern_data[ data_offset ] & 0xFF;
|
||||
data_offset += 1;
|
||||
}
|
||||
}
|
||||
bitmask = bitmask >> 1;
|
||||
}
|
||||
return data_offset;
|
||||
}
|
||||
}
|
||||
133
build/tmp/recompileMc/sources/ibxm/Player.java
Normal file
133
build/tmp/recompileMc/sources/ibxm/Player.java
Normal file
@@ -0,0 +1,133 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
import javax.sound.sampled.*;
|
||||
|
||||
public class Player {
|
||||
private Thread play_thread;
|
||||
private IBXM ibxm;
|
||||
private Module module;
|
||||
private int song_duration, play_position;
|
||||
private boolean running, loop;
|
||||
private byte[] output_buffer;
|
||||
private SourceDataLine output_line;
|
||||
|
||||
/**
|
||||
Simple command-line test player.
|
||||
*/
|
||||
public static void main( String[] args ) throws Exception {
|
||||
if( args.length < 1 ) {
|
||||
System.err.println( "Usage: java ibxm.Player <module file>" );
|
||||
System.exit( 0 );
|
||||
}
|
||||
FileInputStream file_input_stream = new FileInputStream( args[ 0 ] );
|
||||
Player player = new Player();
|
||||
player.set_module( Player.load_module( file_input_stream ) );
|
||||
file_input_stream.close();
|
||||
player.play();
|
||||
}
|
||||
|
||||
/**
|
||||
Decode the data in the specified InputStream into a Module instance.
|
||||
@param input an InputStream containing the module file to be decoded.
|
||||
@throws IllegalArgumentException if the data is not recognised as a module file.
|
||||
*/
|
||||
public static Module load_module( InputStream input ) throws IllegalArgumentException, IOException {
|
||||
DataInputStream data_input_stream = new DataInputStream( input );
|
||||
/* Check if data is in XM format.*/
|
||||
byte[] xm_header = new byte[ 60 ];
|
||||
data_input_stream.readFully( xm_header );
|
||||
if( FastTracker2.is_xm( xm_header ) )
|
||||
return FastTracker2.load_xm( xm_header, data_input_stream );
|
||||
/* Check if data is in ScreamTracker 3 format.*/
|
||||
byte[] s3m_header = new byte[ 96 ];
|
||||
System.arraycopy( xm_header, 0, s3m_header, 0, 60 );
|
||||
data_input_stream.readFully( s3m_header, 60, 36 );
|
||||
if( ScreamTracker3.is_s3m( s3m_header ) )
|
||||
return ScreamTracker3.load_s3m( s3m_header, data_input_stream );
|
||||
/* Check if data is in ProTracker format.*/
|
||||
byte[] mod_header = new byte[ 1084 ];
|
||||
System.arraycopy( s3m_header, 0, mod_header, 0, 96 );
|
||||
data_input_stream.readFully( mod_header, 96, 988 );
|
||||
return ProTracker.load_mod( mod_header, data_input_stream );
|
||||
}
|
||||
|
||||
/**
|
||||
Instantiate a new Player.
|
||||
*/
|
||||
public Player() throws LineUnavailableException {
|
||||
ibxm = new IBXM( 48000 );
|
||||
set_loop( true );
|
||||
output_line = AudioSystem.getSourceDataLine( new AudioFormat( 48000, 16, 2, true, true ) );
|
||||
output_buffer = new byte[ 1024 * 4 ];
|
||||
}
|
||||
|
||||
/**
|
||||
Set the Module instance to be played.
|
||||
*/
|
||||
public void set_module( Module m ) {
|
||||
if( m != null ) module = m;
|
||||
stop();
|
||||
ibxm.set_module( module );
|
||||
song_duration = ibxm.calculate_song_duration();
|
||||
}
|
||||
|
||||
/**
|
||||
If loop is true, playback will continue indefinitely,
|
||||
otherwise the module will play through once and stop.
|
||||
*/
|
||||
public void set_loop( boolean loop ) {
|
||||
this.loop = loop;
|
||||
}
|
||||
|
||||
/**
|
||||
Open the audio device and begin playback.
|
||||
If a module is already playing it will be restarted.
|
||||
*/
|
||||
public void play() {
|
||||
stop();
|
||||
play_thread = new Thread( new Driver() );
|
||||
play_thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
Stop playback and close the audio device.
|
||||
*/
|
||||
public void stop() {
|
||||
running = false;
|
||||
if( play_thread != null ) {
|
||||
try {
|
||||
play_thread.join();
|
||||
} catch( InterruptedException ie ) {}
|
||||
}
|
||||
}
|
||||
|
||||
private class Driver implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if( running ) return;
|
||||
try {
|
||||
output_line.open();
|
||||
output_line.start();
|
||||
play_position = 0;
|
||||
running = true;
|
||||
while( running ) {
|
||||
int frames = song_duration - play_position;
|
||||
if( frames > 1024 ) frames = 1024;
|
||||
ibxm.get_audio( output_buffer, frames );
|
||||
output_line.write( output_buffer, 0, frames * 4 );
|
||||
play_position += frames;
|
||||
if( play_position >= song_duration ) {
|
||||
play_position = 0;
|
||||
if( !loop ) running = false;
|
||||
}
|
||||
}
|
||||
output_line.drain();
|
||||
output_line.close();
|
||||
} catch( LineUnavailableException lue ) {
|
||||
lue.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
233
build/tmp/recompileMc/sources/ibxm/ProTracker.java
Normal file
233
build/tmp/recompileMc/sources/ibxm/ProTracker.java
Normal file
@@ -0,0 +1,233 @@
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ProTracker {
|
||||
public static boolean is_mod( byte[] header_1084_bytes ) {
|
||||
boolean is_mod;
|
||||
is_mod = false;
|
||||
if( calculate_num_channels( header_1084_bytes ) > 0 ) {
|
||||
is_mod = true;
|
||||
}
|
||||
return is_mod;
|
||||
}
|
||||
|
||||
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.pal = ( num_channels == 4 );
|
||||
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 == 0x01 && effect_param == 0 ) {
|
||||
/* Portamento up of zero has no effect. */
|
||||
effect = 0;
|
||||
}
|
||||
if( effect == 0x02 && effect_param == 0 ) {
|
||||
/* Portamento down of zero has no effect. */
|
||||
effect = 0;
|
||||
}
|
||||
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, fine_tune;
|
||||
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_data_length = unsigned_short_be( mod_header, header_offset + 22 ) << 1;
|
||||
fine_tune = mod_header[ header_offset + 24 ] & 0x0F;
|
||||
if( fine_tune > 7 ) {
|
||||
fine_tune -= 16;
|
||||
}
|
||||
sample.transpose = ( fine_tune << IBXM.FP_SHIFT ) / 96;
|
||||
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.log_2( 7256 ) - LogTable.log_2( 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;
|
||||
}
|
||||
}
|
||||
239
build/tmp/recompileMc/sources/ibxm/Sample.java
Normal file
239
build/tmp/recompileMc/sources/ibxm/Sample.java
Normal file
@@ -0,0 +1,239 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
public class Sample {
|
||||
public String name;
|
||||
public boolean set_panning;
|
||||
public int volume, panning;
|
||||
public int transpose;
|
||||
|
||||
private int loop_start, loop_length;
|
||||
private short[] sample_data;
|
||||
|
||||
/* For the sinc interpolator.*/
|
||||
private static final int POINT_SHIFT = 4;
|
||||
private static final int POINTS = 1 << POINT_SHIFT;
|
||||
private static final int OVERLAP = POINTS >> 1;
|
||||
private static final int INTERP_SHIFT = IBXM.FP_SHIFT - 4;
|
||||
private static final int INTERP_BITMASK = ( 1 << INTERP_SHIFT ) - 1;
|
||||
private static final short[] sinc_table = {
|
||||
0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0, 0,
|
||||
0, 0, -5, 36, -142, 450, -1439, 32224, 2302, -974, 455, -190, 64, -15, 2, 0,
|
||||
0, 6, -33, 128, -391, 1042, -2894, 31584, 4540, -1765, 786, -318, 105, -25, 3, 0,
|
||||
0, 10, -55, 204, -597, 1533, -4056, 30535, 6977, -2573, 1121, -449, 148, -36, 5, 0,
|
||||
-1, 13, -71, 261, -757, 1916, -4922, 29105, 9568, -3366, 1448, -578, 191, -47, 7, 0,
|
||||
-1, 15, -81, 300, -870, 2185, -5498, 27328, 12263, -4109, 1749, -698, 232, -58, 9, 0,
|
||||
-1, 15, -86, 322, -936, 2343, -5800, 25249, 15006, -4765, 2011, -802, 269, -68, 10, 0,
|
||||
-1, 15, -87, 328, -957, 2394, -5849, 22920, 17738, -5298, 2215, -885, 299, -77, 12, 0,
|
||||
0, 14, -83, 319, -938, 2347, -5671, 20396, 20396, -5671, 2347, -938, 319, -83, 14, 0,
|
||||
0, 12, -77, 299, -885, 2215, -5298, 17738, 22920, -5849, 2394, -957, 328, -87, 15, -1,
|
||||
0, 10, -68, 269, -802, 2011, -4765, 15006, 25249, -5800, 2343, -936, 322, -86, 15, -1,
|
||||
0, 9, -58, 232, -698, 1749, -4109, 12263, 27328, -5498, 2185, -870, 300, -81, 15, -1,
|
||||
0, 7, -47, 191, -578, 1448, -3366, 9568, 29105, -4922, 1916, -757, 261, -71, 13, -1,
|
||||
0, 5, -36, 148, -449, 1121, -2573, 6977, 30535, -4056, 1533, -597, 204, -55, 10, 0,
|
||||
0, 3, -25, 105, -318, 786, -1765, 4540, 31584, -2894, 1042, -391, 128, -33, 6, 0,
|
||||
0, 2, -15, 64, -190, 455, -974, 2302, 32224, -1439, 450, -142, 36, -5, 0, 0,
|
||||
0, 0, -7, 27, -71, 142, -227, 299, 32439, 299, -227, 142, -71, 27, -7, 0
|
||||
};
|
||||
|
||||
public Sample() {
|
||||
name = "";
|
||||
set_sample_data( new short[ 0 ], 0, 0, false );
|
||||
}
|
||||
|
||||
public void set_sample_data( short[] data, int loop_start, int loop_length, boolean ping_pong ) {
|
||||
int offset;
|
||||
short sample;
|
||||
if( loop_start < 0 ) {
|
||||
loop_start = 0;
|
||||
}
|
||||
if( loop_start >= data.length ) {
|
||||
loop_start = data.length - 1;
|
||||
}
|
||||
if( loop_start + loop_length > data.length ) {
|
||||
loop_length = data.length - loop_start;
|
||||
}
|
||||
if( loop_length <= 1 ) {
|
||||
sample_data = new short[ OVERLAP + data.length + OVERLAP * 3 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, data.length );
|
||||
offset = 0;
|
||||
while( offset < OVERLAP ) {
|
||||
sample = sample_data[ OVERLAP + data.length - 1 ];
|
||||
sample = ( short ) ( sample * ( OVERLAP - offset ) / OVERLAP );
|
||||
sample_data[ OVERLAP + data.length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
loop_start = OVERLAP + data.length + OVERLAP;
|
||||
loop_length = 1;
|
||||
} else {
|
||||
if( ping_pong ) {
|
||||
sample_data = new short[ OVERLAP + loop_start + loop_length * 2 + OVERLAP * 2 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
|
||||
offset = 0;
|
||||
while( offset < loop_length ) {
|
||||
sample = data[ loop_start + loop_length - offset - 1 ];
|
||||
sample_data[ OVERLAP + loop_start + loop_length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
loop_start = loop_start + OVERLAP;
|
||||
loop_length = loop_length * 2;
|
||||
} else {
|
||||
sample_data = new short[ OVERLAP + loop_start + loop_length + OVERLAP * 2 ];
|
||||
System.arraycopy( data, 0, sample_data, OVERLAP, loop_start + loop_length );
|
||||
loop_start = loop_start + OVERLAP;
|
||||
}
|
||||
offset = 0;
|
||||
while( offset < OVERLAP * 2 ) {
|
||||
sample = sample_data[ loop_start + offset ];
|
||||
sample_data[ loop_start + loop_length + offset ] = sample;
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
this.loop_start = loop_start;
|
||||
this.loop_length = loop_length;
|
||||
}
|
||||
|
||||
public void resample_nearest(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int loop_end, offset, end, max_sample_idx;
|
||||
sample_idx += OVERLAP;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( frames > 0 ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
|
||||
if( max_sample_idx > loop_end ) {
|
||||
while( sample_idx <= loop_end ) {
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
} else {
|
||||
while( offset <= end ) {
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += sample_data[ sample_idx ] * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
frames = ( end - offset + 2 ) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void resample_linear(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int loop_end, offset, end, max_sample_idx, amplitude;
|
||||
sample_idx += OVERLAP;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( frames > 0 ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
max_sample_idx = sample_idx + ( ( sample_frac + ( frames - 1 ) * step ) >> IBXM.FP_SHIFT );
|
||||
if( max_sample_idx > loop_end ) {
|
||||
while( sample_idx <= loop_end ) {
|
||||
amplitude = sample_data[ sample_idx ];
|
||||
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
} else {
|
||||
while( offset <= end ) {
|
||||
amplitude = sample_data[ sample_idx ];
|
||||
amplitude += ( sample_data[ sample_idx + 1 ] - amplitude ) * sample_frac >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset++ ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
frames = ( end - offset + 2 ) >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void resample_sinc(
|
||||
int sample_idx, int sample_frac, int step, int left_gain, int right_gain,
|
||||
int[] mix_buffer, int frame_offset, int frames ) {
|
||||
int offset, end, loop_end, table_idx, a1, a2, amplitude;
|
||||
loop_end = loop_start + loop_length - 1;
|
||||
offset = frame_offset << 1;
|
||||
end = ( frame_offset + frames - 1 ) << 1;
|
||||
while( offset <= end ) {
|
||||
if( sample_idx > loop_end ) {
|
||||
if( loop_length <= 1 ) {
|
||||
break;
|
||||
}
|
||||
sample_idx = loop_start + ( sample_idx - loop_start ) % loop_length;
|
||||
}
|
||||
table_idx = ( sample_frac >> INTERP_SHIFT ) << POINT_SHIFT;
|
||||
a1 = sinc_table[ table_idx + 0 ] * sample_data[ sample_idx + 0 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 1 ] * sample_data[ sample_idx + 1 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 2 ] * sample_data[ sample_idx + 2 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 3 ] * sample_data[ sample_idx + 3 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 4 ] * sample_data[ sample_idx + 4 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 5 ] * sample_data[ sample_idx + 5 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 6 ] * sample_data[ sample_idx + 6 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 7 ] * sample_data[ sample_idx + 7 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 8 ] * sample_data[ sample_idx + 8 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 9 ] * sample_data[ sample_idx + 9 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 10 ] * sample_data[ sample_idx + 10 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 11 ] * sample_data[ sample_idx + 11 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 12 ] * sample_data[ sample_idx + 12 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 13 ] * sample_data[ sample_idx + 13 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 14 ] * sample_data[ sample_idx + 14 ] >> 15;
|
||||
a1 += sinc_table[ table_idx + 15 ] * sample_data[ sample_idx + 15 ] >> 15;
|
||||
a2 = sinc_table[ table_idx + 16 ] * sample_data[ sample_idx + 0 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 17 ] * sample_data[ sample_idx + 1 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 18 ] * sample_data[ sample_idx + 2 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 19 ] * sample_data[ sample_idx + 3 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 20 ] * sample_data[ sample_idx + 4 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 21 ] * sample_data[ sample_idx + 5 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 22 ] * sample_data[ sample_idx + 6 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 23 ] * sample_data[ sample_idx + 7 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 24 ] * sample_data[ sample_idx + 8 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 25 ] * sample_data[ sample_idx + 9 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 26 ] * sample_data[ sample_idx + 10 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 27 ] * sample_data[ sample_idx + 11 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 28 ] * sample_data[ sample_idx + 12 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 29 ] * sample_data[ sample_idx + 13 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 30 ] * sample_data[ sample_idx + 14 ] >> 15;
|
||||
a2 += sinc_table[ table_idx + 31 ] * sample_data[ sample_idx + 15 ] >> 15;
|
||||
amplitude = a1 + ( ( a2 - a1 ) * ( sample_frac & INTERP_BITMASK ) >> INTERP_SHIFT );
|
||||
mix_buffer[ offset ] += amplitude * left_gain >> IBXM.FP_SHIFT;
|
||||
mix_buffer[ offset + 1 ] += amplitude * right_gain >> IBXM.FP_SHIFT;
|
||||
offset += 2;
|
||||
sample_frac += step;
|
||||
sample_idx += sample_frac >> IBXM.FP_SHIFT;
|
||||
sample_frac &= IBXM.FP_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean has_finished( int sample_idx ) {
|
||||
boolean finished;
|
||||
finished = false;
|
||||
if( loop_length <= 1 && sample_idx > loop_start ) {
|
||||
finished = true;
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
}
|
||||
489
build/tmp/recompileMc/sources/ibxm/ScreamTracker3.java
Normal file
489
build/tmp/recompileMc/sources/ibxm/ScreamTracker3.java
Normal file
@@ -0,0 +1,489 @@
|
||||
|
||||
package ibxm;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
public class ScreamTracker3 {
|
||||
private static final int[] effect_map = new int[] {
|
||||
0xFF,
|
||||
0x25, /* A: Set Speed.*/
|
||||
0x0B, /* B: Pattern Jump.*/
|
||||
0x0D, /* C: Pattern Break.*/
|
||||
0x0A, /* D: Volume Slide.*/
|
||||
0x02, /* E: Portamento Down.*/
|
||||
0x01, /* F: Portamento Up.*/
|
||||
0x03, /* G: Tone Portamento.*/
|
||||
0x04, /* H: Vibrato.*/
|
||||
0x1D, /* I: Tremor.*/
|
||||
0x00, /* J: Arpeggio.*/
|
||||
0x06, /* K: Vibrato + Volume Slide.*/
|
||||
0x05, /* L: Tone Portamento + Volume Slide.*/
|
||||
0xFF, /* M: */
|
||||
0xFF, /* N: */
|
||||
0x09, /* O: Sample Offset.*/
|
||||
0xFF, /* P: */
|
||||
0x1B, /* Q: Retrig + Volume Slide.*/
|
||||
0x07, /* R: Tremolo.*/
|
||||
0x0E, /* S: Extended Effects.*/
|
||||
0x0F, /* T: Set Tempo.*/
|
||||
0x24, /* U: Fine Vibrato.*/
|
||||
0x10, /* V: Set Global Volume. */
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
private static final int[] effect_s_map = new int[] {
|
||||
0x00, /* 0: Set Filter.*/
|
||||
0x03, /* 1: Glissando.*/
|
||||
0x05, /* 2: Set Fine Tune.*/
|
||||
0x04, /* 3: Set Vibrato Waveform.*/
|
||||
0x07, /* 4: Set Tremolo Waveform.*/
|
||||
0xFF, /* 5: */
|
||||
0xFF, /* 6: */
|
||||
0xFF, /* 7: */
|
||||
0x08, /* 8: Set Panning.*/
|
||||
0xFF, /* 9: */
|
||||
0x09, /* A: Stereo Control.*/
|
||||
0x06, /* B: Pattern Loop.*/
|
||||
0x0C, /* C: Note Cut.*/
|
||||
0x0D, /* D: Note Delay.*/
|
||||
0x0E, /* E: Pattern Delay.*/
|
||||
0x0F /* F: Invert Loop.*/
|
||||
};
|
||||
|
||||
public static boolean is_s3m( byte[] header_96_bytes ) {
|
||||
String s3m_identifier;
|
||||
s3m_identifier = ascii_text( header_96_bytes, 44, 4 );
|
||||
return s3m_identifier.equals( "SCRM" );
|
||||
}
|
||||
|
||||
public static Module load_s3m( byte[] header_96_bytes, DataInput data_input ) throws IOException {
|
||||
int num_pattern_orders, num_instruments, num_patterns, num_channels;
|
||||
@SuppressWarnings("unused") //Forge
|
||||
int flags, tracker_version, master_volume, panning, channel_config, sequence_length;
|
||||
int instrument_idx, pattern_idx, channel_idx, order_idx, panning_offset;
|
||||
boolean signed_samples, stereo_mode, default_panning;
|
||||
int[] channel_map, sequence;
|
||||
byte[] s3m_file;
|
||||
Module module;
|
||||
Instrument instrument;
|
||||
s3m_file = read_s3m_file( header_96_bytes, data_input );
|
||||
module = new Module();
|
||||
module.song_title = ascii_text( s3m_file, 0, 28 );
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
num_instruments = get_num_instruments( s3m_file );
|
||||
num_patterns = get_num_patterns( s3m_file );
|
||||
flags = unsigned_short_le( s3m_file, 38 );
|
||||
tracker_version = unsigned_short_le( s3m_file, 40 );
|
||||
if( ( flags & 0x40 ) == 0x40 || tracker_version == 0x1300 ) {
|
||||
module.fast_volume_slides = true;
|
||||
}
|
||||
signed_samples = false;
|
||||
if( unsigned_short_le( s3m_file, 42 ) == 0x01 ) {
|
||||
signed_samples = true;
|
||||
}
|
||||
module.global_volume = s3m_file[ 48 ] & 0xFF;
|
||||
module.default_speed = s3m_file[ 49 ] & 0xFF;
|
||||
module.default_tempo = s3m_file[ 50 ] & 0xFF;
|
||||
master_volume = s3m_file[ 51 ] & 0x7F;
|
||||
module.channel_gain = ( master_volume << IBXM.FP_SHIFT ) >> 7;
|
||||
stereo_mode = ( s3m_file[ 51 ] & 0x80 ) == 0x80;
|
||||
default_panning = ( s3m_file[ 53 ] & 0xFF ) == 0xFC;
|
||||
channel_map = new int[ 32 ];
|
||||
num_channels = 0;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
channel_config = s3m_file[ 64 + channel_idx ] & 0xFF;
|
||||
channel_map[ channel_idx ] = -1;
|
||||
if( channel_config < 16 ) {
|
||||
channel_map[ channel_idx ] = num_channels;
|
||||
num_channels += 1;
|
||||
}
|
||||
}
|
||||
module.set_num_channels( num_channels );
|
||||
panning_offset = 96 + num_pattern_orders + num_instruments * 2 + num_patterns * 2;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
if( channel_map[ channel_idx ] < 0 ) continue;
|
||||
panning = 7;
|
||||
if( stereo_mode ) {
|
||||
panning = 12;
|
||||
if( ( s3m_file[ 64 + channel_idx ] & 0xFF ) < 8 ) {
|
||||
panning = 3;
|
||||
}
|
||||
}
|
||||
if( default_panning ) {
|
||||
flags = s3m_file[ panning_offset + channel_idx ] & 0xFF;
|
||||
if( ( flags & 0x20 ) == 0x20 ) {
|
||||
panning = flags & 0xF;
|
||||
}
|
||||
}
|
||||
module.set_initial_panning( channel_map[ channel_idx ], panning * 17 );
|
||||
}
|
||||
sequence = read_s3m_sequence( s3m_file );
|
||||
module.set_sequence_length( sequence.length );
|
||||
for( order_idx = 0; order_idx < sequence.length; order_idx++ ) {
|
||||
module.set_sequence( order_idx, sequence[ order_idx ] );
|
||||
}
|
||||
module.set_num_instruments( num_instruments );
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument = read_s3m_instrument( s3m_file, instrument_idx, signed_samples );
|
||||
module.set_instrument( instrument_idx + 1, instrument );
|
||||
}
|
||||
module.set_num_patterns( num_patterns );
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
module.set_pattern( pattern_idx, read_s3m_pattern( s3m_file, pattern_idx, channel_map ) );
|
||||
}
|
||||
return module;
|
||||
}
|
||||
|
||||
private static int[] read_s3m_sequence( byte[] s3m_file ) {
|
||||
int num_pattern_orders, sequence_length;
|
||||
int sequence_idx, order_idx, pattern_order;
|
||||
int[] sequence;
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
sequence_length = 0;
|
||||
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
|
||||
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
|
||||
if( pattern_order == 255 ) {
|
||||
break;
|
||||
} else if( pattern_order < 254 ) {
|
||||
sequence_length += 1;
|
||||
}
|
||||
}
|
||||
sequence = new int[ sequence_length ];
|
||||
sequence_idx = 0;
|
||||
for( order_idx = 0; order_idx < num_pattern_orders; order_idx++ ) {
|
||||
pattern_order = s3m_file[ 96 + order_idx ] & 0xFF;
|
||||
if( pattern_order == 255 ) {
|
||||
break;
|
||||
} else if( pattern_order < 254 ) {
|
||||
sequence[ sequence_idx ] = pattern_order;
|
||||
sequence_idx += 1;
|
||||
}
|
||||
}
|
||||
return sequence;
|
||||
}
|
||||
|
||||
private static Instrument read_s3m_instrument( byte[] s3m_file, int instrument_idx, boolean signed_samples ) {
|
||||
int instrument_offset;
|
||||
int sample_data_offset, sample_data_length;
|
||||
int loop_start, loop_length, c2_rate, sample_idx, amplitude;
|
||||
boolean sixteen_bit;
|
||||
Instrument instrument;
|
||||
Sample sample;
|
||||
short[] sample_data;
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
instrument = new Instrument();
|
||||
instrument.name = ascii_text( s3m_file, instrument_offset + 48, 28 );
|
||||
sample = new Sample();
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_length = get_sample_data_length( s3m_file, instrument_offset );
|
||||
loop_start = unsigned_short_le( s3m_file, instrument_offset + 20 );
|
||||
loop_length = unsigned_short_le( s3m_file, instrument_offset + 24 ) - loop_start;
|
||||
sample.volume = s3m_file[ instrument_offset + 28 ] & 0xFF;
|
||||
if( s3m_file[ instrument_offset + 30 ] != 0 ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Packed samples not supported!" );
|
||||
}
|
||||
if( ( s3m_file[ instrument_offset + 31 ] & 0x01 ) == 0 ) {
|
||||
loop_length = 0;
|
||||
}
|
||||
if( ( s3m_file[ instrument_offset + 31 ] & 0x02 ) != 0 ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Stereo samples not supported!" );
|
||||
}
|
||||
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
|
||||
c2_rate = unsigned_short_le( s3m_file, instrument_offset + 32 );
|
||||
sample.transpose = LogTable.log_2( c2_rate ) - LogTable.log_2( 8363 );
|
||||
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
|
||||
if( sixteen_bit ) {
|
||||
if( signed_samples ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Signed 16-bit samples not supported!" );
|
||||
}
|
||||
sample_data_length >>= 1;
|
||||
sample_data = new short[ sample_data_length ];
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = s3m_file[ sample_data_offset + sample_idx * 2 ] & 0xFF;
|
||||
amplitude |= ( s3m_file[ sample_data_offset + sample_idx * 2 + 1 ] & 0xFF ) << 8;
|
||||
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
|
||||
}
|
||||
} else {
|
||||
sample_data = new short[ sample_data_length ];
|
||||
if( signed_samples ) {
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = s3m_file[ sample_data_offset + sample_idx ] << 8;
|
||||
sample_data[ sample_idx ] = ( short ) amplitude;
|
||||
}
|
||||
} else {
|
||||
for( sample_idx = 0; sample_idx < sample_data_length; sample_idx++ ) {
|
||||
amplitude = ( s3m_file[ sample_data_offset + sample_idx ] & 0xFF ) << 8;
|
||||
sample_data[ sample_idx ] = ( short ) ( amplitude - 32768 );
|
||||
}
|
||||
}
|
||||
}
|
||||
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 Pattern read_s3m_pattern( byte[] s3m_file, int pattern_idx, int[] channel_map ) {
|
||||
int pattern_offset;
|
||||
int num_channels, num_notes;
|
||||
int row_idx, channel_idx, note_idx;
|
||||
int token, key, volume_column, effect, effect_param;
|
||||
byte[] pattern_data;
|
||||
Pattern pattern;
|
||||
num_channels = 0;
|
||||
for( channel_idx = 0; channel_idx < 32; channel_idx++ ) {
|
||||
if( channel_map[ channel_idx ] >= num_channels ) {
|
||||
num_channels = channel_idx + 1;
|
||||
}
|
||||
}
|
||||
num_notes = num_channels * 64;
|
||||
pattern_data = new byte[ num_notes * 5 ];
|
||||
row_idx = 0;
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx ) + 2;
|
||||
while( row_idx < 64 ) {
|
||||
token = s3m_file[ pattern_offset ] & 0xFF;
|
||||
pattern_offset += 1;
|
||||
if( token > 0 ) {
|
||||
channel_idx = channel_map[ token & 0x1F ];
|
||||
note_idx = ( num_channels * row_idx + channel_idx ) * 5;
|
||||
if( ( token & 0x20 ) == 0x20 ) {
|
||||
/* Key + Instrument.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
key = s3m_file[ pattern_offset ] & 0xFF;
|
||||
if( key == 255 ) {
|
||||
key = 0;
|
||||
} else if( key == 254 ) {
|
||||
key = 97;
|
||||
} else {
|
||||
key = ( ( key & 0xF0 ) >> 4 ) * 12 + ( key & 0x0F ) + 1;
|
||||
while( key > 96 ) {
|
||||
key = key - 12;
|
||||
}
|
||||
}
|
||||
pattern_data[ note_idx ] = ( byte ) key;
|
||||
pattern_data[ note_idx + 1 ] = s3m_file[ pattern_offset + 1 ];
|
||||
}
|
||||
pattern_offset += 2;
|
||||
}
|
||||
if( ( token & 0x40 ) == 0x40 ) {
|
||||
/* Volume.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
volume_column = ( s3m_file[ pattern_offset ] & 0xFF ) + 0x10;
|
||||
pattern_data[ note_idx + 2 ] = ( byte ) volume_column;
|
||||
}
|
||||
pattern_offset += 1;
|
||||
}
|
||||
if( ( token & 0x80 ) == 0x80 ) {
|
||||
/* Effect + Param.*/
|
||||
if( channel_idx >= 0 ) {
|
||||
effect = s3m_file[ pattern_offset ] & 0xFF;
|
||||
effect_param = s3m_file[ pattern_offset + 1 ] & 0xFF;
|
||||
effect = effect_map[ effect & 0x1F ];
|
||||
if( effect == 0xFF ) {
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
}
|
||||
if( effect == 0x0E ) {
|
||||
effect = effect_s_map[ ( effect_param & 0xF0 ) >> 4 ];
|
||||
effect_param = effect_param & 0x0F;
|
||||
switch( effect ) {
|
||||
case 0x08:
|
||||
effect = 0x08;
|
||||
effect_param = effect_param * 17;
|
||||
break;
|
||||
case 0x09:
|
||||
effect = 0x08;
|
||||
if( effect_param > 7 ) {
|
||||
effect_param -= 8;
|
||||
} else {
|
||||
effect_param += 8;
|
||||
}
|
||||
effect_param = effect_param * 17;
|
||||
break;
|
||||
case 0xFF:
|
||||
effect = 0;
|
||||
effect_param = 0;
|
||||
break;
|
||||
default:
|
||||
effect_param = ( ( effect & 0x0F ) << 4 ) | ( effect_param & 0x0F );
|
||||
effect = 0x0E;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pattern_data[ note_idx + 3 ] = ( byte ) effect;
|
||||
pattern_data[ note_idx + 4 ] = ( byte ) effect_param;
|
||||
}
|
||||
pattern_offset += 2;
|
||||
}
|
||||
} else {
|
||||
row_idx += 1;
|
||||
}
|
||||
}
|
||||
pattern = new Pattern();
|
||||
pattern.num_rows = 64;
|
||||
pattern.set_pattern_data( pattern_data );
|
||||
return pattern;
|
||||
}
|
||||
|
||||
private static byte[] read_s3m_file( byte[] header_96_bytes, DataInput data_input ) throws IOException {
|
||||
int s3m_file_length;
|
||||
int num_pattern_orders, num_instruments, num_patterns;
|
||||
int instrument_idx, pattern_idx;
|
||||
int instrument_offset, sample_data_offset, pattern_offset;
|
||||
byte[] s3m_file;
|
||||
if( !is_s3m( header_96_bytes ) ) {
|
||||
throw new IllegalArgumentException( "ScreamTracker3: Not an S3M file!" );
|
||||
}
|
||||
s3m_file = header_96_bytes;
|
||||
s3m_file_length = header_96_bytes.length;
|
||||
num_pattern_orders = get_num_pattern_orders( s3m_file );
|
||||
num_instruments = get_num_instruments( s3m_file );
|
||||
num_patterns = get_num_patterns( s3m_file );
|
||||
s3m_file_length += num_pattern_orders;
|
||||
s3m_file_length += num_instruments * 2;
|
||||
s3m_file_length += num_patterns * 2;
|
||||
/* Read enough of file to calculate the length.*/
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
instrument_offset += 80;
|
||||
if( instrument_offset > s3m_file_length ) {
|
||||
s3m_file_length = instrument_offset;
|
||||
}
|
||||
}
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
|
||||
pattern_offset += 2;
|
||||
if( pattern_offset > s3m_file_length ) {
|
||||
s3m_file_length = pattern_offset;
|
||||
}
|
||||
}
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
/* Read rest of file.*/
|
||||
for( instrument_idx = 0; instrument_idx < num_instruments; instrument_idx++ ) {
|
||||
instrument_offset = get_instrument_offset( s3m_file, instrument_idx );
|
||||
sample_data_offset = get_sample_data_offset( s3m_file, instrument_offset );
|
||||
sample_data_offset += get_sample_data_length( s3m_file, instrument_offset );
|
||||
if( sample_data_offset > s3m_file_length ) {
|
||||
s3m_file_length = sample_data_offset;
|
||||
}
|
||||
}
|
||||
for( pattern_idx = 0; pattern_idx < num_patterns; pattern_idx++ ) {
|
||||
pattern_offset = get_pattern_offset( s3m_file, pattern_idx );
|
||||
pattern_offset += get_pattern_length( s3m_file, pattern_offset );
|
||||
pattern_offset += 2;
|
||||
if( pattern_offset > s3m_file_length ) {
|
||||
s3m_file_length = pattern_offset;
|
||||
}
|
||||
}
|
||||
s3m_file = read_more( s3m_file, s3m_file_length, data_input );
|
||||
return s3m_file;
|
||||
}
|
||||
|
||||
private static int get_num_pattern_orders( byte[] s3m_file ) {
|
||||
int num_pattern_orders;
|
||||
num_pattern_orders = unsigned_short_le( s3m_file, 32 );
|
||||
return num_pattern_orders;
|
||||
}
|
||||
|
||||
private static int get_num_instruments( byte[] s3m_file ) {
|
||||
int num_instruments;
|
||||
num_instruments = unsigned_short_le( s3m_file, 34 );
|
||||
return num_instruments;
|
||||
}
|
||||
|
||||
private static int get_num_patterns( byte[] s3m_file ) {
|
||||
int num_patterns;
|
||||
num_patterns = unsigned_short_le( s3m_file, 36 );
|
||||
return num_patterns;
|
||||
}
|
||||
|
||||
private static int get_instrument_offset( byte[] s3m_file, int instrument_idx ) {
|
||||
int instrument_offset, pointer_offset;
|
||||
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
|
||||
instrument_offset = unsigned_short_le( s3m_file, pointer_offset + instrument_idx * 2 ) << 4;
|
||||
return instrument_offset;
|
||||
}
|
||||
|
||||
private static int get_sample_data_offset( byte[] s3m_file, int instrument_offset ) {
|
||||
int sample_data_offset;
|
||||
sample_data_offset = 0;
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_offset = ( s3m_file[ instrument_offset + 13 ] & 0xFF ) << 20;
|
||||
sample_data_offset |= unsigned_short_le( s3m_file, instrument_offset + 14 ) << 4;
|
||||
}
|
||||
return sample_data_offset;
|
||||
}
|
||||
|
||||
private static int get_sample_data_length( byte[] s3m_file, int instrument_offset ) {
|
||||
int sample_data_length;
|
||||
boolean sixteen_bit;
|
||||
sample_data_length = 0;
|
||||
if( s3m_file[ instrument_offset ] == 1 ) {
|
||||
sample_data_length = unsigned_short_le( s3m_file, instrument_offset + 16 );
|
||||
sixteen_bit = ( s3m_file[ instrument_offset + 31 ] & 0x04 ) != 0;
|
||||
if( sixteen_bit ) {
|
||||
sample_data_length <<= 1;
|
||||
}
|
||||
}
|
||||
return sample_data_length;
|
||||
}
|
||||
|
||||
private static int get_pattern_offset( byte[] s3m_file, int pattern_idx ) {
|
||||
int pattern_offset, pointer_offset;
|
||||
pointer_offset = 96 + get_num_pattern_orders( s3m_file );
|
||||
pointer_offset += get_num_instruments( s3m_file ) * 2;
|
||||
pattern_offset = unsigned_short_le( s3m_file, pointer_offset + pattern_idx * 2 ) << 4;
|
||||
return pattern_offset;
|
||||
}
|
||||
|
||||
private static int get_pattern_length( byte[] s3m_file, int pattern_offset ) {
|
||||
int pattern_length;
|
||||
pattern_length = unsigned_short_le( s3m_file, pattern_offset );
|
||||
return pattern_length;
|
||||
}
|
||||
|
||||
private static byte[] read_more( byte[] old_data, int new_length, DataInput data_input ) throws IOException {
|
||||
byte[] new_data;
|
||||
new_data = old_data;
|
||||
if( new_length > old_data.length ) {
|
||||
new_data = new byte[ new_length ];
|
||||
System.arraycopy( old_data, 0, new_data, 0, old_data.length );
|
||||
try {
|
||||
data_input.readFully( new_data, old_data.length, new_data.length - old_data.length );
|
||||
} catch( EOFException e ) {
|
||||
System.out.println( "ScreamTracker3: Module has been truncated!" );
|
||||
}
|
||||
}
|
||||
return new_data;
|
||||
}
|
||||
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user