mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-02 09:01:50 -05:00 
			
		
		
		
	filter-chain: allow multiple filters for convolver
Choose the closest match to reduce resampling loss. If match is not equal, resample. Keep the backwards compatibility retaining `filename` property. When both set, `filenames` takes priority
This commit is contained in:
		
							parent
							
								
									134a20c3c5
								
							
						
					
					
						commit
						2251b9b1d9
					
				
					 1 changed files with 100 additions and 22 deletions
				
			
		| 
						 | 
					@ -23,6 +23,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "pipewire/private.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <float.h>
 | 
					#include <float.h>
 | 
				
			||||||
#include <math.h>
 | 
					#include <math.h>
 | 
				
			||||||
| 
						 | 
					@ -439,22 +440,12 @@ struct convolver_impl {
 | 
				
			||||||
	struct convolver *conv;
 | 
						struct convolver *conv;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static float *read_samples(const char *filename, float gain, int delay, int offset,
 | 
					 | 
				
			||||||
		int length, int channel, long unsigned *rate, int *n_samples)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	float *samples;
 | 
					 | 
				
			||||||
#ifdef HAVE_SNDFILE
 | 
					#ifdef HAVE_SNDFILE
 | 
				
			||||||
	SF_INFO info;
 | 
					static float *read_samples_from_sf(SNDFILE *f, SF_INFO info, float gain, int delay,
 | 
				
			||||||
	SNDFILE *f;
 | 
							int offset, int length, int channel, long unsigned *rate, int *n_samples) {
 | 
				
			||||||
 | 
						float *samples;
 | 
				
			||||||
	int i, n;
 | 
						int i, n;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_zero(info);
 | 
					 | 
				
			||||||
	f = sf_open(filename, SFM_READ, &info) ;
 | 
					 | 
				
			||||||
	if (f == NULL) {
 | 
					 | 
				
			||||||
		pw_log_error("can't open %s", filename);
 | 
					 | 
				
			||||||
		return NULL;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (length <= 0)
 | 
						if (length <= 0)
 | 
				
			||||||
		length = info.frames;
 | 
							length = info.frames;
 | 
				
			||||||
	else
 | 
						else
 | 
				
			||||||
| 
						 | 
					@ -467,13 +458,12 @@ static float *read_samples(const char *filename, float gain, int delay, int offs
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	samples = calloc(n * info.channels, sizeof(float));
 | 
						samples = calloc(n * info.channels, sizeof(float));
 | 
				
			||||||
        if (samples == NULL)
 | 
						if (samples == NULL)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (offset > 0)
 | 
						if (offset > 0)
 | 
				
			||||||
		sf_seek(f, offset, SEEK_SET);
 | 
							sf_seek(f, offset, SEEK_SET);
 | 
				
			||||||
	sf_readf_float(f, samples + (delay * info.channels), length);
 | 
						sf_readf_float(f, samples + (delay * info.channels), length);
 | 
				
			||||||
	sf_close(f);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	channel = channel % info.channels;
 | 
						channel = channel % info.channels;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -483,16 +473,77 @@ static float *read_samples(const char *filename, float gain, int delay, int offs
 | 
				
			||||||
	*n_samples = n;
 | 
						*n_samples = n;
 | 
				
			||||||
	*rate = info.samplerate;
 | 
						*rate = info.samplerate;
 | 
				
			||||||
	return samples;
 | 
						return samples;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static float *read_samples(const char *filename, float gain, int delay, int offset,
 | 
				
			||||||
 | 
							int length, int channel, long unsigned *rate, int *n_samples)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef HAVE_SNDFILE
 | 
				
			||||||
 | 
						SF_INFO info;
 | 
				
			||||||
 | 
						SNDFILE *f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(info);
 | 
				
			||||||
 | 
						f = sf_open(filename, SFM_READ, &info) ;
 | 
				
			||||||
 | 
						if (f == NULL) {
 | 
				
			||||||
 | 
							pw_log_error("can't open %s", filename);
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float *samples = read_samples_from_sf(f, info, gain, delay, offset,
 | 
				
			||||||
 | 
									length, channel, rate, n_samples);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sf_close(f);
 | 
				
			||||||
 | 
						return samples;
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
	pw_log_error("compiled without sndfile support, can't load samples: "
 | 
						pw_log_error("compiled without sndfile support, can't load samples: "
 | 
				
			||||||
			"using dirac impulse");
 | 
								"using dirac impulse");
 | 
				
			||||||
	samples = calloc(1, sizeof(float));
 | 
						float *samples = calloc(1, sizeof(float));
 | 
				
			||||||
	samples[0] = gain;
 | 
						samples[0] = gain;
 | 
				
			||||||
	*n_samples = 1;
 | 
						*n_samples = 1;
 | 
				
			||||||
	return samples;
 | 
						return samples;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static float *read_closest(char **filenames, float gain, int delay, int offset,
 | 
				
			||||||
 | 
							int length, int channel, long unsigned *rate, int *n_samples)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef HAVE_SNDFILE
 | 
				
			||||||
 | 
						SF_INFO infos[MAX_RATES];
 | 
				
			||||||
 | 
						SNDFILE *fs[MAX_RATES];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(infos);
 | 
				
			||||||
 | 
						spa_zero(fs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int diff = INT_MAX;
 | 
				
			||||||
 | 
						uint32_t best = 0, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < MAX_RATES && filenames[i] && filenames[i][0]; i++) {
 | 
				
			||||||
 | 
							fs[i] = sf_open(filenames[i], SFM_READ, &infos[i]);
 | 
				
			||||||
 | 
							if (!fs[i])
 | 
				
			||||||
 | 
								continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (labs((long)infos[i].samplerate - (long)*rate) < diff) {
 | 
				
			||||||
 | 
								best = i;
 | 
				
			||||||
 | 
								diff = labs((long)infos[i].samplerate - (long)*rate);
 | 
				
			||||||
 | 
								pw_log_debug("new closest match: %d", infos[i].samplerate);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float *samples = read_samples_from_sf(fs[best], infos[best], gain, delay,
 | 
				
			||||||
 | 
							offset, length, channel, rate, n_samples);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < MAX_RATES; i++)
 | 
				
			||||||
 | 
							if (fs[i])
 | 
				
			||||||
 | 
								sf_close(fs[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return samples;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						return read_samples(filenames[0], gain, delay, offset,
 | 
				
			||||||
 | 
							length, channel, rate, n_samples);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static float *create_hilbert(const char *filename, float gain, int delay, int offset,
 | 
					static float *create_hilbert(const char *filename, float gain, int delay, int offset,
 | 
				
			||||||
		int length, int *n_samples)
 | 
							int length, int *n_samples)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -617,16 +668,21 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
 | 
				
			||||||
	struct convolver_impl *impl;
 | 
						struct convolver_impl *impl;
 | 
				
			||||||
	float *samples;
 | 
						float *samples;
 | 
				
			||||||
	int offset = 0, length = 0, channel = index, n_samples;
 | 
						int offset = 0, length = 0, channel = index, n_samples;
 | 
				
			||||||
 | 
						uint32_t i = 0;
 | 
				
			||||||
	struct spa_json it[2];
 | 
						struct spa_json it[2];
 | 
				
			||||||
 | 
						struct spa_json filenames_json;
 | 
				
			||||||
	const char *val;
 | 
						const char *val;
 | 
				
			||||||
	char key[256];
 | 
						char key[256], v[256];
 | 
				
			||||||
	char filename[PATH_MAX] = "";
 | 
						char filename[PATH_MAX] = "";
 | 
				
			||||||
 | 
						char *filenames[MAX_RATES];
 | 
				
			||||||
	int blocksize = 0, tailsize = 0;
 | 
						int blocksize = 0, tailsize = 0;
 | 
				
			||||||
	int delay = 0;
 | 
						int delay = 0;
 | 
				
			||||||
	int resample_quality = RESAMPLE_DEFAULT_QUALITY;
 | 
						int resample_quality = RESAMPLE_DEFAULT_QUALITY;
 | 
				
			||||||
	float gain = 1.0f;
 | 
						float gain = 1.0f;
 | 
				
			||||||
	unsigned long rate;
 | 
						unsigned long rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_zero(filenames);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	errno = EINVAL;
 | 
						errno = EINVAL;
 | 
				
			||||||
	if (config == NULL)
 | 
						if (config == NULL)
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
| 
						 | 
					@ -666,6 +722,17 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
 | 
				
			||||||
				return NULL;
 | 
									return NULL;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							else if (spa_streq(key, "filenames")) {
 | 
				
			||||||
 | 
								if (spa_json_enter_array(&it[1], &filenames_json) <= 0) {
 | 
				
			||||||
 | 
									pw_log_error("convolver:filenames requires an array");
 | 
				
			||||||
 | 
									return NULL;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								while (spa_json_get_string(&filenames_json, v, sizeof(v)) > 0 && i < MAX_RATES) {
 | 
				
			||||||
 | 
									filenames[i] = calloc(strlen(v) + 1, sizeof(char));
 | 
				
			||||||
 | 
									strncpy(filenames[i], v, strlen(v));
 | 
				
			||||||
 | 
									i++;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		else if (spa_streq(key, "offset")) {
 | 
							else if (spa_streq(key, "offset")) {
 | 
				
			||||||
			if (spa_json_get_int(&it[1], &offset) <= 0) {
 | 
								if (spa_json_get_int(&it[1], &offset) <= 0) {
 | 
				
			||||||
				pw_log_error("convolver:offset requires a number");
 | 
									pw_log_error("convolver:offset requires a number");
 | 
				
			||||||
| 
						 | 
					@ -693,8 +760,8 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
 | 
				
			||||||
		else if (spa_json_next(&it[1], &val) < 0)
 | 
							else if (spa_json_next(&it[1], &val) < 0)
 | 
				
			||||||
			break;
 | 
								break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (!filename[0]) {
 | 
						if (!filename[0] && !filenames[0]) {
 | 
				
			||||||
		pw_log_error("convolver:filename was not given");
 | 
							pw_log_error("convolver:filename nor filenames was given");
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -703,7 +770,14 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
 | 
				
			||||||
	if (offset < 0)
 | 
						if (offset < 0)
 | 
				
			||||||
		offset = 0;
 | 
							offset = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (spa_streq(filename, "/hilbert")) {
 | 
						if (filenames[0]) {
 | 
				
			||||||
 | 
							rate = SampleRate;
 | 
				
			||||||
 | 
							samples = read_closest(filenames, gain, delay, offset,
 | 
				
			||||||
 | 
									length, channel, &rate, &n_samples);
 | 
				
			||||||
 | 
							if (rate != SampleRate)
 | 
				
			||||||
 | 
								samples = resample_buffer(samples, &n_samples,
 | 
				
			||||||
 | 
										rate, SampleRate, resample_quality);
 | 
				
			||||||
 | 
						} else if (spa_streq(filename, "/hilbert")) {
 | 
				
			||||||
		samples = create_hilbert(filename, gain, delay, offset,
 | 
							samples = create_hilbert(filename, gain, delay, offset,
 | 
				
			||||||
				length, &n_samples);
 | 
									length, &n_samples);
 | 
				
			||||||
	} else if (spa_streq(filename, "/dirac")) {
 | 
						} else if (spa_streq(filename, "/dirac")) {
 | 
				
			||||||
| 
						 | 
					@ -722,13 +796,17 @@ static void * convolver_instantiate(const struct fc_descriptor * Descriptor,
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < MAX_RATES; i++)
 | 
				
			||||||
 | 
							if (filenames[i])
 | 
				
			||||||
 | 
								free(filenames[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (blocksize <= 0)
 | 
						if (blocksize <= 0)
 | 
				
			||||||
		blocksize = SPA_CLAMP(n_samples, 64, 256);
 | 
							blocksize = SPA_CLAMP(n_samples, 64, 256);
 | 
				
			||||||
	if (tailsize <= 0)
 | 
						if (tailsize <= 0)
 | 
				
			||||||
		tailsize = SPA_CLAMP(4096, blocksize, 32768);
 | 
							tailsize = SPA_CLAMP(4096, blocksize, 32768);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pw_log_info("using n_samples:%u %d:%d blocksize ir:%s", n_samples,
 | 
						pw_log_info("using n_samples:%u %d:%d blocksize", n_samples,
 | 
				
			||||||
			blocksize, tailsize, filename);
 | 
								blocksize, tailsize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	impl = calloc(1, sizeof(*impl));
 | 
						impl = calloc(1, sizeof(*impl));
 | 
				
			||||||
	if (impl == NULL)
 | 
						if (impl == NULL)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue