/*
Calf Box, an open source musical instrument.
Copyright (C) 2010-2013 Krzysztof Foltman

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"
#include "config-api.h"
#include "dspmath.h"
#include "errors.h"
#include "midi.h"
#include "module.h"
#include "rt.h"
#include "sampler.h"
#include "sampler_impl.h"
#include "sfzloader.h"
#include "stm.h"
#include <assert.h>
#include <errno.h>
#include <glib.h>
#include <math.h>
#include <memory.h>
#include <sndfile.h>
#include <stdio.h>
#include <stdlib.h>


static void lfo_update_freq(struct sampler_lfo *lfo, struct sampler_lfo_params *lfop, int srate, double srate_inv)
{
    lfo->delta = (uint32_t)(lfop->freq * 65536.0 * 65536.0 * CBOX_BLOCK_SIZE * srate_inv);
    lfo->delay = (uint32_t)(lfop->delay * srate);
    lfo->fade = (uint32_t)(lfop->fade * srate);
    lfo->wave = (int32_t)(lfop->wave);
}

static void lfo_init(struct sampler_lfo *lfo, struct sampler_lfo_params *lfop, int srate, double srate_inv)
{
    lfo->phase = 0;
    lfo->xdelta = 0;
    lfo->age = 0;
    lfo->random_value = 0; // safe, less CPU intensive value
    lfo_update_freq(lfo, lfop, srate, srate_inv);
}

static inline float lfo_run(struct sampler_lfo *lfo)
{
    if (lfo->age < lfo->delay)
    {
        lfo->age += CBOX_BLOCK_SIZE;
        return 0.f;
    }
    uint32_t delta = lfo->delta + lfo->xdelta;

    const int FRAC_BITS = 32 - 11;

    float v = 0;
    switch(lfo->wave) {
        case 0: // triangle
        {
            uint32_t ph = lfo->phase + 0x40000000;
            uint32_t tri = (ph & 0x7FFFFFFF) ^ (((ph >> 31) - 1) & 0x7FFFFFFF);
            v = -1.0 + tri * (2.0 / (1U << 31));
            break;
        }
        case 1: // sine (default)
        default:
        {
            uint32_t iphase = lfo->phase >> FRAC_BITS;
            float frac = (lfo->phase & ((1 << FRAC_BITS) - 1)) * (1.0 / (1 << FRAC_BITS));
            v = sampler_sine_wave[iphase] + (sampler_sine_wave[iphase + 1] - sampler_sine_wave[iphase]) * frac;
            break;
        }
        case 2:
            v = lfo->phase < 0xC0000000 ? 1 : -1;
            break;
        case 3:
            v = lfo->phase < 0x80000000 ? 1 : -1;
            break;
        case 4:
            v = lfo->phase < 0x40000000 ? 1 : -1;
            break;
        case 5:
            v = lfo->phase < 0x20000000 ? 1 : -1;
            break;
        case 6:
            v = -1 + lfo->phase * (1.0 / (1U << 31));
            break;
        case 7:
            v = 1 - lfo->phase * (1.0 / (1U << 31));
            break;
        case 12:
            if ((lfo->phase & 0x80000000) != ((lfo->phase + delta) & 0x80000000))
                lfo->random_value = -1 + 2 * rand() / (1.0 * RAND_MAX);
            v = lfo->random_value;
            break;
    }
    lfo->phase += delta;
    if (lfo->fade && lfo->age < lfo->delay + lfo->fade)
    {
        v *= (lfo->age - lfo->delay) * 1.0 / lfo->fade;
        lfo->age += CBOX_BLOCK_SIZE;
    }

    return v;
}

static gboolean is_tail_finished(struct sampler_voice *v)
{
    if (!v->layer->computed.eff_num_stages)
        return TRUE;
    double eps = 1.0 / 65536.0;
    if (cbox_biquadf_is_audible(&v->filter.filter_left[0], eps))
        return FALSE;
    if (cbox_biquadf_is_audible(&v->filter.filter_right[0], eps))
        return FALSE;
    int num_stages = v->layer->computed.eff_num_stages;
    if (num_stages > 1)
    {
        if (cbox_biquadf_is_audible(&v->filter.filter_left[num_stages - 1], eps))
            return FALSE;
        if (cbox_biquadf_is_audible(&v->filter.filter_right[num_stages - 1], eps))
            return FALSE;
    }
    
    return TRUE;
}

#if USE_NEON

#include <arm_neon.h>

static inline void mix_block_into_with_gain(cbox_sample_t **outputs, int oofs, float *src_leftright, float gain)
{
    float *dst_left = outputs[oofs];
    float *dst_right = outputs[oofs + 1];
    float32x2_t gain2 = {gain, gain};
    for (size_t i = 0; i < CBOX_BLOCK_SIZE; i += 2)
    {
        float32x2_t lr1 = vld1_f32(&src_leftright[2 * i]);
        float32x2_t lr2 = vld1_f32(&src_leftright[2 * i + 2]);
        float32x2x2_t lr12 = vtrn_f32(lr1, lr2);
        float32x2_t dl1 = vld1_f32(&dst_left[i]);
        float32x2_t dr1 = vld1_f32(&dst_right[i]);
        
        float32x2_t l1 = vmla_f32(dl1, lr12.val[0], gain2);
        vst1_f32(&dst_left[i], l1);
        float32x2_t r1 = vmla_f32(dr1, lr12.val[1], gain2);
        vst1_f32(&dst_right[i], r1);
    }
}

static inline void mix_block_into(cbox_sample_t **outputs, int oofs, float *src_leftright)
{
    float *dst_left = outputs[oofs];
    float *dst_right = outputs[oofs + 1];
    for (size_t i = 0; i < CBOX_BLOCK_SIZE; i += 2)
    {
        float32x2_t lr1 = vld1_f32(&src_leftright[2 * i]);
        float32x2_t lr2 = vld1_f32(&src_leftright[2 * i + 2]);
        float32x2x2_t lr12 = vtrn_f32(lr1, lr2);
        float32x2_t dl1 = vld1_f32(&dst_left[i]);
        float32x2_t dr1 = vld1_f32(&dst_right[i]);
        
        float32x2_t l1 = vadd_f32(dl1, lr12.val[0]);
        vst1_f32(&dst_left[i], l1);
        float32x2_t r1 = vadd_f32(dr1, lr12.val[1]);
        vst1_f32(&dst_right[i], r1);
    }
}

#else

static inline void mix_block_into_with_gain(cbox_sample_t **outputs, int oofs, float *src_leftright, float gain)
{
    cbox_sample_t *dst_left = outputs[oofs];
    cbox_sample_t *dst_right = outputs[oofs + 1];
    for (size_t i = 0; i < CBOX_BLOCK_SIZE; i++)
    {
        dst_left[i] += gain * src_leftright[2 * i];
        dst_right[i] += gain * src_leftright[2 * i + 1];
    }
}

static inline void mix_block_into(cbox_sample_t **outputs, int oofs, float *src_leftright)
{
    cbox_sample_t *dst_left = outputs[oofs];
    cbox_sample_t *dst_right = outputs[oofs + 1];
    for (size_t i = 0; i < CBOX_BLOCK_SIZE; i++)
    {
        dst_left[i] += src_leftright[2 * i];
        dst_right[i] += src_leftright[2 * i + 1];
    }
}

#endif

////////////////////////////////////////////////////////////////////////////////

static float sfz_crossfade(float param, float xfin_lo, float xfin_hi, float xfout_lo, float xfout_hi, enum sampler_xf_curve xfc)
{
    if (param >= xfin_hi && param <= xfout_lo)
        return 1.f;
    if (param < xfin_lo || param > xfout_hi)
        return 0.f;
    float for0 = (param < xfout_lo) ? xfin_lo : xfout_hi;
    float for1 = (param < xfout_lo) ? xfin_hi : xfout_lo;
    if (for0 == for1)
        return 1.f;
    if (xfc == stxc_gain)
        return (param - for0) / (for1 - for0);
    else
    {
        float v = (param - for0) / (for1 - for0);
        return sqrtf(v);
    }
}

// One-half version for CC-based crossfades
static inline float sfz_crossfade2(float param, float xflo, float xfhi, float left, float right, enum sampler_xf_curve xfc)
{
    if (xflo > xfhi)
        return sfz_crossfade2(param, xfhi, xflo, right, left, xfc);
    if (param <= xflo)
        return left;
    if (param >= xfhi)
        return right;
    float res;
    if (xflo == xfhi)
        res = 0.5f * (left + right);
    else
        res = left + (right - left) * (param - xflo) / (xfhi - xflo);
    if (xfc == stxc_gain)
        return res;
    else
        return sqrtf(res);
}

////////////////////////////////////////////////////////////////////////////////

void sampler_voice_activate(struct sampler_voice *v, enum sampler_player_type mode)
{
    assert(v->gen.mode == spt_inactive);
    sampler_voice_unlink(&v->program->module->voices_free, v);
    assert(mode != spt_inactive);
    assert(v->channel);
    v->gen.mode = mode;
    sampler_voice_link(&v->channel->voices_running, v);
}

void sampler_voice_start_silent(struct sampler_layer_data *l, struct sampler_released_groups *exgroupdata)
{
    if (l->group >= 1)
        sampler_released_groups_add(exgroupdata, l->group);
}

void sampler_voice_start(struct sampler_voice *v, struct sampler_channel *c, struct sampler_layer_data *l, int note, int vel, struct sampler_released_groups *exgroupdata)
{
    struct sampler_module *m = c->module;
    sampler_gen_reset(&v->gen);
    
    v->age = 0;
    if (l->trigger == stm_release)
    {
        // time since last 'note on' for that note
        v->age = m->current_time - c->prev_note_start_time[note];
        double age = v->age *  m->module.srate_inv;
        // if attenuation is more than 84dB, ignore the release trigger
        if (age * l->rt_decay > 84)
            return;
    }
    uint32_t end = l->computed.eff_waveform->info.frames;
    if (l->end != 0)
        end = (l->end == SAMPLER_NO_LOOP) ? 0 : l->end;
    v->last_waveform = l->computed.eff_waveform;
    v->gen.cur_sample_end = end;
    if (end > l->computed.eff_waveform->info.frames)
        end = l->computed.eff_waveform->info.frames;
    
    assert(!v->current_pipe);
    if (end > l->computed.eff_waveform->preloaded_frames)
    {
        if (l->computed.eff_loop_mode == slm_loop_continuous && l->computed.eff_loop_end < l->computed.eff_waveform->preloaded_frames)
        {
            // Everything fits in prefetch, because loop ends in prefetch and post-loop part is not being played
        }
        else
        {
            uint32_t loop_start = -1, loop_end = end;
            // If in loop mode, set the loop over the looped part... unless we're doing sustain-only loop on prefetch area only. Then
            // streaming will only cover the release part, and it shouldn't be looped.
            if (l->computed.eff_loop_mode == slm_loop_continuous || (l->computed.eff_loop_mode == slm_loop_sustain && l->computed.eff_loop_end >= l->computed.eff_waveform->preloaded_frames))
            {
                loop_start = l->computed.eff_loop_start;
                loop_end = l->computed.eff_loop_end;
            }
            // Those are initial values only, they will be adjusted in process function
            v->current_pipe = cbox_prefetch_stack_pop(m->pipe_stack, l->computed.eff_waveform, loop_start, loop_end, l->count);
            if (!v->current_pipe)
            {
                g_warning("Prefetch pipe pool exhausted, no streaming playback will be possible");
                end = l->computed.eff_waveform->preloaded_frames;
                v->gen.cur_sample_end = end;
            }
        }
    }
    
    v->output_pair_no = (l->output + c->output_shift) % m->output_pairs;
    v->serial_no = m->serial_no;

    v->gen.loop_overlap = l->loop_overlap;
    v->gen.loop_overlap_step = l->loop_overlap > 0 ? 1.0 / l->loop_overlap : 0;
    v->gain_fromvel = l->computed.eff_amp_velcurve[vel];
    v->gain_shift = (note - l->amp_keycenter) * l->amp_keytrack;

    v->gain_fromvel *= sfz_crossfade(note, l->xfin_lokey, l->xfin_hikey, l->xfout_lokey, l->xfout_hikey, l->xf_keycurve);
    v->gain_fromvel *= sfz_crossfade(vel, l->xfin_lovel, l->xfin_hivel, l->xfout_lovel, l->xfout_hivel, l->xf_velcurve);

    v->note = note;
    v->vel = vel;
    v->off_vel = 0;
    v->pitch_shift = 0;
    v->released = 0;
    v->released_with_sustain = 0;
    v->released_with_sostenuto = 0;
    v->captured_sostenuto = 0;
    v->channel = c;
    v->layer = l;
    v->program = c->program;
    v->amp_env.shape = &l->amp_env_shape;
    v->filter_env.shape = &l->filter_env_shape;
    v->pitch_env.shape = &l->pitch_env_shape;
    
    v->cutoff_shift = vel * l->fil_veltrack / 127.0 + (note - l->fil_keycenter) * l->fil_keytrack;
    v->cutoff2_shift = vel * l->fil2_veltrack / 127.0 + (note - l->fil2_keycenter) * l->fil2_keytrack;
    v->loop_mode = l->computed.eff_loop_mode;
    v->off_by = l->off_by;
    v->reloffset = l->reloffset;
    int auxes = (m->module.outputs - m->module.aux_offset) / 2;
    if (l->effect1bus >= 1 && l->effect1bus < 1 + auxes)
        v->send1bus = l->effect1bus;
    else
        v->send1bus = 0;
    if (l->effect2bus >= 1 && l->effect2bus < 1 + auxes)
        v->send2bus = l->effect2bus;
    else
        v->send2bus = 0;
    v->send1gain = l->effect1 * 0.01;
    v->send2gain = l->effect2 * 0.01;
    if (l->group >= 1)
    {
        sampler_released_groups_add(exgroupdata, l->group);
    }
    lfo_init(&v->amp_lfo, &l->amp_lfo, m->module.srate, m->module.srate_inv);
    lfo_init(&v->filter_lfo, &l->filter_lfo, m->module.srate, m->module.srate_inv);
    lfo_init(&v->pitch_lfo, &l->pitch_lfo, m->module.srate, m->module.srate_inv);
    
    for (int i = 0; i < 3; ++i)
    {
        cbox_biquadf_reset(&v->filter.filter_left[i]);
        cbox_biquadf_reset(&v->filter.filter_right[i]);
        cbox_biquadf_reset(&v->filter2.filter_left[i]);
        cbox_biquadf_reset(&v->filter2.filter_right[i]);
    }
    cbox_onepolef_reset(&v->onepole_left);
    cbox_onepolef_reset(&v->onepole_right);
    // set gain later (it's a less expensive operation)
    if (l->tonectl_freq != 0)
        cbox_onepolef_set_highshelf_tonectl(&v->onepole_coeffs, l->tonectl_freq * M_PI * m->module.srate_inv, 1.0);
    
    v->offset = l->offset;
    for(struct sampler_noteinitfunc *nif = v->layer->voice_nifs; nif; nif = nif->next)
        nif->key.notefunc_voice(nif, v);
    if (v->gain_shift)
        v->gain_fromvel *= dB2gain(v->gain_shift);

    if (v->offset + v->reloffset != 0)
    {
        // For streamed samples, allow only half the preload period worth of offset to avoid gaps
        // (maybe we can allow up to preload period minus one buffer size here?)
        uint32_t maxend = v->current_pipe ? (l->computed.eff_waveform->preloaded_frames >> 1) : l->computed.eff_waveform->preloaded_frames;
        int32_t pos = v->offset + v->reloffset * maxend * 0.01;
        if (pos < 0)
            pos = 0;
        if ((uint32_t)pos > maxend)
            pos = (int32_t)maxend;
        v->offset = pos;
    }
    
    cbox_envelope_reset(&v->amp_env);
    cbox_envelope_reset(&v->filter_env);
    cbox_envelope_reset(&v->pitch_env);

    v->last_eq_bitmask = 0;

    sampler_voice_activate(v, l->computed.eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16);
    
    uint32_t pos = v->offset;
    if (l->offset_random)
        pos += ((uint32_t)(rand() + (rand() << 16))) % l->offset_random;
    if (pos >= end)
        pos = end;
    v->gen.bigpos = ((uint64_t)pos) << 32;
    v->gen.virtpos = ((uint64_t)pos) << 32;
    
    if (v->current_pipe && v->gen.bigpos)
        cbox_prefetch_pipe_consumed(v->current_pipe, v->gen.bigpos >> 32);
    v->layer_changed = TRUE;
}

void sampler_voice_link(struct sampler_voice **pv, struct sampler_voice *v)
{
    v->prev = NULL;
    v->next = *pv;
    if (*pv)
        (*pv)->prev = v;
    *pv = v;
}

void sampler_voice_unlink(struct sampler_voice **pv, struct sampler_voice *v)
{
    if (*pv == v)
        *pv = v->next;
    if (v->prev)
        v->prev->next = v->next;
    if (v->next)
        v->next->prev = v->prev;
    v->prev = NULL;
    v->next = NULL;
}

void sampler_voice_inactivate(struct sampler_voice *v, gboolean expect_active)
{
    assert((v->gen.mode != spt_inactive) == expect_active);
    sampler_voice_unlink(&v->channel->voices_running, v);
    v->gen.mode = spt_inactive;
    if (v->current_pipe)
    {
        cbox_prefetch_stack_push(v->program->module->pipe_stack, v->current_pipe);
        v->current_pipe = NULL;
    }
    v->channel = NULL;
    sampler_voice_link(&v->program->module->voices_free, v);
}

void sampler_voice_release(struct sampler_voice *v, gboolean is_polyaft)
{
    if ((v->loop_mode == slm_one_shot_chokeable) != is_polyaft)
        return;
    if (v->loop_mode != slm_one_shot && !v->layer->count)
    {
        v->released = 1;
        if (v->loop_mode == slm_loop_sustain && v->current_pipe)
        {
            // Break the loop
            v->current_pipe->file_loop_end = v->gen.cur_sample_end;
            v->current_pipe->file_loop_start = -1;
        }
    }
}

void sampler_voice_update_params_from_layer(struct sampler_voice *v)
{
    struct sampler_layer_data *l = v->layer;
    struct sampler_module *m = v->program->module;
    lfo_update_freq(&v->amp_lfo, &l->amp_lfo, m->module.srate, m->module.srate_inv);
    lfo_update_freq(&v->filter_lfo, &l->filter_lfo, m->module.srate, m->module.srate_inv);
    lfo_update_freq(&v->pitch_lfo, &l->pitch_lfo, m->module.srate, m->module.srate_inv);
    cbox_envelope_update_shape(&v->amp_env, &l->amp_env_shape);
    cbox_envelope_update_shape(&v->filter_env, &l->filter_env_shape);
    cbox_envelope_update_shape(&v->pitch_env, &l->pitch_env_shape);
}

static inline void lfo_update_xdelta(struct sampler_module *m, struct sampler_lfo *lfo, uint32_t modmask, uint32_t dest, const float *moddests)
{
    if (!(modmask & (1 << dest)))
        lfo->xdelta = 0;
    else
        lfo->xdelta = (uint32_t)(moddests[dest] * 65536.0 * 65536.0 * CBOX_BLOCK_SIZE * m->module.srate_inv);
}

static inline void sampler_filter_process_control(struct sampler_filter *f, enum sampler_filter_type fil_type, float logcutoff, float resonance_linearized, const struct cbox_sincos *sincos_base)
{
    f->second_filter = &f->filter_coeffs;

    if (logcutoff < 0)
        logcutoff = 0;
    if (logcutoff > 12798)
        logcutoff = 12798;
    //float resonance = v->resonance*pow(32.0,c->cc[71]/maxv);
    float resonance = resonance_linearized;
    if (resonance < 0.7f)
        resonance = 0.7f;
    if (resonance > 32.f)
        resonance = 32.f;
    const struct cbox_sincos *sincos = &sincos_base[(int)logcutoff];
    switch(fil_type)
    {
    case sft_lp24hybrid:
        cbox_biquadf_set_lp_rbj_lookup(&f->filter_coeffs, sincos, resonance * resonance);
        cbox_biquadf_set_1plp_lookup(&f->filter_coeffs_extra, sincos, 1);
        f->second_filter = &f->filter_coeffs_extra;
        break;
    case sft_lp12:
    case sft_lp24:
    case sft_lp36:
        cbox_biquadf_set_lp_rbj_lookup(&f->filter_coeffs, sincos, resonance);
        break;
    case sft_hp12:
    case sft_hp24:
        cbox_biquadf_set_hp_rbj_lookup(&f->filter_coeffs, sincos, resonance);
        break;
    case sft_bp6:
    case sft_bp12:
        cbox_biquadf_set_bp_rbj_lookup(&f->filter_coeffs, sincos, resonance);
        break;
    case sft_lp6:
    case sft_lp12nr:
    case sft_lp24nr:
        cbox_biquadf_set_1plp_lookup(&f->filter_coeffs, sincos, fil_type != sft_lp6);
        break;
    case sft_hp6:
    case sft_hp12nr:
    case sft_hp24nr:
        cbox_biquadf_set_1php_lookup(&f->filter_coeffs, sincos, fil_type != sft_hp6);
        break;
    default:
        assert(0);
    }
}

static inline void sampler_filter_process_audio(struct sampler_filter *f, int num_stages, float *leftright)
{
    for (int i = 0; i < num_stages; ++i)
        cbox_biquadf_process_stereo(&f->filter_left[i], &f->filter_right[i], i ? f->second_filter : &f->filter_coeffs, leftright);
}

static inline void do_channel_mixing(float *leftright, uint32_t numsamples, float position, float width)
{
    float crossmix = (100.0 - width) * 0.005f;
    float amtleft = position > 0 ? 1 - 0.01 * position : 1;
    float amtright = position < 0 ? 1 - 0.01 * -position : 1;
    if (amtleft < 0)
        amtleft = 0;
    if (amtright < 0)
        amtright = 0;
    for (uint32_t i = 0; i < 2 * numsamples; i += 2) {
        float left = leftright[i], right = leftright[i + 1];
        float newleft = left + crossmix * (right - left);
        float newright = right + crossmix * (left - right);
        leftright[i] = newleft * amtleft;
        leftright[i + 1] = newright * amtright;
    }
}

static inline uint32_t sampler_gen_sample_playback_with_pipe(struct sampler_gen *gen, float *leftright, struct cbox_prefetch_pipe *current_pipe)
{
    if (!current_pipe)
        return sampler_gen_sample_playback(gen, leftright, (uint32_t)-1);

    uint32_t limit = cbox_prefetch_pipe_get_remaining(current_pipe);
    if (limit <= 4)
    {
        gen->mode = spt_inactive;
        return 0;
    }
    uint32_t samples = sampler_gen_sample_playback(gen, leftright, limit - 4);
    cbox_prefetch_pipe_consumed(current_pipe, gen->consumed);
    gen->consumed = 0;
    return samples;
}

static const float gain_for_num_stages[] = { 1, 1, 0.5, 0.33f };

void sampler_voice_process(struct sampler_voice *v, struct sampler_module *m, cbox_sample_t **outputs)
{
    struct sampler_layer_data *l = v->layer;
    assert(v->gen.mode != spt_inactive);
    
    struct sampler_channel *c = v->channel;
    v->age += CBOX_BLOCK_SIZE;
    
    const float velscl = v->vel * (1.f / 127.f);

    struct cbox_envelope_shape *pitcheg_shape = v->pitch_env.shape, *fileg_shape = v->filter_env.shape, *ampeg_shape = v->amp_env.shape;
    if (__builtin_expect(l->computed.mod_bitmask, 0))
    {
        #define COPY_ORIG_SHAPE(envtype, envtype2, index) \
            if (l->computed.mod_bitmask & slmb_##envtype2##eg_cc) { \
                memcpy(&v->cc_envs[index], envtype2##eg_shape, sizeof(struct cbox_envelope_shape)); \
                envtype2##eg_shape = &v->cc_envs[index]; \
            }
        COPY_ORIG_SHAPE(amp, amp, 0)
        COPY_ORIG_SHAPE(filter, fil, 1)
        COPY_ORIG_SHAPE(pitch, pitch, 2)

        for(struct sampler_modulation *sm = l->modulations; sm; sm = sm->next)
        {
            // Simplified modulations for EG stages (CCs only)
            if (sm->key.dest >= smdest_eg_stage_start && sm->key.dest <= smdest_eg_stage_end)
            {
                float value = 0.f;
                if (sm->key.src < smsrc_pernote_offset)
                    value = sampler_channel_getcc_mod(c, v, sm->key.src, sm->value.curve_id, sm->value.step);
                uint32_t param = sm->key.dest - smdest_eg_stage_start;
                if (value * sm->value.amount != 0)
                    cbox_envelope_modify_dahdsr(&v->cc_envs[(param >> 4)], param & 0x0F, value * sm->value.amount, m->module.srate * 1.0 / CBOX_BLOCK_SIZE);
            }
        }
        #define UPDATE_ENV_POSITION(envtype, envtype2) \
            if (l->computed.mod_bitmask & slmb_##envtype##eg_cc) \
                cbox_envelope_update_shape_after_modify(&v->envtype2##_env, envtype##eg_shape, m->module.srate * 1.0 / CBOX_BLOCK_SIZE);
        UPDATE_ENV_POSITION(amp, amp)
        UPDATE_ENV_POSITION(fil, filter)
        UPDATE_ENV_POSITION(pitch, pitch)
    }

    // if it's a DAHD envelope without sustain, consider the note finished
    if (__builtin_expect(v->amp_env.cur_stage == 4 && ampeg_shape->stages[3].end_value <= 0.f, 0))
        cbox_envelope_go_to(&v->amp_env, 15);

    #define RECALC_EQ_MASK_EQ1 (7 << smdest_eq1_freq)
    #define RECALC_EQ_MASK_EQ2 (7 << smdest_eq2_freq)
    #define RECALC_EQ_MASK_EQ3 (7 << smdest_eq3_freq)
    #define RECALC_EQ_MASK_ALL (RECALC_EQ_MASK_EQ1 | RECALC_EQ_MASK_EQ2 | RECALC_EQ_MASK_EQ3)
    uint32_t recalc_eq_mask = 0;

    if (__builtin_expect(v->layer_changed, 0))
    {
        v->last_level = -1;
        if (v->last_waveform != v->layer->computed.eff_waveform)
        {
            v->last_waveform = v->layer->computed.eff_waveform;
            if (v->layer->computed.eff_waveform)
            {
                v->gen.mode = v->layer->computed.eff_waveform->info.channels == 2 ? spt_stereo16 : spt_mono16;
                v->gen.cur_sample_end = v->layer->computed.eff_waveform->info.frames;
            }
            else
            {
                sampler_voice_inactivate(v, TRUE);
                return;
            }
        }
        if (l->computed.eq_bitmask & (1 << 0)) recalc_eq_mask |= RECALC_EQ_MASK_EQ1;
        if (l->computed.eq_bitmask & (1 << 1)) recalc_eq_mask |= RECALC_EQ_MASK_EQ2;
        if (l->computed.eq_bitmask & (1 << 2)) recalc_eq_mask |= RECALC_EQ_MASK_EQ3;
        v->last_eq_bitmask = l->computed.eq_bitmask;
        v->layer_changed = FALSE;
    }

    float pitch = (v->note - l->pitch_keycenter) * l->pitch_keytrack + l->tune + l->transpose * 100 + v->pitch_shift;
    float modsrcs[smsrc_pernote_count];
    modsrcs[smsrc_vel - smsrc_pernote_offset] = v->vel * velscl;
    modsrcs[smsrc_pitch - smsrc_pernote_offset] = pitch * (1.f / 100.f);
    modsrcs[smsrc_chanaft - smsrc_pernote_offset] = c->last_chanaft * (1.f / 127.f);
    modsrcs[smsrc_polyaft - smsrc_pernote_offset] = sampler_channel_get_poly_pressure(c, v->note);
    modsrcs[smsrc_pitchenv - smsrc_pernote_offset] = cbox_envelope_get_value(&v->pitch_env, pitcheg_shape) * 0.01f;
    modsrcs[smsrc_filenv - smsrc_pernote_offset] = l->computed.eff_use_filter_mods ? cbox_envelope_get_value(&v->filter_env, fileg_shape) * 0.01f : 0;
    modsrcs[smsrc_ampenv - smsrc_pernote_offset] = cbox_envelope_get_value(&v->amp_env, ampeg_shape) * 0.01f;

    modsrcs[smsrc_amplfo - smsrc_pernote_offset] = lfo_run(&v->amp_lfo);
    modsrcs[smsrc_fillfo - smsrc_pernote_offset] = l->computed.eff_use_filter_mods ? lfo_run(&v->filter_lfo) : 0;
    modsrcs[smsrc_pitchlfo - smsrc_pernote_offset] = lfo_run(&v->pitch_lfo);

    float moddests[smdestcount];
    moddests[smdest_pitch] = pitch;
    moddests[smdest_cutoff] = v->cutoff_shift;
    moddests[smdest_cutoff2] = v->cutoff2_shift;
    // These are always set
    uint32_t modmask = (1 << smdest_pitch) | (1 << smdest_cutoff) | (1 << smdest_cutoff2);
#if 0
    // Those are lazy-initialized using modmask.
    moddests[smdest_gain] = 0;
    moddests[smdest_resonance] = 0;
    moddests[smdest_tonectl] = 0;
    moddests[smdest_pitchlfo_freq] = 0;
    moddests[smdest_fillfo_freq] = 0;
    moddests[smdest_amplfo_freq] = 0;
#endif
    if (__builtin_expect(l->trigger == stm_release, 0))
    {
        moddests[smdest_gain] = -v->age * l->rt_decay * m->module.srate_inv;
        modmask |= (1 << smdest_gain);
    }
    
    if (c->pitchwheel)
    {
        int pw = c->pitchwheel * (c->pitchwheel > 0 ? l->bend_up : -l->bend_down);
        // approximate dividing by 8191
        if (pw < 0)
            pw >>= 13;
        else
            pw = (pw + 4096) >> 13;
        if (l->bend_step > 1)
            pw = (pw / l->bend_step) * l->bend_step;
        moddests[smdest_pitch] += pw;
    }
    
    for (struct sampler_modulation *sm = l->modulations; sm; sm = sm->next)
    {
        enum sampler_modsrc src = sm->key.src;
        enum sampler_modsrc src2 = sm->key.src2;
        enum sampler_moddest dest = sm->key.dest;
        float value = 0.f, value2 = 1.f;
        if (src < smsrc_pernote_offset)
            value = sampler_channel_getcc_mod(c, v, src, sm->value.curve_id, sm->value.step);
        else
            value = modsrcs[src - smsrc_pernote_offset];

        if (src2 != smsrc_none)
        {
            if (src2 < smsrc_pernote_offset)
                value2 = sampler_channel_getcc_mod(c, v, src2, sm->value.curve_id, sm->value.step);
            else
                value2 = modsrcs[src2 - smsrc_pernote_offset];
            
            value *= value2;
        }
        if (dest < 32)
        {
            if (dest == smdest_amplitude)
            {
                if (!(modmask & (1 << dest))) // first value
                {
                    moddests[dest] = value * sm->value.amount;
                    modmask |= (1 << dest);
                }
                else
                    moddests[dest] *= value * sm->value.amount;
            }
            else if (!(modmask & (1 << dest))) // first value
            {
                moddests[dest] = value * sm->value.amount;
                modmask |= (1 << dest);
            }
            else
                moddests[dest] += value * sm->value.amount;
        }
    }
    lfo_update_xdelta(m, &v->pitch_lfo, modmask, smdest_pitchlfo_freq, moddests);
    if (l->computed.eff_use_filter_mods)
        lfo_update_xdelta(m, &v->filter_lfo, modmask, smdest_fillfo_freq, moddests);
    lfo_update_xdelta(m, &v->amp_lfo, modmask, smdest_amplfo_freq, moddests);
    recalc_eq_mask |= modmask;

    #define RECALC_EQ_IF(index) \
        if (recalc_eq_mask & RECALC_EQ_MASK_EQ##index) \
        { \
            float dfreq = velscl * l->eq##index.vel2freq + ((modmask & (1 << smdest_eq##index##_freq)) ? moddests[smdest_eq##index##_freq] : 0);\
            float fbw = (modmask & (1 << smdest_eq##index##_bw)) ? pow(0.5, moddests[smdest_eq##index##_bw]) : 1;\
            float dgain = velscl * l->eq##index.vel2gain + ((modmask & (1 << smdest_eq##index##_gain)) ? moddests[smdest_eq##index##_gain] : 0);\
            cbox_biquadf_set_peakeq_rbj_scaled(&v->eq_coeffs[index - 1], l->eq##index.effective_freq + dfreq, fbw / l->eq##index.bw, dB2gain(0.5 * (l->eq##index.gain + dgain)), m->module.srate); \
            if (!(v->last_eq_bitmask & (1 << (index - 1)))) \
            { \
                cbox_biquadf_reset(&v->eq_left[index-1]); \
                cbox_biquadf_reset(&v->eq_right[index-1]); \
            } \
        }
    if (__builtin_expect(recalc_eq_mask, 0))
    {
        RECALC_EQ_IF(1)
        RECALC_EQ_IF(2)
        RECALC_EQ_IF(3)
    }
    cbox_envelope_advance(&v->pitch_env, v->released, pitcheg_shape);
    if (l->computed.eff_use_filter_mods)
        cbox_envelope_advance(&v->filter_env, v->released, fileg_shape);
    cbox_envelope_advance(&v->amp_env, v->released, ampeg_shape);
    if (__builtin_expect(v->amp_env.cur_stage < 0, 0))
    {
        if (__builtin_expect(is_tail_finished(v), 0))
        {
            sampler_voice_inactivate(v, TRUE);
            return;
        }
    }
    
    double maxv = 127 << 7;
    double freq = l->computed.eff_freq * cent2factor(moddests[smdest_pitch]) ;
    uint64_t freq64 = (uint64_t)(freq * 65536.0 * 65536.0 * m->module.srate_inv);

    gboolean playing_sustain_loop = !v->released && v->loop_mode == slm_loop_sustain;
    uint32_t loop_start, loop_end;
    gboolean bandlimited = FALSE;

    if (!v->current_pipe)
    {
        v->gen.sample_data = v->last_waveform->data;
        if (v->last_waveform->levels)
        {
            gboolean use_cached = v->last_level > 0 && v->last_level < v->last_waveform->level_count
                && freq64 > v->last_level_min_rate && freq64 <= v->last_waveform->levels[v->last_level].max_rate;
            if (__builtin_expect(use_cached, 1))
            {
                v->gen.sample_data = v->last_waveform->levels[v->last_level].data;
                bandlimited = TRUE;
            }
            else
            {
                for (int i = 0; i < v->last_waveform->level_count; i++)
                {
                    if (freq64 <= v->last_waveform->levels[i].max_rate)
                    {
                        v->last_level = i;
                        v->gen.sample_data = v->last_waveform->levels[i].data;
                        bandlimited = TRUE;
                        
                        break;
                    }
                    v->last_level_min_rate = v->last_waveform->levels[i].max_rate;
                }
            }
        }
    }
    
    // XXXKF or maybe check for on-cc being in the on-cc range instead?
    gboolean play_loop = v->layer->computed.eff_loop_end && (v->loop_mode == slm_loop_continuous || playing_sustain_loop) && !v->layer->on_cc;
    loop_start = play_loop ? v->layer->computed.eff_loop_start : (v->layer->count ? 0 : (uint32_t)-1);
    loop_end = play_loop ? v->layer->computed.eff_loop_end : v->gen.cur_sample_end;

    if (v->current_pipe)
    {
        v->gen.sample_data = v->gen.loop_count ? v->current_pipe->data : v->last_waveform->data;
        v->gen.streaming_buffer = v->current_pipe->data;
        
        v->gen.prefetch_only_loop = (loop_end < v->last_waveform->preloaded_frames);
        v->gen.loop_overlap = 0;
        if (v->gen.prefetch_only_loop)
        {
            assert(!v->gen.in_streaming_buffer); // XXXKF this won't hold true when loops are edited while sound is being played (but that's not supported yet anyway)
            v->gen.loop_start = loop_start;
            v->gen.loop_end = loop_end;
            v->gen.streaming_buffer_frames = 0;
        }
        else
        {
            v->gen.loop_start = 0;
            v->gen.loop_end = v->last_waveform->preloaded_frames;
            v->gen.streaming_buffer_frames = v->current_pipe->buffer_loop_end;
        }
    }
    else
    {
        v->gen.loop_count = v->layer->count;
        v->gen.loop_start = loop_start;
        v->gen.loop_end = loop_end;
        
        if (!bandlimited)
        {
            // Use pre-calculated join
            v->gen.scratch = loop_start == (uint32_t)-1 ? v->layer->computed.scratch_end : v->layer->computed.scratch_loop;
        }
        else
        {
            // The standard waveforms have extra MAX_INTERPOLATION_ORDER of samples from the loop start added past loop_end,
            // to avoid wasting time generating the joins in all the practical cases. The slow path covers custom loops
            // (i.e. partial loop or no loop) over bandlimited versions of the standard waveforms, and those are probably
            // not very useful anyway, as changing the loop removes the guarantee of the waveform being bandlimited and
            // may cause looping artifacts or introduce DC offset (e.g. if only a positive part of a sine wave is looped).
            if (loop_start == 0 && loop_end == l->computed.eff_waveform->info.frames)
                v->gen.scratch = v->gen.sample_data + l->computed.eff_waveform->info.frames - MAX_INTERPOLATION_ORDER;
            else
            {
                // Generate the join for the current wave level
                // XXXKF this could be optimised further, by checking if waveform and loops are the same as the last
                // time. However, this code is not likely to be used... ever, so optimising it is not the priority.
                int shift = l->computed.eff_waveform->info.channels == 2 ? 1 : 0;
                uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift;
                
                v->gen.scratch = v->gen.scratch_bandlimited;
                memcpy(&v->gen.scratch_bandlimited[0], &v->gen.sample_data[(loop_end - MAX_INTERPOLATION_ORDER) << shift], halfscratch * sizeof(int16_t) );
                if (loop_start != (uint32_t)-1)
                    memcpy(v->gen.scratch_bandlimited + halfscratch, &v->gen.sample_data[loop_start << shift], halfscratch * sizeof(int16_t));
                else
                    memset(v->gen.scratch_bandlimited + halfscratch, 0, halfscratch * sizeof(int16_t));
            }
        }
    }
        
    if (l->timestretch)
    {
        v->gen.bigdelta = freq64;
        v->gen.virtdelta = (uint64_t)(l->computed.eff_freq * 65536.0 * 65536.0 * m->module.srate_inv);
        v->gen.stretching_jump = l->timestretch_jump;
        v->gen.stretching_crossfade = l->timestretch_crossfade;
    }
    else
    {
        v->gen.bigdelta = freq64;
        v->gen.virtdelta = freq64;
    }
    float gain = modsrcs[smsrc_ampenv - smsrc_pernote_offset] * l->volume_linearized * v->gain_fromvel * c->channel_volume_cc * sampler_channel_addcc(c, 11) / (maxv * maxv);
    if (l->computed.eff_use_xfcc) {
        for(struct sampler_cc_range *p = l->xfin_cc; p; p = p->next)
            gain *= sfz_crossfade2(c->intcc[p->key.cc_number], p->value.locc, p->value.hicc, 0, 1, l->xf_cccurve);
        for(struct sampler_cc_range *p = l->xfout_cc; p; p = p->next)
            gain *= sfz_crossfade2(c->intcc[p->key.cc_number], p->value.locc, p->value.hicc, 1, 0, l->xf_cccurve);
    }
    if ((modmask & (1 << smdest_gain)) && moddests[smdest_gain] != 0.f)
        gain *= dB2gain(moddests[smdest_gain]);

    float amplitude = l->amplitude;
    if ((modmask & (1 << smdest_amplitude)))
        amplitude *= moddests[smdest_amplitude];

    gain *= amplitude * (1.0 / 100.0);
    // http://drealm.info/sfz/plj-sfz.xhtml#amp "The overall gain must remain in the range -144 to 6 decibels."
    if (gain > 2.f)
        gain = 2.f;
    float pan = (l->pan + ((modmask & (1 << smdest_pan) ? moddests[smdest_pan] : 0)) + 100.f) * (1.f / 200.f) + (c->channel_pan_cc * 1.f / maxv - 0.5f) * 2.f;
    if (pan < 0.f)
        pan = 0.f;
    if (pan > 1.f)
        pan = 1.f;
    v->gen.lgain = gain * (1.f - pan)  / 32768.f;
    v->gen.rgain = gain * pan / 32768.f;

    if (l->cutoff != -1)
    {
        float mod_resonance = (modmask & (1 << smdest_resonance)) ? dB2gain(gain_for_num_stages[l->computed.eff_num_stages] * moddests[smdest_resonance]) : 1;
        sampler_filter_process_control(&v->filter, l->fil_type, l->computed.logcutoff + moddests[smdest_cutoff], l->computed.resonance_scaled * mod_resonance, m->sincos);
    }
    if (l->cutoff2 != -1)
    {
        float mod_resonance = (modmask & (1 << smdest_resonance2)) ? dB2gain(gain_for_num_stages[l->computed.eff_num_stages2] * moddests[smdest_resonance2]) : 1;
        sampler_filter_process_control(&v->filter2, l->fil2_type, l->computed.logcutoff2 + moddests[smdest_cutoff2], l->computed.resonance2_scaled * mod_resonance, m->sincos);
    }

    if (__builtin_expect(l->tonectl_freq != 0, 0))
    {
        float ctl = l->tonectl + (modmask & (1 << smdest_tonectl) ? moddests[smdest_tonectl] : 0);
        if (fabs(ctl) > 0.0001f)
            cbox_onepolef_set_highshelf_setgain(&v->onepole_coeffs, dB2gain(ctl));
        else
            cbox_onepolef_set_highshelf_setgain(&v->onepole_coeffs, 1.0);
    }

    // Audio processing starts here
    float leftright[2 * CBOX_BLOCK_SIZE];
        
    uint32_t samples = sampler_gen_sample_playback_with_pipe(&v->gen, leftright, v->current_pipe);
    if (l->computed.eff_use_channel_mixer)
        do_channel_mixing(leftright, samples, l->position, l->width);
    for (int i = 2 * samples; i < 2 * CBOX_BLOCK_SIZE; i++)
        leftright[i] = 0.f;

    if (l->cutoff != -1)
        sampler_filter_process_audio(&v->filter, l->computed.eff_num_stages, leftright);
    if (l->cutoff2 != -1)
        sampler_filter_process_audio(&v->filter2, l->computed.eff_num_stages2, leftright);

    if (__builtin_expect(l->tonectl_freq != 0, 0))
        cbox_onepolef_process_stereo(&v->onepole_left, &v->onepole_right, &v->onepole_coeffs, leftright);

    if (__builtin_expect(l->computed.eq_bitmask, 0))
    {
        for (int eq = 0; eq < 3; eq++)
        {
            if (l->computed.eq_bitmask & (1 << eq))
            { 
                cbox_biquadf_process_stereo(&v->eq_left[eq], &v->eq_right[eq], &v->eq_coeffs[eq], leftright);
            }
        }
    }
        
    mix_block_into(outputs, v->output_pair_no * 2, leftright);
    if (__builtin_expect((v->send1bus > 0 && v->send1gain != 0) || (v->send2bus > 0 && v->send2gain != 0), 0))
    {
        if (v->send1bus > 0 && v->send1gain != 0)
        {
            int oofs = m->module.aux_offset + (v->send1bus - 1) * 2;
            mix_block_into_with_gain(outputs, oofs, leftright, v->send1gain);
        }
        if (v->send2bus > 0 && v->send2gain != 0)
        {
            int oofs = m->module.aux_offset + (v->send2bus - 1) * 2;
            mix_block_into_with_gain(outputs, oofs, leftright, v->send2gain);
        }
    }
    if (v->gen.mode == spt_inactive)
        sampler_voice_inactivate(v, FALSE);
}

