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.
 
 

509 lines
20 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-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;
}