
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 );
	}
}
