mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	audioconvert: Always apply noise when asked
Rename empty.noise -> dither.noise and always add this amount of noise when > 0. This also adds the noise to silent sounds, not only when nothing is connected because that would also be a problem when an amp needs to be kept alive with an non-0 signal. Rename noise -> dither because we can use this also for dithering later. See #705
This commit is contained in:
		
							parent
							
								
									abcf7cb8d8
								
							
						
					
					
						commit
						9f55708e9d
					
				
					 9 changed files with 107 additions and 68 deletions
				
			
		| 
						 | 
				
			
			@ -49,7 +49,7 @@
 | 
			
		|||
#include "fmt-ops.h"
 | 
			
		||||
#include "channelmix-ops.h"
 | 
			
		||||
#include "resample.h"
 | 
			
		||||
#include "noise-ops.h"
 | 
			
		||||
#include "dither-ops.h"
 | 
			
		||||
 | 
			
		||||
#undef SPA_LOG_TOPIC_DEFAULT
 | 
			
		||||
#define SPA_LOG_TOPIC_DEFAULT log_topic
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +93,7 @@ struct props {
 | 
			
		|||
	unsigned int resample_quality;
 | 
			
		||||
	unsigned int resample_disabled:1;
 | 
			
		||||
	double rate;
 | 
			
		||||
	uint32_t empty_noise;
 | 
			
		||||
	uint32_t dither_noise;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void props_reset(struct props *props)
 | 
			
		||||
| 
						 | 
				
			
			@ -110,7 +110,7 @@ static void props_reset(struct props *props)
 | 
			
		|||
	props->rate = 1.0;
 | 
			
		||||
	props->resample_quality = RESAMPLE_DEFAULT_QUALITY;
 | 
			
		||||
	props->resample_disabled = false;
 | 
			
		||||
	props->empty_noise = 0;
 | 
			
		||||
	props->dither_noise = 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct buffer {
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +212,7 @@ struct impl {
 | 
			
		|||
	struct channelmix mix;
 | 
			
		||||
	struct resample resample;
 | 
			
		||||
	struct volume volume;
 | 
			
		||||
	struct noise noise;
 | 
			
		||||
	struct dither dither;
 | 
			
		||||
	double rate_scale;
 | 
			
		||||
 | 
			
		||||
	uint32_t in_offset;
 | 
			
		||||
| 
						 | 
				
			
			@ -612,9 +612,9 @@ static int impl_node_enum_params(void *object, int seq,
 | 
			
		|||
		case 21:
 | 
			
		||||
			param = spa_pod_builder_add_object(&b,
 | 
			
		||||
				SPA_TYPE_OBJECT_PropInfo, id,
 | 
			
		||||
				SPA_PROP_INFO_name, SPA_POD_String("empty.noise"),
 | 
			
		||||
				SPA_PROP_INFO_description, SPA_POD_String("Fill empty buffers with noise"),
 | 
			
		||||
				SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->empty_noise, 0, 16),
 | 
			
		||||
				SPA_PROP_INFO_name, SPA_POD_String("dither.noise"),
 | 
			
		||||
				SPA_PROP_INFO_description, SPA_POD_String("Add dithering noise"),
 | 
			
		||||
				SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Int(p->dither_noise, 0, 16),
 | 
			
		||||
				SPA_PROP_INFO_params, SPA_POD_Bool(true));
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
| 
						 | 
				
			
			@ -683,8 +683,8 @@ static int impl_node_enum_params(void *object, int seq,
 | 
			
		|||
			spa_pod_builder_int(&b, p->resample_quality);
 | 
			
		||||
			spa_pod_builder_string(&b, "resample.disable");
 | 
			
		||||
			spa_pod_builder_bool(&b, p->resample_disabled);
 | 
			
		||||
			spa_pod_builder_string(&b, "empty.noise");
 | 
			
		||||
			spa_pod_builder_int(&b, p->empty_noise);
 | 
			
		||||
			spa_pod_builder_string(&b, "dither.noise");
 | 
			
		||||
			spa_pod_builder_int(&b, p->dither_noise);
 | 
			
		||||
			spa_pod_builder_pop(&b, &f[1]);
 | 
			
		||||
			param = spa_pod_builder_pop(&b, &f[0]);
 | 
			
		||||
			break;
 | 
			
		||||
| 
						 | 
				
			
			@ -754,8 +754,8 @@ static int audioconvert_set_param(struct impl *this, const char *k, const char *
 | 
			
		|||
		this->props.resample_quality = atoi(s);
 | 
			
		||||
	else if (spa_streq(k, "resample.disable"))
 | 
			
		||||
		this->props.resample_disabled = spa_atob(s);
 | 
			
		||||
	else if (spa_streq(k, "empty.noise"))
 | 
			
		||||
		spa_atou32(s, &this->props.empty_noise, 0);
 | 
			
		||||
	else if (spa_streq(k, "dither.noise"))
 | 
			
		||||
		spa_atou32(s, &this->props.dither_noise, 0);
 | 
			
		||||
	else
 | 
			
		||||
		return 0;
 | 
			
		||||
	return 1;
 | 
			
		||||
| 
						 | 
				
			
			@ -1395,17 +1395,18 @@ static int setup_out_convert(struct impl *this)
 | 
			
		|||
	spa_log_debug(this->log, "%p: got converter features %08x:%08x passthrough:%d", this,
 | 
			
		||||
			this->cpu_flags, out->conv.cpu_flags, out->conv.is_passthrough);
 | 
			
		||||
 | 
			
		||||
	this->noise.intensity = (calc_width(&dst_info) * 8) - 1;
 | 
			
		||||
	this->noise.intensity -= SPA_MIN(this->noise.intensity, this->props.empty_noise);
 | 
			
		||||
	this->noise.n_channels = dst_info.info.raw.channels;
 | 
			
		||||
	this->noise.cpu_flags = this->cpu_flags;
 | 
			
		||||
	if (this->props.dither_noise > 0) {
 | 
			
		||||
		this->dither.intensity = (calc_width(&dst_info) * 8) - 1;
 | 
			
		||||
		this->dither.intensity -= SPA_MIN(this->dither.intensity, this->props.dither_noise);
 | 
			
		||||
		this->dither.n_channels = dst_info.info.raw.channels;
 | 
			
		||||
		this->dither.cpu_flags = this->cpu_flags;
 | 
			
		||||
 | 
			
		||||
	if ((res = noise_init(&this->noise)) < 0)
 | 
			
		||||
		return res;
 | 
			
		||||
 | 
			
		||||
	spa_log_debug(this->log, "%p: empty noise:%d intensity:%d", this,
 | 
			
		||||
			this->props.empty_noise, this->noise.intensity);
 | 
			
		||||
		if ((res = dither_init(&this->dither)) < 0)
 | 
			
		||||
			return res;
 | 
			
		||||
 | 
			
		||||
		spa_log_debug(this->log, "%p: dither noise:%d intensity:%d", this,
 | 
			
		||||
				this->props.dither_noise, this->dither.intensity);
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -2472,10 +2473,13 @@ static int impl_node_process(void *object)
 | 
			
		|||
	}
 | 
			
		||||
	this->out_offset += n_samples;
 | 
			
		||||
 | 
			
		||||
	if (in_empty && this->props.empty_noise > 0) {
 | 
			
		||||
	if (this->props.dither_noise > 0) {
 | 
			
		||||
		in_datas = (const void**)out_datas;
 | 
			
		||||
		if (out_passthrough)
 | 
			
		||||
			out_datas = dst_datas;
 | 
			
		||||
		noise_process(&this->noise, out_datas, n_samples);
 | 
			
		||||
		else
 | 
			
		||||
			out_datas = (void **)this->tmp_datas[(tmp++) & 1];
 | 
			
		||||
		dither_process(&this->dither, out_datas, in_datas, n_samples);
 | 
			
		||||
		in_empty = false;
 | 
			
		||||
	}
 | 
			
		||||
	if (!out_passthrough) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2657,6 +2661,8 @@ impl_init(const struct spa_handle_factory *factory,
 | 
			
		|||
	this->mix.rear_delay = 12.0f;
 | 
			
		||||
	this->mix.widen = 0.0f;
 | 
			
		||||
 | 
			
		||||
	this->dither.log = this->log;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; info && i < info->n_items; i++) {
 | 
			
		||||
		const char *k = info->items[i].key;
 | 
			
		||||
		const char *s = info->items[i].value;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,18 +22,21 @@
 | 
			
		|||
 * DEALINGS IN THE SOFTWARE.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "noise-ops.h"
 | 
			
		||||
#include "dither-ops.h"
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
noise_f32_c(struct noise *ns, void * SPA_RESTRICT dst[], uint32_t n_samples)
 | 
			
		||||
void dither_f32_c(struct dither *dt, void * SPA_RESTRICT dst[],
 | 
			
		||||
		const void * SPA_RESTRICT src[], uint32_t n_samples)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t i, n;
 | 
			
		||||
	const float **s = (const float**)src;
 | 
			
		||||
	float **d = (float**)dst;
 | 
			
		||||
	const float *t = ns->tab;
 | 
			
		||||
	int tab_idx = ns->tab_idx;
 | 
			
		||||
	const float *t = dt->tab;
 | 
			
		||||
	int tab_idx = dt->tab_idx;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < ns->n_channels; i++)
 | 
			
		||||
	for (i = 0; i < dt->n_channels; i++) {
 | 
			
		||||
		for (n = 0; n < n_samples; n++)
 | 
			
		||||
			d[i][n] = t[tab_idx++ & NOISE_MOD];
 | 
			
		||||
	ns->tab_idx = (tab_idx + 23) & NOISE_MOD;
 | 
			
		||||
			d[i][n] = s[i][n] + t[tab_idx++ & DITHER_MOD];
 | 
			
		||||
		tab_idx += 61;
 | 
			
		||||
	}
 | 
			
		||||
	dt->tab_idx = tab_idx & DITHER_MOD;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -32,52 +32,53 @@
 | 
			
		|||
#include <spa/support/log.h>
 | 
			
		||||
#include <spa/utils/defs.h>
 | 
			
		||||
 | 
			
		||||
#include "noise-ops.h"
 | 
			
		||||
#include "dither-ops.h"
 | 
			
		||||
 | 
			
		||||
typedef void (*noise_func_t) (struct noise *ns, void * SPA_RESTRICT dst[], uint32_t n_samples);
 | 
			
		||||
typedef void (*dither_func_t) (struct dither *d, void * SPA_RESTRICT dst[],
 | 
			
		||||
		const void * SPA_RESTRICT src[], uint32_t n_samples);
 | 
			
		||||
 | 
			
		||||
static const struct noise_info {
 | 
			
		||||
	noise_func_t process;
 | 
			
		||||
static const struct dither_info {
 | 
			
		||||
	dither_func_t process;
 | 
			
		||||
	uint32_t cpu_flags;
 | 
			
		||||
} noise_table[] =
 | 
			
		||||
} dither_table[] =
 | 
			
		||||
{
 | 
			
		||||
	{ noise_f32_c, 0 },
 | 
			
		||||
	{ dither_f32_c, 0 },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define MATCH_CPU_FLAGS(a,b)	((a) == 0 || ((a) & (b)) == a)
 | 
			
		||||
 | 
			
		||||
static const struct noise_info *find_noise_info(uint32_t cpu_flags)
 | 
			
		||||
static const struct dither_info *find_dither_info(uint32_t cpu_flags)
 | 
			
		||||
{
 | 
			
		||||
	size_t i;
 | 
			
		||||
	for (i = 0; i < SPA_N_ELEMENTS(noise_table); i++) {
 | 
			
		||||
		if (!MATCH_CPU_FLAGS(noise_table[i].cpu_flags, cpu_flags))
 | 
			
		||||
	for (i = 0; i < SPA_N_ELEMENTS(dither_table); i++) {
 | 
			
		||||
		if (!MATCH_CPU_FLAGS(dither_table[i].cpu_flags, cpu_flags))
 | 
			
		||||
			continue;
 | 
			
		||||
		return &noise_table[i];
 | 
			
		||||
		return &dither_table[i];
 | 
			
		||||
	}
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void impl_noise_free(struct noise *ns)
 | 
			
		||||
static void impl_dither_free(struct dither *d)
 | 
			
		||||
{
 | 
			
		||||
	ns->process = NULL;
 | 
			
		||||
	d->process = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int noise_init(struct noise *ns)
 | 
			
		||||
int dither_init(struct dither *d)
 | 
			
		||||
{
 | 
			
		||||
	const struct noise_info *info;
 | 
			
		||||
	const struct dither_info *info;
 | 
			
		||||
	size_t i;
 | 
			
		||||
 | 
			
		||||
	info = find_noise_info(ns->cpu_flags);
 | 
			
		||||
	info = find_dither_info(d->cpu_flags);
 | 
			
		||||
	if (info == NULL)
 | 
			
		||||
		return -ENOTSUP;
 | 
			
		||||
 | 
			
		||||
	if (ns->intensity >= 64)
 | 
			
		||||
	if (d->intensity >= 64)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < SPA_N_ELEMENTS(ns->tab); i++)
 | 
			
		||||
		ns->tab[i] = (drand48() - 0.5) / (UINT64_C(1) << ns->intensity);
 | 
			
		||||
	for (i = 0; i < SPA_N_ELEMENTS(d->tab); i++)
 | 
			
		||||
		d->tab[i] = (drand48() - 0.5) / (UINT64_C(1) << d->intensity);
 | 
			
		||||
 | 
			
		||||
	ns->free = impl_noise_free;
 | 
			
		||||
	ns->process = info->process;
 | 
			
		||||
	d->free = impl_dither_free;
 | 
			
		||||
	d->process = info->process;
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -26,36 +26,65 @@
 | 
			
		|||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
#include <spa/utils/defs.h>
 | 
			
		||||
#include <spa/utils/string.h>
 | 
			
		||||
#include <spa/param/audio/raw.h>
 | 
			
		||||
 | 
			
		||||
#define NOISE_SIZE	(1<<8)
 | 
			
		||||
#define NOISE_MOD	(NOISE_SIZE-1)
 | 
			
		||||
#define DITHER_SIZE	(1<<8)
 | 
			
		||||
#define DITHER_MOD	(DITHER_SIZE-1)
 | 
			
		||||
 | 
			
		||||
struct noise {
 | 
			
		||||
struct dither {
 | 
			
		||||
	uint32_t intensity;
 | 
			
		||||
#define DITHER_METHOD_NONE 		0
 | 
			
		||||
#define DITHER_METHOD_RECTANGULAR 	2
 | 
			
		||||
#define DITHER_METHOD_TRIANGULAR 	3
 | 
			
		||||
#define DITHER_METHOD_SHAPED_5	 	4
 | 
			
		||||
	uint32_t method;
 | 
			
		||||
	uint32_t n_channels;
 | 
			
		||||
	uint32_t cpu_flags;
 | 
			
		||||
 | 
			
		||||
	struct spa_log *log;
 | 
			
		||||
 | 
			
		||||
	void (*process) (struct noise *ns, void * SPA_RESTRICT dst[], uint32_t n_samples);
 | 
			
		||||
	void (*free) (struct noise *ns);
 | 
			
		||||
	void (*process) (struct dither *d, void * SPA_RESTRICT dst[],
 | 
			
		||||
			const void * SPA_RESTRICT src[], uint32_t n_samples);
 | 
			
		||||
	void (*free) (struct dither *d);
 | 
			
		||||
 | 
			
		||||
	float tab[NOISE_SIZE];
 | 
			
		||||
	float tab[DITHER_SIZE];
 | 
			
		||||
	int tab_idx;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int noise_init(struct noise *ns);
 | 
			
		||||
int dither_init(struct dither *d);
 | 
			
		||||
 | 
			
		||||
#define noise_process(ns,...)		(ns)->process(ns, __VA_ARGS__)
 | 
			
		||||
#define noise_free(ns)			(ns)->free(ns)
 | 
			
		||||
static const struct dither_method_info {
 | 
			
		||||
	const char *label;
 | 
			
		||||
	const char *description;
 | 
			
		||||
	uint32_t method;
 | 
			
		||||
} dither_method_info[] = {
 | 
			
		||||
	[DITHER_METHOD_NONE] = { "none", "Disabled", DITHER_METHOD_NONE },
 | 
			
		||||
	[DITHER_METHOD_RECTANGULAR] = { "rectangular", "Rectangular dithering", DITHER_METHOD_RECTANGULAR },
 | 
			
		||||
	[DITHER_METHOD_TRIANGULAR] = { "triangular", "Triangular dithering", DITHER_METHOD_TRIANGULAR },
 | 
			
		||||
	[DITHER_METHOD_SHAPED_5] = { "shaped5", "Shaped 5 dithering", DITHER_METHOD_SHAPED_5 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static inline uint32_t dither_method_from_label(const char *label)
 | 
			
		||||
{
 | 
			
		||||
	uint32_t i;
 | 
			
		||||
	for (i = 0; i < SPA_N_ELEMENTS(dither_method_info); i++) {
 | 
			
		||||
		if (spa_streq(dither_method_info[i].label, label))
 | 
			
		||||
			return dither_method_info[i].method;
 | 
			
		||||
	}
 | 
			
		||||
	return DITHER_METHOD_NONE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define dither_process(d,...)		(d)->process(d, __VA_ARGS__)
 | 
			
		||||
#define dither_free(d)			(d)->free(d)
 | 
			
		||||
 | 
			
		||||
#define DEFINE_FUNCTION(name,arch)			\
 | 
			
		||||
void noise_##name##_##arch(struct noise *ns,		\
 | 
			
		||||
void dither_##name##_##arch(struct dither *d,		\
 | 
			
		||||
		void * SPA_RESTRICT dst[],		\
 | 
			
		||||
		const void * SPA_RESTRICT src[],	\
 | 
			
		||||
		uint32_t n_samples);
 | 
			
		||||
 | 
			
		||||
#define NOISE_OPS_MAX_ALIGN	16
 | 
			
		||||
#define DITHER_OPS_MAX_ALIGN	16
 | 
			
		||||
 | 
			
		||||
DEFINE_FUNCTION(f32, c);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -95,8 +95,8 @@ audioconvert_lib = static_library('audioconvert',
 | 
			
		|||
    'fmt-ops-c.c',
 | 
			
		||||
    'volume-ops.c',
 | 
			
		||||
    'volume-ops-c.c',
 | 
			
		||||
    'noise-ops.c',
 | 
			
		||||
    'noise-ops-c.c' ],
 | 
			
		||||
    'dither-ops.c',
 | 
			
		||||
    'dither-ops-c.c' ],
 | 
			
		||||
  c_args : [ simd_cargs, '-O3'],
 | 
			
		||||
  link_with : simd_dependencies,
 | 
			
		||||
  include_directories : [configinc],
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue