mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2026-02-17 22:05:38 -05:00
resample: use fixed point for resample phase and input rate
If phase is float, calculations in impl_native_in_len/out_len can produce wrong results due to rounding error. It's probably better to not be in the business of predicting floating-point rounding, so replace this by fixed-point arithmetic. Also make sure `offset+1` cannot overflow data->filter array in do_resample_inter* due to float multiplication possibly rounding up.
This commit is contained in:
parent
3cade43cf3
commit
244d5a1cc1
3 changed files with 61 additions and 32 deletions
|
|
@ -12,6 +12,18 @@ typedef void (*resample_func_t)(struct resample *r,
|
|||
const void * SPA_RESTRICT src[], uint32_t ioffs, uint32_t *in_len,
|
||||
void * SPA_RESTRICT dst[], uint32_t ooffs, uint32_t *out_len);
|
||||
|
||||
#define FIXP_SHIFT 32
|
||||
#define FIXP_SCALE ((uint64_t)1 << FIXP_SHIFT)
|
||||
#define FIXP_MASK (FIXP_SCALE - 1)
|
||||
#define UINT32_TO_FIXP(v) ((struct fixp) { (uint64_t)((uint32_t)(v)) << FIXP_SHIFT })
|
||||
#define FLOAT_TO_FIXP(d) ((struct fixp) { (uint64_t)((d) * (float)FIXP_SCALE) })
|
||||
#define FIXP_TO_UINT32(f) ((f).value >> FIXP_SHIFT)
|
||||
#define FIXP_TO_FLOAT(f) ((f).value / (float)FIXP_SCALE)
|
||||
|
||||
struct fixp {
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
struct resample_info {
|
||||
uint32_t format;
|
||||
resample_func_t process_copy;
|
||||
|
|
@ -29,10 +41,10 @@ struct native_data {
|
|||
uint32_t n_phases;
|
||||
uint32_t in_rate;
|
||||
uint32_t out_rate;
|
||||
float phase;
|
||||
struct fixp phase;
|
||||
float pm;
|
||||
uint32_t inc;
|
||||
uint32_t frac;
|
||||
struct fixp frac;
|
||||
uint32_t filter_stride;
|
||||
uint32_t filter_stride_os;
|
||||
uint32_t gcd;
|
||||
|
|
@ -86,25 +98,26 @@ DEFINE_RESAMPLER(full,arch) \
|
|||
{ \
|
||||
struct native_data *data = r->data; \
|
||||
uint32_t n_taps = data->n_taps, stride = data->filter_stride_os; \
|
||||
uint32_t index, phase, out_rate = data->out_rate; \
|
||||
uint32_t index; \
|
||||
uint32_t c, o, olen = *out_len, ilen = *in_len; \
|
||||
uint32_t inc = data->inc, frac = data->frac, ch = r->channels; \
|
||||
uint32_t inc = data->inc, ch = r->channels; \
|
||||
uint64_t frac = data->frac.value, phase = data->phase.value; \
|
||||
uint64_t denom = UINT32_TO_FIXP(data->out_rate).value; \
|
||||
\
|
||||
index = ioffs; \
|
||||
phase = (uint32_t)data->phase; \
|
||||
for (o = ooffs; o < olen && index + n_taps <= ilen; o++) { \
|
||||
float *filter = &data->filter[phase * stride]; \
|
||||
float *filter = &data->filter[(phase >> FIXP_SHIFT) * stride]; \
|
||||
for (c = 0; c < ch; c++) { \
|
||||
const float *s = src[c]; \
|
||||
float *d = dst[c]; \
|
||||
inner_product_##arch(&d[o], &s[index], \
|
||||
filter, n_taps); \
|
||||
} \
|
||||
INC(index, phase, out_rate); \
|
||||
INC(index, phase, denom); \
|
||||
} \
|
||||
*in_len = index; \
|
||||
*out_len = o; \
|
||||
data->phase = phase; \
|
||||
data->phase.value = phase; \
|
||||
}
|
||||
|
||||
#define MAKE_RESAMPLER_INTER(arch) \
|
||||
|
|
@ -112,16 +125,18 @@ DEFINE_RESAMPLER(inter,arch) \
|
|||
{ \
|
||||
struct native_data *data = r->data; \
|
||||
uint32_t index, stride = data->filter_stride; \
|
||||
uint32_t n_taps = data->n_taps, out_rate = data->out_rate; \
|
||||
uint32_t n_taps = data->n_taps; \
|
||||
uint32_t c, o, olen = *out_len, ilen = *in_len; \
|
||||
uint32_t inc = data->inc, frac = data->frac, ch = r->channels; \
|
||||
float phase, pm = data->pm; \
|
||||
uint32_t inc = data->inc, ch = r->channels; \
|
||||
uint32_t ph_max = data->n_phases - 1; \
|
||||
uint64_t frac = data->frac.value, phase = data->phase.value; \
|
||||
uint64_t denom = UINT32_TO_FIXP(data->out_rate).value; \
|
||||
float pm = data->pm; \
|
||||
\
|
||||
index = ioffs; \
|
||||
phase = data->phase; \
|
||||
for (o = ooffs; o < olen && index + n_taps <= ilen; o++) { \
|
||||
float ph = phase * pm; \
|
||||
uint32_t offset = (uint32_t)floorf(ph); \
|
||||
uint32_t offset = SPA_MIN((uint32_t)floorf(ph), ph_max); \
|
||||
float *filter0 = &data->filter[(offset+0) * stride]; \
|
||||
float *filter1 = &data->filter[(offset+1) * stride]; \
|
||||
float pho = ph - offset; \
|
||||
|
|
@ -131,11 +146,11 @@ DEFINE_RESAMPLER(inter,arch) \
|
|||
inner_product_ip_##arch(&d[o], &s[index], \
|
||||
filter0, filter1, pho, n_taps); \
|
||||
} \
|
||||
INC(index, phase, out_rate); \
|
||||
INC(index, phase, denom); \
|
||||
} \
|
||||
*in_len = index; \
|
||||
*out_len = o; \
|
||||
data->phase = phase; \
|
||||
data->phase.value = phase; \
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue