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