audioconvert: move some things around

To make it easier to add other implementations later.
Improve selection of resampler function
This commit is contained in:
Wim Taymans 2020-04-03 17:46:04 +02:00
parent bf3ebb67aa
commit 0d1cef6b3a
12 changed files with 172 additions and 131 deletions

View file

@ -29,7 +29,10 @@
#include <errno.h>
#include <time.h>
#include "fmt-ops.c"
#include "fmt-ops.h"
typedef void (*convert_func_t) (struct convert *conv, void * SPA_RESTRICT dst[],
const void * SPA_RESTRICT src[], uint32_t n_samples);
struct stats {
uint32_t n_samples;

View file

@ -30,7 +30,6 @@
#include <time.h>
#include "resample.h"
#include "resample-native.h"
#define MAX_SAMPLES 4096
#define MAX_CHANNELS 11
@ -135,7 +134,7 @@ int main(int argc, char *argv[])
r.i_rate = in_rates[i];
r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
run_test("native", "c", &r);
resample_free(&r);
}
@ -147,7 +146,7 @@ int main(int argc, char *argv[])
r.i_rate = in_rates[i];
r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
run_test("native", "sse", &r);
resample_free(&r);
}
@ -160,7 +159,7 @@ int main(int argc, char *argv[])
r.i_rate = in_rates[i];
r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
run_test("native", "ssse3", &r);
resample_free(&r);
}
@ -173,7 +172,7 @@ int main(int argc, char *argv[])
r.i_rate = in_rates[i];
r.o_rate = out_rates[i];
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
run_test("native", "avx", &r);
resample_free(&r);
}

View file

@ -1,9 +1,7 @@
audioconvert_sources = ['audioadapter.c',
'audioconvert.c',
'fmtconvert.c',
'fmt-ops.c',
'channelmix.c',
'channelmix-ops.c',
'merger.c',
'plugin.c',
'resample.c',
@ -13,8 +11,11 @@ simd_cargs = []
simd_dependencies = []
audioconvert_c = static_library('audioconvert_c',
['resample-native-c.c',
['fmt-ops.c',
'channelmix-ops.c',
'channelmix-ops-c.c',
'resample-native.c',
'resample-peaks.c',
'fmt-ops-c.c' ],
c_args : ['-O3'],
include_directories : [spa_inc],
@ -25,6 +26,7 @@ simd_dependencies += audioconvert_c
if have_sse
audioconvert_sse = static_library('audioconvert_sse',
['resample-native-sse.c',
'resample-peaks-sse.c',
'channelmix-ops-sse.c' ],
c_args : [sse_args, '-O3', '-DHAVE_SSE'],
include_directories : [spa_inc],
@ -145,7 +147,7 @@ foreach a : benchmark_apps
dependencies : [dl_lib, pthread_lib, mathlib, ],
include_directories : [spa_inc ],
c_args : [ simd_cargs, '-D_GNU_SOURCE' ],
link_with : simd_dependencies,
link_with : [ simd_dependencies, audioconvertlib ],
install : false),
env : [
'SPA_PLUGIN_DIR=@0@/spa/plugins/'.format(meson.build_root()),

View file

@ -1,54 +0,0 @@
/* Spa
*
* Copyright © 2019 Wim Taymans
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "resample-native-impl.h"
static void inner_product_c(float *d, const float * SPA_RESTRICT s,
const float * SPA_RESTRICT taps, uint32_t n_taps)
{
float sum = 0.0f;
uint32_t i;
for (i = 0; i < n_taps; i++)
sum += s[i] * taps[i];
*d = sum;
}
static void inner_product_ip_c(float *d, const float * SPA_RESTRICT s,
const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x,
uint32_t n_taps)
{
float sum[2] = { 0.0f, 0.0f };
uint32_t i;
for (i = 0; i < n_taps; i++) {
sum[0] += s[i] * t0[i];
sum[1] += s[i] * t1[i];
}
*d = (sum[1] - sum[0]) * x + sum[0];
}
MAKE_RESAMPLER_COPY(c);
MAKE_RESAMPLER_FULL(c);
MAKE_RESAMPLER_INTER(c);

View file

@ -32,6 +32,14 @@ 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);
struct resample_info {
uint32_t format;
uint32_t cpu_flags;
resample_func_t process_copy;
resample_func_t process_full;
resample_func_t process_inter;
};
struct native_data {
double rate;
uint32_t n_taps;
@ -48,6 +56,7 @@ struct native_data {
resample_func_t func;
float *filter;
float *hist_mem;
const struct resample_info *info;
};
#define DEFINE_RESAMPLER(type,arch) \

View file

@ -22,6 +22,10 @@
* DEALINGS IN THE SOFTWARE.
*/
#include <errno.h>
#include <spa/param/audio/format.h>
#include "resample-native-impl.h"
struct quality {
@ -77,6 +81,69 @@ static int build_filter(float *taps, uint32_t stride, uint32_t n_taps, uint32_t
return 0;
}
static void inner_product_c(float *d, const float * SPA_RESTRICT s,
const float * SPA_RESTRICT taps, uint32_t n_taps)
{
float sum = 0.0f;
uint32_t i;
for (i = 0; i < n_taps; i++)
sum += s[i] * taps[i];
*d = sum;
}
static void inner_product_ip_c(float *d, const float * SPA_RESTRICT s,
const float * SPA_RESTRICT t0, const float * SPA_RESTRICT t1, float x,
uint32_t n_taps)
{
float sum[2] = { 0.0f, 0.0f };
uint32_t i;
for (i = 0; i < n_taps; i++) {
sum[0] += s[i] * t0[i];
sum[1] += s[i] * t1[i];
}
*d = (sum[1] - sum[0]) * x + sum[0];
}
MAKE_RESAMPLER_COPY(c);
MAKE_RESAMPLER_FULL(c);
MAKE_RESAMPLER_INTER(c);
static struct resample_info resample_table[] =
{
#if defined (HAVE_NEON)
{ SPA_AUDIO_FORMAT_F32, SPA_CPU_FLAG_NEON,
do_resample_copy_c, do_resample_full_neon, do_resample_inter_neon },
#endif
#if defined(HAVE_AVX) && defined(HAVE_FMA)
{ SPA_AUDIO_FORMAT_F32, SPA_CPU_FLAG_AVX | SPA_CPU_FLAG_FMA3,
do_resample_copy_c, do_resample_full_avx, do_resample_inter_avx },
#endif
#if defined (HAVE_SSSE3)
{ SPA_AUDIO_FORMAT_F32, SPA_CPU_FLAG_SSSE3 | SPA_CPU_FLAG_SLOW_UNALIGNED,
do_resample_copy_c, do_resample_full_ssse3, do_resample_inter_ssse3 },
#endif
#if defined (HAVE_SSE)
{ SPA_AUDIO_FORMAT_F32, SPA_CPU_FLAG_SSE,
do_resample_copy_c, do_resample_full_sse, do_resample_inter_sse },
#endif
{ SPA_AUDIO_FORMAT_F32, 0,
do_resample_copy_c, do_resample_full_c, do_resample_inter_c },
};
#define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a)
static const struct resample_info *find_resample_info(uint32_t format, uint32_t cpu_flags)
{
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(resample_table); i++) {
if (resample_table[i].format == format &&
MATCH_CPU_FLAGS(resample_table[i].cpu_flags, cpu_flags))
return &resample_table[i];
}
return NULL;
}
static void impl_native_free(struct resample *r)
{
free(r->data);
@ -118,32 +185,16 @@ static void impl_native_update_rate(struct resample *r, double rate)
data->inc = data->in_rate / data->out_rate;
data->frac = data->in_rate % data->out_rate;
if (data->in_rate == data->out_rate)
data->func = data->info->process_copy;
else if (rate == 1.0)
data->func = data->info->process_full;
else
data->func = data->info->process_inter;
spa_log_trace_fp(r->log, "native %p: rate:%f in:%d out:%d phase:%d inc:%d frac:%d", r,
rate, data->in_rate, data->out_rate, data->phase, data->inc, data->frac);
if (data->in_rate == data->out_rate)
data->func = do_resample_copy_c;
else {
bool is_full = rate == 1.0;
data->func = is_full ? do_resample_full_c : do_resample_inter_c;
#if defined (HAVE_NEON)
if (SPA_FLAG_IS_SET(r->cpu_flags, SPA_CPU_FLAG_NEON))
data->func = is_full ? do_resample_full_neon : do_resample_inter_neon;
#endif
#if defined (HAVE_SSE)
if (SPA_FLAG_IS_SET(r->cpu_flags, SPA_CPU_FLAG_SSE))
data->func = is_full ? do_resample_full_sse : do_resample_inter_sse;
#endif
#if defined (HAVE_SSSE3)
if (SPA_FLAG_IS_SET(r->cpu_flags, SPA_CPU_FLAG_SSSE3 | SPA_CPU_FLAG_SLOW_UNALIGNED))
data->func = is_full ? do_resample_full_ssse3 : do_resample_inter_ssse3;
#endif
#if defined(HAVE_AVX) && defined(HAVE_FMA)
if (SPA_FLAG_IS_SET(r->cpu_flags, SPA_CPU_FLAG_AVX | SPA_CPU_FLAG_FMA3))
data->func = is_full ? do_resample_full_avx : do_resample_inter_avx;
#endif
}
}
static uint32_t impl_native_in_len(struct resample *r, uint32_t out_len)
@ -264,7 +315,7 @@ static uint32_t impl_native_delay (struct resample *r)
return d->n_taps / 2;
}
static int impl_native_init(struct resample *r)
int resample_native_init(struct resample *r)
{
struct native_data *d;
const struct quality *q;
@ -326,8 +377,13 @@ static int impl_native_init(struct resample *r)
build_filter(d->filter, d->filter_stride, n_taps, n_phases, scale);
spa_log_debug(r->log, "native %p: q:%d in:%d out:%d n_taps:%d n_phases:%d",
r, r->quality, in_rate, out_rate, n_taps, n_phases);
d->info = find_resample_info(SPA_AUDIO_FORMAT_F32, r->cpu_flags);
spa_log_debug(r->log, "native %p: q:%d in:%d out:%d n_taps:%d n_phases:%d features:%08x:%08x",
r, r->quality, in_rate, out_rate, n_taps, n_phases,
r->cpu_flags, d->info->cpu_flags);
r->cpu_flags = d->info->cpu_flags;
impl_native_reset(r);
impl_native_update_rate(r, 1.0);

View file

@ -26,6 +26,8 @@
#include <xmmintrin.h>
#include "resample-peaks-impl.h"
static inline float hmax_ps(__m128 val)
{
__m128 t = _mm_movehl_ps(val, val);
@ -35,7 +37,7 @@ static inline float hmax_ps(__m128 val)
return _mm_cvtss_f32(val);
}
static void impl_peaks_process_sse(struct resample *r,
void resample_peaks_process_sse(struct resample *r,
const void * SPA_RESTRICT src[], uint32_t *in_len,
void * SPA_RESTRICT dst[], uint32_t *out_len)
{

View file

@ -23,28 +23,13 @@
*/
#include <math.h>
#include <errno.h>
struct peaks_data {
uint32_t o_count;
uint32_t i_count;
float max_f[0];
};
#include <spa/param/audio/format.h>
#if defined (__SSE__)
#include "resample-peaks-sse.h"
#endif
#include "resample-peaks-impl.h"
static void impl_peaks_free(struct resample *r)
{
free(r->data);
r->data = NULL;
}
static void impl_peaks_update_rate(struct resample *r, double rate)
{
}
static void impl_peaks_process(struct resample *r,
static void resample_peaks_process_c(struct resample *r,
const void * SPA_RESTRICT src[], uint32_t *in_len,
void * SPA_RESTRICT dst[], uint32_t *out_len)
{
@ -90,32 +75,73 @@ static void impl_peaks_process(struct resample *r,
}
}
struct resample_info {
uint32_t format;
uint32_t cpu_flags;
void (*process) (struct resample *r,
const void * SPA_RESTRICT src[], uint32_t *in_len,
void * SPA_RESTRICT dst[], uint32_t *out_len);
};
static struct resample_info resample_table[] =
{
#if defined (HAVE_SSE)
{ SPA_AUDIO_FORMAT_F32, SPA_CPU_FLAG_SSE, resample_peaks_process_sse, },
#endif
{ SPA_AUDIO_FORMAT_F32, 0, resample_peaks_process_c, },
};
#define MATCH_CPU_FLAGS(a,b) ((a) == 0 || ((a) & (b)) == a)
static const struct resample_info *find_resample_info(uint32_t format, uint32_t cpu_flags)
{
size_t i;
for (i = 0; i < SPA_N_ELEMENTS(resample_table); i++) {
if (resample_table[i].format == format &&
MATCH_CPU_FLAGS(resample_table[i].cpu_flags, cpu_flags)) {
return &resample_table[i];
}
}
return NULL;
}
static void impl_peaks_free(struct resample *r)
{
free(r->data);
r->data = NULL;
}
static void impl_peaks_update_rate(struct resample *r, double rate)
{
}
static void impl_peaks_reset (struct resample *r)
{
struct peaks_data *d = r->data;
d->i_count = d->o_count = 0;
}
static int impl_peaks_init(struct resample *r)
int resample_peaks_init(struct resample *r)
{
struct peaks_data *d;
const struct resample_info *info;
r->free = impl_peaks_free;
r->update_rate = impl_peaks_update_rate;
#if defined (__SSE__)
if (r->cpu_flags & SPA_CPU_FLAG_SSE)
r->process = impl_peaks_process_sse;
else
#endif
r->process = impl_peaks_process;
if ((info = find_resample_info(SPA_AUDIO_FORMAT_F32, r->cpu_flags)) == NULL)
return -ENOTSUP;
r->process = info->process;
r->reset = impl_peaks_reset;
d = r->data = calloc(1, sizeof(struct peaks_data) * sizeof(float) * r->channels);
if (r->data == NULL)
return -errno;
spa_log_debug(r->log, "peaks %p: in:%d out:%d", r, r->i_rate, r->o_rate);
spa_log_debug(r->log, "peaks %p: in:%d out:%d features:%08x:%08x", r,
r->i_rate, r->o_rate, r->cpu_flags, info->cpu_flags);
r->cpu_flags = info->cpu_flags;
d->i_count = d->o_count = 0;
return 0;
}

View file

@ -40,9 +40,6 @@
#include "resample.h"
#include "resample-peaks.h"
#include "resample-native.h"
#define NAME "resample"
#define DEFAULT_RATE 48000
@ -166,9 +163,9 @@ static int setup_convert(struct impl *this,
this->resample.quality = this->props.quality;
if (this->peaks)
err = impl_peaks_init(&this->resample);
err = resample_peaks_init(&this->resample);
else
err = impl_native_init(&this->resample);
err = resample_native_init(&this->resample);
return err;
}

View file

@ -59,4 +59,7 @@ struct resample {
#define resample_reset(r) (r)->reset(r)
#define resample_delay(r) (r)->delay(r)
int resample_native_init(struct resample *r);
int resample_peaks_init(struct resample *r);
#endif /* RESAMPLE_H */

View file

@ -38,7 +38,6 @@
SPA_LOG_IMPL(logger);
#include "resample.h"
#include "resample-native.h"
#define DEFAULT_QUALITY RESAMPLE_DEFAULT_QUALITY
@ -166,7 +165,7 @@ static int do_conversion(struct data *d)
r.i_rate = d->iinfo.samplerate;
r.o_rate = d->oinfo.samplerate;
r.quality = d->quality < 0 ? DEFAULT_QUALITY : d->quality;
impl_native_init(&r);
resample_native_init(&r);
for (j = 0; j < channels; j++)
src[j] = &in[MAX_SAMPLES * j];

View file

@ -35,7 +35,6 @@
SPA_LOG_IMPL(logger);
#include "resample.h"
#include "resample-native.h"
#define N_SAMPLES 253
#define N_CHANNELS 11
@ -73,7 +72,7 @@ static void test_native(void)
r.i_rate = 44100;
r.o_rate = 44100;
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
feed_1(&r);
@ -83,7 +82,7 @@ static void test_native(void)
r.i_rate = 44100;
r.o_rate = 48000;
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
feed_1(&r);
}
@ -126,7 +125,7 @@ static void test_in_len(void)
r.i_rate = 32000;
r.o_rate = 48000;
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
pull_blocks(&r, 1024);
@ -136,7 +135,7 @@ static void test_in_len(void)
r.i_rate = 44100;
r.o_rate = 48000;
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
pull_blocks(&r, 1024);
@ -146,7 +145,7 @@ static void test_in_len(void)
r.i_rate = 48000;
r.o_rate = 44100;
r.quality = RESAMPLE_DEFAULT_QUALITY;
impl_native_init(&r);
resample_native_init(&r);
pull_blocks(&r, 1024);
}