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.
510 lines
20 KiB
510 lines
20 KiB
3 years ago
|
/*
|
||
|
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-api.h"
|
||
|
#include "dspmath.h"
|
||
|
#include "errors.h"
|
||
|
#include "midi.h"
|
||
|
#include "module.h"
|
||
|
#include "rt.h"
|
||
|
#include "sampler.h"
|
||
|
#include "sfzloader.h"
|
||
|
#include <assert.h>
|
||
|
#include <errno.h>
|
||
|
#include <math.h>
|
||
|
#include <memory.h>
|
||
|
#include <stdio.h>
|
||
|
|
||
|
#define LOW_QUALITY_INTERPOLATION 0
|
||
|
|
||
|
struct resampler_state
|
||
|
{
|
||
|
float *leftright;
|
||
|
int offset;
|
||
|
float lgain, rgain, lgain_delta, rgain_delta;
|
||
|
};
|
||
|
|
||
|
#if USE_NEON
|
||
|
|
||
|
#include <arm_neon.h>
|
||
|
|
||
|
static inline void process_voice_mono_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos)
|
||
|
{
|
||
|
static const float32x2_t shift1a = {0.f, 1.f}, shift1b = {1.f, 1.f};
|
||
|
static const float32x2_t shift2a = {-1.f, -1.f}, shift2b = {0.f, 0.f};
|
||
|
static const float32x2_t shift3a = {-2.f, -2.f}, shift3b = {-2.f, -1.f};
|
||
|
static const float32x2_t scalinga = {-1 / 6.0, 3 / 6.0}, scalingb = {-3 / 6.0, 1 / 6.0};
|
||
|
uint64x1_t pos = v->bigpos, delta = v->bigdelta;
|
||
|
float32x2_t gains = {rs->lgain, rs->rgain};
|
||
|
const float32x2_t gaindeltas = {rs->lgain_delta, rs->rgain_delta};
|
||
|
for (uint32_t i = rs->offset; i < endpos; i++)
|
||
|
{
|
||
|
float32x2_t posposf = vcvt_n_f32_u32(vreinterpret_u32_u64(pos), 32);
|
||
|
|
||
|
int32x4_t smp = vmovl_s16(vld1_s16(&srcdata[pos >> 32]));
|
||
|
pos = vadd_u64(pos, delta);
|
||
|
|
||
|
float32x2_t t2 = vdup_n_f32(posposf[0]);
|
||
|
float32x2_t samplesa = vcvt_f32_s32(vget_low_s32(smp)), samplesb = vcvt_f32_s32(vget_high_s32(smp));
|
||
|
|
||
|
float32x2_t mula = vmul_f32(vmul_f32(vadd_f32(t2, shift1a), vadd_f32(t2, shift2a)), vmul_f32(vadd_f32(t2, shift3a), scalinga));
|
||
|
float32x2_t mulb = vmul_f32(vmul_f32(vadd_f32(t2, shift1b), vadd_f32(t2, shift2b)), vmul_f32(vadd_f32(t2, shift3b), scalingb));
|
||
|
float32x2_t v = vmla_f32(vmul_f32(samplesa, mula), samplesb, mulb);
|
||
|
float32x2_t result = vmul_f32(gains, vadd_f32(v, vrev64_f32(v)));
|
||
|
gains = vadd_f32(gains, gaindeltas);
|
||
|
|
||
|
rs->leftright[2 * i] = result[0];
|
||
|
rs->leftright[2 * i + 1] = result[1];
|
||
|
}
|
||
|
rs->lgain = gains[0];
|
||
|
rs->rgain = gains[1];
|
||
|
v->bigpos = pos;
|
||
|
rs->offset = endpos;
|
||
|
}
|
||
|
|
||
|
static inline void process_voice_stereo_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos)
|
||
|
{
|
||
|
static const float32x2_t shift1a = {0.f, 1.f}, shift1b = {1.f, 1.f};
|
||
|
static const float32x2_t shift2a = {-1.f, -1.f}, shift2b = {0.f, 0.f};
|
||
|
static const float32x2_t shift3a = {-2.f, -2.f}, shift3b = {-2.f, -1.f};
|
||
|
static const float32x2_t scalinga = {-1 / 6.0, 3 / 6.0}, scalingb = {-3 / 6.0, 1 / 6.0};
|
||
|
uint64x1_t pos = v->bigpos, delta = v->bigdelta;
|
||
|
float32x2_t gains = {rs->lgain, rs->rgain};
|
||
|
const float32x2_t gaindeltas = {rs->lgain_delta, rs->rgain_delta};
|
||
|
for (uint32_t i = rs->offset; i < endpos; i++)
|
||
|
{
|
||
|
float32x2_t posposf = vcvt_n_f32_u32(vreinterpret_u32_u64(pos), 32);
|
||
|
|
||
|
int16x4x2_t pp = vld2_s16(&srcdata[(pos >> 31) &~ 1]);
|
||
|
pos = vadd_u64(pos, delta);
|
||
|
int32x4_t smp_left = vmovl_s16(pp.val[0]), smp_right = vmovl_s16(pp.val[1]);
|
||
|
|
||
|
float32x2_t t2 = vdup_n_f32(posposf[0]);
|
||
|
float32x2_t samplesLa = vcvt_f32_s32(vget_low_s32(smp_left)), samplesLb = vcvt_f32_s32(vget_high_s32(smp_left));
|
||
|
float32x2_t samplesRa = vcvt_f32_s32(vget_low_s32(smp_right)), samplesRb = vcvt_f32_s32(vget_high_s32(smp_right));
|
||
|
|
||
|
float32x2_t mula = vmul_f32(vmul_f32(vadd_f32(t2, shift1a), vadd_f32(t2, shift2a)), vmul_f32(vadd_f32(t2, shift3a), scalinga));
|
||
|
float32x2_t mulb = vmul_f32(vmul_f32(vadd_f32(t2, shift1b), vadd_f32(t2, shift2b)), vmul_f32(vadd_f32(t2, shift3b), scalingb));
|
||
|
float32x2_t vL = vmla_f32(vmul_f32(samplesLa, mula), samplesLb, mulb);
|
||
|
float32x2_t vR = vmla_f32(vmul_f32(samplesRa, mula), samplesRb, mulb);
|
||
|
float32x2x2_t transposed = vtrn_f32(vL, vR);
|
||
|
float32x2_t result = vmul_f32(gains, vadd_f32(transposed.val[0], transposed.val[1]));
|
||
|
gains = vadd_f32(gains, gaindeltas);
|
||
|
|
||
|
rs->leftright[2 * i] = result[0];
|
||
|
rs->leftright[2 * i + 1] = result[1];
|
||
|
}
|
||
|
rs->lgain = gains[0];
|
||
|
rs->rgain = gains[1];
|
||
|
v->bigpos = pos;
|
||
|
rs->offset = endpos;
|
||
|
}
|
||
|
|
||
|
#elif USE_SSE
|
||
|
|
||
|
#include <xmmintrin.h>
|
||
|
|
||
|
typedef __m128 V4SF;
|
||
|
|
||
|
static const V4SF shift1 = {0, 1, 1, 1};
|
||
|
static const V4SF shift2 = {-1, -1, 0, 0};
|
||
|
static const V4SF shift3 = {-2, -2, -2, -1};
|
||
|
static const V4SF scaling = {-1, 3, -3, 1};
|
||
|
static const V4SF zero = {0, 0, 0, 0};
|
||
|
|
||
|
|
||
|
static inline void process_voice_mono_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos)
|
||
|
{
|
||
|
uint64_t pos = v->bigpos;
|
||
|
const float ffrac = 1.0f / 6.0f;
|
||
|
const float _scaler = 1.f / (128.f * 16777216.f);
|
||
|
for (int i = rs->offset; i < endpos; i++)
|
||
|
{
|
||
|
//float t = ((pos >> 8) & 0x00FFFFFF) * scaler;
|
||
|
const int16_t *p = &srcdata[pos >> 32];
|
||
|
|
||
|
V4SF t2 = __builtin_ia32_cvtsi2ss(zero, (pos & 0xFFFFFFFF) >> 1) * _scaler;
|
||
|
pos += v->bigdelta;
|
||
|
|
||
|
V4SF t4 = __builtin_ia32_shufps(t2, t2, 0);
|
||
|
V4SF v4mul = (t4 + shift1) * (t4 + shift2) * (t4 + shift3) * scaling;
|
||
|
V4SF samples = {p[0], p[1], p[2], p[3]};
|
||
|
v4mul = __builtin_ia32_mulps(samples, v4mul);
|
||
|
|
||
|
float c = (v4mul[0] + v4mul[1] + v4mul[2] + v4mul[3]) * ffrac;
|
||
|
|
||
|
rs->leftright[2 * i] = rs->lgain * c;
|
||
|
rs->leftright[2 * i + 1] = rs->rgain * c;
|
||
|
rs->lgain += rs->lgain_delta;
|
||
|
rs->rgain += rs->rgain_delta;
|
||
|
}
|
||
|
v->bigpos = pos;
|
||
|
rs->offset = endpos;
|
||
|
}
|
||
|
|
||
|
static inline void process_voice_stereo_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos)
|
||
|
{
|
||
|
uint64_t pos = v->bigpos;
|
||
|
const float ffrac = 1.0f / 6.0f;
|
||
|
const float _scaler = 1.f / (128.f * 16777216.f);
|
||
|
for (int i = rs->offset; i < endpos; i++)
|
||
|
{
|
||
|
//float t = ((pos >> 8) & 0x00FFFFFF) * scaler;
|
||
|
const int16_t *p = &srcdata[(pos >> 31) & ~1];
|
||
|
|
||
|
V4SF t2 = __builtin_ia32_cvtsi2ss(zero, (pos & 0xFFFFFFFF) >> 1) * _scaler;
|
||
|
pos += v->bigdelta;
|
||
|
|
||
|
V4SF t4 = __builtin_ia32_shufps(t2, t2, 0);
|
||
|
V4SF v4mul = (t4 + shift1) * (t4 + shift2) * (t4 + shift3) * scaling;
|
||
|
V4SF samples_left = {p[0], p[2], p[4], p[6]};
|
||
|
samples_left = __builtin_ia32_mulps(samples_left, v4mul);
|
||
|
V4SF samples_right = {p[1], p[3], p[5], p[7]};
|
||
|
samples_right = __builtin_ia32_mulps(samples_right, v4mul);
|
||
|
|
||
|
float cl = (samples_left[0] + samples_left[1] + samples_left[2] + samples_left[3]) * ffrac;
|
||
|
float cr = (samples_right[0] + samples_right[1] + samples_right[2] + samples_right[3]) * ffrac;
|
||
|
|
||
|
rs->leftright[2 * i] = rs->lgain * cl;
|
||
|
rs->leftright[2 * i + 1] = rs->rgain * cr;
|
||
|
rs->lgain += rs->lgain_delta;
|
||
|
rs->rgain += rs->rgain_delta;
|
||
|
}
|
||
|
v->bigpos = pos;
|
||
|
rs->offset = endpos;
|
||
|
}
|
||
|
|
||
|
#else
|
||
|
|
||
|
static inline void process_voice_mono_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos)
|
||
|
{
|
||
|
const float ffrac = 1.0f / 6.0f;
|
||
|
const float scaler = 1.f / 16777216.f;
|
||
|
|
||
|
for (int i = rs->offset; i < endpos; i++)
|
||
|
{
|
||
|
float t = ((v->bigpos >> 8) & 0x00FFFFFF) * scaler;
|
||
|
const int16_t *p = &srcdata[v->bigpos >> 32];
|
||
|
#if LOW_QUALITY_INTERPOLATION
|
||
|
float c = (1.f - t) * p[1] + t * p[2];
|
||
|
#else
|
||
|
float b0 = -t*(t-1.f)*(t-2.f);
|
||
|
float b1 = 3.f*(t+1.f)*(t-1.f)*(t-2.f);
|
||
|
float c = (b0 * p[0] + b1 * p[1] - 3.f*(t+1.f)*t*(t-2.f) * p[2] + (t+1.f)*t*(t-1.f) * p[3]) * ffrac;
|
||
|
#endif
|
||
|
rs->leftright[2 * i] = rs->lgain * c;
|
||
|
rs->leftright[2 * i + 1] = rs->rgain * c;
|
||
|
rs->lgain += rs->lgain_delta;
|
||
|
rs->rgain += rs->rgain_delta;
|
||
|
v->bigpos += v->bigdelta;
|
||
|
}
|
||
|
rs->offset = endpos;
|
||
|
}
|
||
|
|
||
|
static inline void process_voice_stereo_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, int endpos)
|
||
|
{
|
||
|
const float ffrac = 1.0f / 6.0f;
|
||
|
const float scaler = 1.f / 16777216.f;
|
||
|
|
||
|
for (int i = rs->offset; i < endpos; i++)
|
||
|
{
|
||
|
float t = ((v->bigpos >> 8) & 0x00FFFFFF) * scaler;
|
||
|
const int16_t *p = &srcdata[(v->bigpos >> 31) & ~1];
|
||
|
#if LOW_QUALITY_INTERPOLATION
|
||
|
float c0 = (1.f - t) * p[2] + t * p[4];
|
||
|
float c1 = (1.f - t) * p[3] + t * p[5];
|
||
|
#else
|
||
|
float b0 = -t*(t-1.f)*(t-2.f);
|
||
|
float b1 = 3.f*(t+1.f)*(t-1.f)*(t-2.f);
|
||
|
float c0 = (b0 * p[0] + b1 * p[2] - 3.f*(t+1.f)*t*(t-2.f) * p[4] + (t+1.f)*t*(t-1.f) * p[6]) * ffrac;
|
||
|
float c1 = (b0 * p[1] + b1 * p[3] - 3.f*(t+1.f)*t*(t-2.f) * p[5] + (t+1.f)*t*(t-1.f) * p[7]) * ffrac;
|
||
|
#endif
|
||
|
rs->leftright[2 * i] = rs->lgain * c0;
|
||
|
rs->leftright[2 * i + 1] = rs->rgain * c1;
|
||
|
rs->lgain += rs->lgain_delta;
|
||
|
rs->rgain += rs->rgain_delta;
|
||
|
v->bigpos += v->bigdelta;
|
||
|
}
|
||
|
rs->offset = endpos;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
static inline uint32_t process_voice_noloop(struct sampler_gen *v, struct resampler_state *rs, const int16_t *srcdata, uint32_t pos_offset, uint32_t usable_sample_end)
|
||
|
{
|
||
|
uint32_t out_frames = CBOX_BLOCK_SIZE - rs->offset;
|
||
|
|
||
|
uint64_t sample_end64 = ((uint64_t)usable_sample_end) << 32;
|
||
|
// Check how many frames can be written to output buffer without going
|
||
|
// past usable_sample_end.
|
||
|
if (__builtin_expect(v->bigpos + (out_frames - 1) * v->bigdelta >= sample_end64, 0))
|
||
|
out_frames = (sample_end64 - v->bigpos) / v->bigdelta + 1;
|
||
|
|
||
|
assert(out_frames > 0 && out_frames <= (uint32_t)(CBOX_BLOCK_SIZE - rs->offset));
|
||
|
uint32_t oldpos = v->bigpos >> 32;
|
||
|
if (v->mode == spt_stereo16)
|
||
|
process_voice_stereo_noloop(v, rs, srcdata - (pos_offset << 1), rs->offset + out_frames);
|
||
|
else
|
||
|
process_voice_mono_noloop(v, rs, srcdata - pos_offset, rs->offset + out_frames);
|
||
|
return (v->bigpos >> 32) - oldpos;
|
||
|
}
|
||
|
|
||
|
static void process_voice_withloop(struct sampler_gen *v, struct resampler_state *rs)
|
||
|
{
|
||
|
// This is the first frame where interpolation will cross the loop boundary
|
||
|
uint32_t loop_end = v->loop_end;
|
||
|
uint32_t loop_edge = loop_end - MAX_INTERPOLATION_ORDER;
|
||
|
|
||
|
while ( rs->offset < CBOX_BLOCK_SIZE ) {
|
||
|
uint64_t startframe = v->bigpos >> 32;
|
||
|
|
||
|
int16_t *source_data = v->sample_data;
|
||
|
uint32_t source_offset = 0;
|
||
|
uint32_t usable_sample_end = loop_edge;
|
||
|
// if the first frame to play is already within 3 frames of loop end
|
||
|
// (we need consecutive 4 frames for cubic interpolation) then
|
||
|
// "straighten out" the area around the loop, and play that
|
||
|
if (__builtin_expect(startframe >= loop_edge, 0))
|
||
|
{
|
||
|
// if fully past the loop end, then it's normal wraparound
|
||
|
// (or end of the sample if not looping)
|
||
|
if (startframe >= loop_end)
|
||
|
{
|
||
|
if (v->loop_start == (uint32_t)-1)
|
||
|
{
|
||
|
v->mode = spt_inactive;
|
||
|
return;
|
||
|
}
|
||
|
v->play_count++;
|
||
|
if (v->loop_count && v->play_count >= v->loop_count)
|
||
|
{
|
||
|
v->mode = spt_inactive;
|
||
|
return;
|
||
|
}
|
||
|
v->bigpos -= (uint64_t)(loop_end - v->loop_start) << 32;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
usable_sample_end = loop_end;
|
||
|
source_data = v->scratch;
|
||
|
source_offset = loop_edge;
|
||
|
}
|
||
|
|
||
|
process_voice_noloop(v, rs, source_data, source_offset, usable_sample_end);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void process_voice_streaming(struct sampler_gen *v, struct resampler_state *rs, uint32_t limit)
|
||
|
{
|
||
|
if (v->consumed_credit > 0)
|
||
|
{
|
||
|
if (v->consumed_credit >= limit)
|
||
|
{
|
||
|
v->consumed_credit -= limit;
|
||
|
return;
|
||
|
}
|
||
|
limit -= v->consumed_credit;
|
||
|
v->consumed_credit = 0;
|
||
|
}
|
||
|
// This is the first frame where interpolation will cross the loop boundary
|
||
|
int16_t scratch[2 * MAX_INTERPOLATION_ORDER * 2];
|
||
|
|
||
|
while ( limit && rs->offset < CBOX_BLOCK_SIZE ) {
|
||
|
uint64_t startframe = v->bigpos >> 32;
|
||
|
|
||
|
int16_t *source_data = v->in_streaming_buffer ? v->streaming_buffer : v->sample_data;
|
||
|
uint32_t loop_start = v->in_streaming_buffer ? 0 : v->loop_start;
|
||
|
uint32_t loop_end = v->in_streaming_buffer ? v->streaming_buffer_frames : v->loop_end;
|
||
|
uint32_t loop_edge = loop_end - MAX_INTERPOLATION_ORDER;
|
||
|
uint32_t source_offset = 0;
|
||
|
uint32_t usable_sample_end = loop_edge;
|
||
|
// if the first frame to play is already within 3 frames of loop end
|
||
|
// (we need consecutive 4 frames for cubic interpolation) then
|
||
|
// "straighten out" the area around the loop, and play that
|
||
|
if (startframe >= loop_edge)
|
||
|
{
|
||
|
// if fully past the loop end, then it's normal wraparound
|
||
|
// (or end of the sample if not looping)
|
||
|
if (startframe >= loop_end)
|
||
|
{
|
||
|
if (v->loop_start == (uint32_t)-1)
|
||
|
{
|
||
|
v->mode = spt_inactive;
|
||
|
return;
|
||
|
}
|
||
|
v->bigpos -= (uint64_t)(loop_end - loop_start) << 32;
|
||
|
if (v->prefetch_only_loop)
|
||
|
v->consumed -= (loop_end - loop_start);
|
||
|
else
|
||
|
v->in_streaming_buffer = TRUE;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
int shift = (v->mode == spt_stereo16) ? 1 : 0;
|
||
|
|
||
|
// 'linearize' the virtual circular buffer - write 3 (or N) frames before end of the loop
|
||
|
// and 3 (N) frames at the start of the loop, and play it; in rare cases this will need to be
|
||
|
// repeated twice if output write pointer is close to CBOX_BLOCK_SIZE or playback rate is very low,
|
||
|
// but that's OK.
|
||
|
uint32_t halfscratch = MAX_INTERPOLATION_ORDER << shift;
|
||
|
memcpy(&scratch[0], &source_data[loop_edge << shift], halfscratch * sizeof(int16_t) );
|
||
|
if (v->loop_start == (uint32_t)-1)
|
||
|
memset(scratch + halfscratch, 0, halfscratch * sizeof(int16_t));
|
||
|
else
|
||
|
memcpy(scratch + halfscratch, &v->streaming_buffer[v->loop_start << shift], halfscratch * sizeof(int16_t));
|
||
|
|
||
|
usable_sample_end = loop_end;
|
||
|
source_data = scratch;
|
||
|
source_offset = loop_edge;
|
||
|
}
|
||
|
if (limit != (uint32_t)-1 && usable_sample_end - startframe > limit)
|
||
|
usable_sample_end = startframe + limit;
|
||
|
|
||
|
uint32_t consumed = process_voice_noloop(v, rs, source_data, source_offset, usable_sample_end);
|
||
|
if (consumed > limit)
|
||
|
{
|
||
|
// The number of frames 'consumed' may be greater than the amount
|
||
|
// available because of sample-skipping (at least that's the only
|
||
|
// *legitimate* reason). This should be accounted for in the,
|
||
|
// consumed sample counter (hence temporary storage of the
|
||
|
// 'buffer overconsumption' in the consumed_credit field), but is not
|
||
|
// actually causing any use of missing data, as the missing samples
|
||
|
// have been skipped.
|
||
|
assert(v->consumed_credit == 0);
|
||
|
v->consumed_credit = consumed - limit;
|
||
|
assert (v->consumed_credit <= 1 + (v->bigdelta >> 32));
|
||
|
consumed = limit;
|
||
|
}
|
||
|
v->consumed += consumed;
|
||
|
if (consumed < limit)
|
||
|
limit -= consumed;
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void sampler_gen_reset(struct sampler_gen *v)
|
||
|
{
|
||
|
v->mode = spt_inactive;
|
||
|
v->bigpos = 0;
|
||
|
v->last_lgain = 0.f;
|
||
|
v->last_rgain = 0.f;
|
||
|
v->play_count = 0;
|
||
|
v->consumed = 0;
|
||
|
v->consumed_credit = 0;
|
||
|
v->streaming_buffer = NULL;
|
||
|
v->in_streaming_buffer = FALSE;
|
||
|
v->prefetch_only_loop = FALSE;
|
||
|
v->fadein_counter = -1.f;
|
||
|
}
|
||
|
|
||
|
uint32_t sampler_gen_sample_playback(struct sampler_gen *v, float *leftright, uint32_t limit)
|
||
|
{
|
||
|
struct resampler_state rs;
|
||
|
rs.leftright = leftright;
|
||
|
rs.offset = 0;
|
||
|
rs.lgain = v->last_lgain;
|
||
|
rs.rgain = v->last_rgain;
|
||
|
rs.lgain_delta = (v->lgain - v->last_lgain) * (1.f / CBOX_BLOCK_SIZE);
|
||
|
rs.rgain_delta = (v->rgain - v->last_rgain) * (1.f / CBOX_BLOCK_SIZE);
|
||
|
if (v->streaming_buffer)
|
||
|
process_voice_streaming(v, &rs, limit);
|
||
|
else
|
||
|
{
|
||
|
process_voice_withloop(v, &rs);
|
||
|
}
|
||
|
uint32_t written = rs.offset;
|
||
|
|
||
|
if (!v->streaming_buffer)
|
||
|
{
|
||
|
v->virtpos += written * v->virtdelta;
|
||
|
if (v->virtpos != v->bigpos)
|
||
|
{
|
||
|
while ((v->virtpos >> 32) >= v->loop_end && v->loop_start != SAMPLER_NO_LOOP)
|
||
|
v->virtpos -= ((uint64_t)(v->loop_end - v->loop_start)) << 32;
|
||
|
}
|
||
|
// XXXKF looping
|
||
|
if (v->fadein_counter == -1 && fabs((v->bigpos - v->virtpos) / (65536.0 * 65536.0)) > v->stretching_jump)
|
||
|
{
|
||
|
int64_t jump = (int64_t)(v->stretching_jump * 65536.0 * 65536.0);
|
||
|
int64_t newpos = v->bigpos > v->virtpos ? v->bigpos - jump : v->bigpos + jump;
|
||
|
if (newpos < 0)
|
||
|
newpos = 0;
|
||
|
// XXXKF beware of extremely short loops
|
||
|
while ((newpos >> 32) >= v->loop_end && v->loop_start != SAMPLER_NO_LOOP)
|
||
|
newpos -= ((uint64_t)(v->loop_end - v->loop_start)) << 32;
|
||
|
if ((newpos >> 32) >= v->cur_sample_end - 4)
|
||
|
newpos = ((uint64_t)v->cur_sample_end - 4)<< 32;
|
||
|
v->fadein_pos = newpos;
|
||
|
v->fadein_counter = 0;
|
||
|
}
|
||
|
else if (v->fadein_counter != -1)
|
||
|
{
|
||
|
float leftright_fadein[2 * CBOX_BLOCK_SIZE];
|
||
|
|
||
|
rs.offset = 0;
|
||
|
rs.leftright = leftright_fadein;
|
||
|
rs.lgain = v->last_lgain;
|
||
|
rs.rgain = v->last_rgain;
|
||
|
|
||
|
uint64_t oldpos = v->bigpos;
|
||
|
v->bigpos = v->fadein_pos;
|
||
|
process_voice_withloop(v, &rs);
|
||
|
v->fadein_pos = v->bigpos;
|
||
|
v->bigpos = oldpos;
|
||
|
|
||
|
uint32_t written2 = rs.offset;
|
||
|
|
||
|
// XXXKF not the best set of special cases
|
||
|
uint32_t i;
|
||
|
if (written2 > written)
|
||
|
{
|
||
|
for (i = 2 * written; i < 2 * written2; i += 2)
|
||
|
leftright[i] = leftright[i + 1] = 0.f;
|
||
|
written = written2;
|
||
|
}
|
||
|
if (written2 < written)
|
||
|
{
|
||
|
for (i = 2 * written2; i < 2 * written; i += 2)
|
||
|
leftright_fadein[i] = leftright_fadein[i + 1] = 0.f;
|
||
|
written2 = written;
|
||
|
}
|
||
|
float cnt = v->fadein_counter;
|
||
|
float scl = v->bigdelta / (v->stretching_crossfade * v->virtdelta);
|
||
|
for (i = 0; i < 2 * written2; i += 2)
|
||
|
{
|
||
|
leftright[i] += (leftright_fadein[i] - leftright[i]) * cnt;
|
||
|
leftright[i + 1] += (leftright_fadein[i + 1] - leftright[i + 1]) * cnt;
|
||
|
cnt += scl;
|
||
|
if (cnt > 1.f)
|
||
|
cnt = 1.f;
|
||
|
}
|
||
|
if (cnt >= 1.f)
|
||
|
{
|
||
|
cnt = -1.f;
|
||
|
v->bigpos = v->fadein_pos;
|
||
|
}
|
||
|
v->fadein_counter = cnt;
|
||
|
}
|
||
|
}
|
||
|
v->last_lgain = v->lgain;
|
||
|
v->last_rgain = v->rgain;
|
||
|
return written;
|
||
|
}
|
||
|
|