mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-11-01 22:58:50 -04:00
resampler: Precompute some common filter coefficients
While this is quite fast on x86 (order of a few microseconds), the computation can take a few milliseconds on ARM (measured at 1.9ms (32000 -> 48000) and 3.3ms (32000 -> 44100) on a Cortex A53). Let's precompute some common rates so that we can avoid this overhead on each stream (or any other audioconvert) instantiation. The approach taken here is to write a little program to create the resampler instance, and run that on the host at compile-time to generate some common rate conversions.
This commit is contained in:
parent
46f89d8009
commit
70a7bae5d7
5 changed files with 260 additions and 1 deletions
200
spa/plugins/audioconvert/spa-resample-dump-coeffs.c
Normal file
200
spa/plugins/audioconvert/spa-resample-dump-coeffs.c
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/* Spa */
|
||||
/* SPDX-FileCopyrightText: Copyright © 2024 Arun Raghavan <arun@asymptotic.io> */
|
||||
/* SPDX-License-Identifier: MIT */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
|
||||
#include <spa/support/log-impl.h>
|
||||
#include <spa/utils/result.h>
|
||||
#include <spa/utils/string.h>
|
||||
|
||||
SPA_LOG_IMPL(logger);
|
||||
|
||||
#include "resample.h"
|
||||
#include "resample-native-impl.h"
|
||||
|
||||
#define OPTIONS "ht:"
|
||||
static const struct option long_options[] = {
|
||||
{ "help", no_argument, NULL, 'h'},
|
||||
|
||||
{ "tuple", required_argument, NULL, 't' },
|
||||
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
static void show_usage(const char *name, bool is_error)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = is_error ? stderr : stdout;
|
||||
|
||||
fprintf(fp, "%s [options]\n", name);
|
||||
fprintf(fp,
|
||||
" -h, --help Show this help\n"
|
||||
"\n"
|
||||
" -t --tuple Sample rate tuple (as \"in_rate,out_rate[,quality]\")\n"
|
||||
"\n");
|
||||
}
|
||||
|
||||
static void parse_tuple(const char *arg, int *in, int *out, int *quality)
|
||||
{
|
||||
char tuple[256];
|
||||
char *token;
|
||||
|
||||
strncpy(tuple, arg, sizeof(tuple) - 1);
|
||||
*in = 0;
|
||||
*out = 0;
|
||||
|
||||
token = strtok(tuple, ",");
|
||||
if (!token || !spa_atoi32(token, in, 10))
|
||||
return;
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
if (!token || !spa_atoi32(token, out, 10))
|
||||
return;
|
||||
|
||||
token = strtok(NULL, ",");
|
||||
if (!token) {
|
||||
*quality = RESAMPLE_DEFAULT_QUALITY;
|
||||
} else if (!spa_atoi32(token, quality, 10)) {
|
||||
*quality = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
/* first, second now contain zeroes on error, or the numbers on success,
|
||||
* third contains a quality or -1 on error, default value if unspecified */
|
||||
}
|
||||
|
||||
#define PREFIX "__precomp_coeff"
|
||||
|
||||
static void dump_header(void)
|
||||
{
|
||||
printf("/* This is a generated file, see spa-resample-dump-coeffs.c */");
|
||||
printf("\n#include <stdint.h>\n");
|
||||
printf("\n#include <stdlib.h>\n");
|
||||
printf("\n");
|
||||
printf("struct resample_coeffs {\n");
|
||||
printf("\tuint32_t in_rate;\n");
|
||||
printf("\tuint32_t out_rate;\n");
|
||||
printf("\tint quality;\n");
|
||||
printf("\tconst float *filter;\n");
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
static void dump_footer(const uint32_t *ins, const uint32_t *outs, const int *qualities)
|
||||
{
|
||||
printf("\n");
|
||||
printf("static const struct resample_coeffs precomp_coeffs[] = {\n");
|
||||
while (*ins && *outs) {
|
||||
printf("\t{ .in_rate = %u, .out_rate = %u, .quality = %u, "
|
||||
".filter = %s_%u_%u_%u },\n",
|
||||
*ins, *outs, *qualities, PREFIX, *ins, *outs, *qualities);
|
||||
ins++;
|
||||
outs++;
|
||||
qualities++;
|
||||
}
|
||||
printf("\t{ .in_rate = 0, .out_rate = 0, .quality = 0, .filter = NULL },\n");
|
||||
printf("};\n");
|
||||
}
|
||||
|
||||
static void dump_coeffs(unsigned int in_rate, unsigned int out_rate, int quality)
|
||||
{
|
||||
struct resample r = { 0, };
|
||||
struct native_data *d;
|
||||
unsigned int i, filter_size;
|
||||
int ret;
|
||||
|
||||
r.log = &logger.log;
|
||||
r.i_rate = in_rate;
|
||||
r.o_rate = out_rate;
|
||||
r.quality = quality;
|
||||
r.channels = 1; /* irrelevant for generated taps */
|
||||
|
||||
if ((ret = resample_native_init(&r)) < 0) {
|
||||
fprintf(stderr, "can't init converter: %s\n", spa_strerror(ret));
|
||||
return;
|
||||
}
|
||||
|
||||
d = r.data;
|
||||
filter_size = d->filter_stride * (d->n_phases + 1);
|
||||
|
||||
printf("\n");
|
||||
printf("static const float %s_%u_%u_%u[] = {", PREFIX, in_rate, out_rate, quality);
|
||||
for (i = 0; i < filter_size; i++) {
|
||||
printf("%a", d->filter[i]);
|
||||
if (i != filter_size - 1)
|
||||
printf(",");
|
||||
}
|
||||
printf("};\n");
|
||||
|
||||
if (r.free)
|
||||
r.free(&r);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
unsigned int ins[256] = { 0, }, outs[256] = { 0, };
|
||||
int qualities[256] = { 0, };
|
||||
int in_rate = 0, out_rate = 0, quality = 0;
|
||||
int c, longopt_index = 0, i = 0;
|
||||
|
||||
while ((c = getopt_long(argc, argv, OPTIONS, long_options, &longopt_index)) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
show_usage(argv[0], false);
|
||||
return EXIT_SUCCESS;
|
||||
case 't':
|
||||
parse_tuple(optarg, &in_rate, &out_rate, &quality);
|
||||
if (in_rate <= 0) {
|
||||
fprintf(stderr, "error: bad input rate %d\n", in_rate);
|
||||
goto error;
|
||||
}
|
||||
if (out_rate <= 0) {
|
||||
fprintf(stderr, "error: bad output rate %d\n", out_rate);
|
||||
goto error;
|
||||
}
|
||||
if (quality < 0 || quality > 14) {
|
||||
fprintf(stderr, "error: bad quality value %s\n", optarg);
|
||||
goto error;
|
||||
}
|
||||
ins[i] = in_rate;
|
||||
outs[i] = out_rate;
|
||||
qualities[i] = quality;
|
||||
i++;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "error: unknown option\n");
|
||||
goto error_usage;
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc) {
|
||||
fprintf(stderr, "error: got %d extra argument(s))\n",
|
||||
optind - argc);
|
||||
goto error_usage;
|
||||
}
|
||||
if (in_rate == 0) {
|
||||
fprintf(stderr, "error: input rate must be specified\n");
|
||||
goto error;
|
||||
}
|
||||
if (out_rate == 0) {
|
||||
fprintf(stderr, "error: input rate must be specified\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
dump_header();
|
||||
while (i--) {
|
||||
dump_coeffs(ins[i], outs[i], qualities[i]);
|
||||
}
|
||||
dump_footer(ins, outs, qualities);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
||||
error_usage:
|
||||
show_usage(argv[0], true);
|
||||
error:
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue