mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	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.
		
			
				
	
	
		
			200 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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;
 | 
						|
}
 |