You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
995 lines
38 KiB
995 lines
38 KiB
/*
|
|
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);
|
|
}
|
|
|
|
|