mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-10-29 05:40:27 -04:00 
			
		
		
		
	
							parent
							
								
									d62d2764e8
								
							
						
					
					
						commit
						5a307c11e1
					
				
					 9 changed files with 141 additions and 26 deletions
				
			
		|  | @ -287,15 +287,20 @@ channelmix_f32_2_5p1_c(struct channelmix *mix, void * SPA_RESTRICT dst[], | |||
| 		for (i = 0; i < n_dst; i++) | ||||
| 			memset(d[i], 0, n_samples * sizeof(float)); | ||||
| 	} | ||||
| 	else if (v0 == 1.0f && v1 == 1.0f && v4 == 1.0f && v5 == 1.0f) { | ||||
| 	else if (v0 == 1.0f && v1 == 1.0f) { | ||||
| 		for (n = 0; n < n_samples; n++) { | ||||
| 			float c = s[0][n] + s[1][n]; | ||||
| 			d[0][n] = d[4][n] = s[0][n]; | ||||
| 			d[1][n] = d[5][n] = s[1][n]; | ||||
| 			d[0][n] = s[0][n]; | ||||
| 			d[1][n] = s[1][n]; | ||||
| 			d[2][n] = c; | ||||
| 		} | ||||
| 		lr4_process(&mix->lr4[3], d[3], d[2], v3, n_samples); | ||||
| 		lr4_process(&mix->lr4[2], d[2], d[2], v2, n_samples); | ||||
| 
 | ||||
| 		delay_run(mix->buffer[0], &mix->pos[0], BUFFER_SIZE, mix->delay, | ||||
| 				d[4], s[0], v4, n_samples); | ||||
| 		delay_run(mix->buffer[1], &mix->pos[1], BUFFER_SIZE, mix->delay, | ||||
| 				d[5], s[1], v5, n_samples); | ||||
| 	} | ||||
| 	else { | ||||
| 		for (n = 0; n < n_samples; n++) { | ||||
|  | @ -303,11 +308,14 @@ channelmix_f32_2_5p1_c(struct channelmix *mix, void * SPA_RESTRICT dst[], | |||
| 			d[0][n] = s[0][n] * v0; | ||||
| 			d[1][n] = s[1][n] * v1; | ||||
| 			d[2][n] = c; | ||||
| 			d[4][n] = s[0][n] * v4; | ||||
| 			d[5][n] = s[1][n] * v5; | ||||
| 		} | ||||
| 		lr4_process(&mix->lr4[3], d[3], d[2], v3, n_samples); | ||||
| 		lr4_process(&mix->lr4[2], d[2], d[2], v2, n_samples); | ||||
| 
 | ||||
| 		delay_run(mix->buffer[0], &mix->pos[0], BUFFER_SIZE, mix->delay, | ||||
| 				d[4], s[0], v4, n_samples); | ||||
| 		delay_run(mix->buffer[1], &mix->pos[1], BUFFER_SIZE, mix->delay, | ||||
| 				d[5], s[1], v5, n_samples); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -331,16 +339,20 @@ channelmix_f32_2_7p1_c(struct channelmix *mix, void * SPA_RESTRICT dst[], | |||
| 		for (i = 0; i < n_dst; i++) | ||||
| 			memset(d[i], 0, n_samples * sizeof(float)); | ||||
| 	} | ||||
| 	else if (v0 == 1.0f && v1 == 1.0f && v4 == 1.0f && v5 == 1.0f && | ||||
| 	    v6 == 1.0f && v7 == 1.0f) { | ||||
| 	else if (v0 == 1.0f && v1 == 1.0f && v4 == 1.0f && v5 == 1.0f) { | ||||
| 		for (n = 0; n < n_samples; n++) { | ||||
| 			float c = s[0][n] + s[1][n]; | ||||
| 			d[0][n] = d[4][n] = d[6][n] = s[0][n]; | ||||
| 			d[1][n] = d[5][n] = d[7][n] = s[1][n]; | ||||
| 			d[0][n] = d[4][n] = s[0][n]; | ||||
| 			d[1][n] = d[5][n] = s[1][n]; | ||||
| 			d[2][n] = c; | ||||
| 		} | ||||
| 		lr4_process(&mix->lr4[3], d[3], d[2], v3, n_samples); | ||||
| 		lr4_process(&mix->lr4[2], d[2], d[2], v2, n_samples); | ||||
| 
 | ||||
| 		delay_run(mix->buffer[0], &mix->pos[0], BUFFER_SIZE, mix->delay, | ||||
| 				d[6], s[0], v6, n_samples); | ||||
| 		delay_run(mix->buffer[1], &mix->pos[1], BUFFER_SIZE, mix->delay, | ||||
| 				d[7], s[1], v7, n_samples); | ||||
| 	} | ||||
| 	else { | ||||
| 		for (n = 0; n < n_samples; n++) { | ||||
|  | @ -350,11 +362,14 @@ channelmix_f32_2_7p1_c(struct channelmix *mix, void * SPA_RESTRICT dst[], | |||
| 			d[2][n] = c; | ||||
| 			d[4][n] = s[0][n] * v4; | ||||
| 			d[5][n] = s[1][n] * v5; | ||||
| 			d[6][n] = s[0][n] * v6; | ||||
| 			d[7][n] = s[1][n] * v7; | ||||
| 		} | ||||
| 		lr4_process(&mix->lr4[3], d[3], d[2], v3, n_samples); | ||||
| 		lr4_process(&mix->lr4[2], d[2], d[2], v2, n_samples); | ||||
| 
 | ||||
| 		delay_run(mix->buffer[0], &mix->pos[0], BUFFER_SIZE, mix->delay, | ||||
| 				d[6], s[0], v6, n_samples); | ||||
| 		delay_run(mix->buffer[1], &mix->pos[1], BUFFER_SIZE, mix->delay, | ||||
| 				d[7], s[1], v7, n_samples); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -544,11 +544,13 @@ int channelmix_init(struct channelmix *mix) | |||
| 	if (info == NULL) | ||||
| 		return -ENOTSUP; | ||||
| 
 | ||||
| 	spa_log_debug(mix->log, "selected %s", info->name); | ||||
| 
 | ||||
| 	mix->free = impl_channelmix_free; | ||||
| 	mix->process = info->process; | ||||
| 	mix->set_volume = impl_channelmix_set_volume; | ||||
| 	mix->cpu_flags = info->cpu_flags; | ||||
| 	mix->delay = mix->rear_delay * mix->freq / 1000.0f; | ||||
| 
 | ||||
| 	spa_log_debug(mix->log, "selected %s delay:%d", info->name, mix->delay); | ||||
| 
 | ||||
| 	return make_matrix(mix); | ||||
| } | ||||
|  |  | |||
|  | @ -33,6 +33,7 @@ | |||
| extern struct spa_log_topic *log_topic; | ||||
| 
 | ||||
| #include "crossover.h" | ||||
| #include "delay.h" | ||||
| 
 | ||||
| #define VOLUME_MIN 0.0f | ||||
| #define VOLUME_NORM 1.0f | ||||
|  | @ -45,6 +46,7 @@ extern struct spa_log_topic *log_topic; | |||
| #define MASK_5_1	_M(FL)|_M(FR)|_M(FC)|_M(LFE)|_M(SL)|_M(SR)|_M(RL)|_M(RR) | ||||
| #define MASK_7_1	_M(FL)|_M(FR)|_M(FC)|_M(LFE)|_M(SL)|_M(SR)|_M(RL)|_M(RR) | ||||
| 
 | ||||
| #define BUFFER_SIZE 4096 | ||||
| 
 | ||||
| struct channelmix { | ||||
| 	uint32_t src_chan; | ||||
|  | @ -69,8 +71,13 @@ struct channelmix { | |||
| 
 | ||||
| 	float freq;					/* sample frequency */ | ||||
| 	float lfe_cutoff;				/* in Hz, 0 is disabled */ | ||||
| 	float rear_delay;				/* in ms, 0 is disabled */ | ||||
| 	struct lr4 lr4[SPA_AUDIO_MAX_CHANNELS]; | ||||
| 
 | ||||
| 	float buffer[2][BUFFER_SIZE]; | ||||
| 	uint32_t pos[2]; | ||||
| 	uint32_t delay; | ||||
| 
 | ||||
| 	void (*process) (struct channelmix *mix, void * SPA_RESTRICT dst[], | ||||
| 			const void * SPA_RESTRICT src[], uint32_t n_samples); | ||||
| 	void (*set_volume) (struct channelmix *mix, float volume, bool mute, | ||||
|  |  | |||
|  | @ -466,6 +466,14 @@ static int impl_node_enum_params(void *object, int seq, | |||
| 				SPA_PROP_INFO_container, SPA_POD_Id(SPA_TYPE_Array)); | ||||
| 			break; | ||||
| 		case 8: | ||||
| 			param = spa_pod_builder_add_object(&b, | ||||
| 				SPA_TYPE_OBJECT_PropInfo, id, | ||||
| 				SPA_PROP_INFO_name, SPA_POD_String("channelmix.disable"), | ||||
| 				SPA_PROP_INFO_description, SPA_POD_String("Disable Channel mixing"), | ||||
| 				SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->disabled), | ||||
| 				SPA_PROP_INFO_params, SPA_POD_Bool(true)); | ||||
| 			break; | ||||
| 		case 9: | ||||
| 			param = spa_pod_builder_add_object(&b, | ||||
| 				SPA_TYPE_OBJECT_PropInfo, id, | ||||
| 				SPA_PROP_INFO_name, SPA_POD_String("channelmix.normalize"), | ||||
|  | @ -474,7 +482,7 @@ static int impl_node_enum_params(void *object, int seq, | |||
| 					SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_NORMALIZE)), | ||||
| 				SPA_PROP_INFO_params, SPA_POD_Bool(true)); | ||||
| 			break; | ||||
| 		case 9: | ||||
| 		case 10: | ||||
| 			param = spa_pod_builder_add_object(&b, | ||||
| 				SPA_TYPE_OBJECT_PropInfo, id, | ||||
| 				SPA_PROP_INFO_name, SPA_POD_String("channelmix.mix-lfe"), | ||||
|  | @ -483,7 +491,7 @@ static int impl_node_enum_params(void *object, int seq, | |||
| 					SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_MIX_LFE)), | ||||
| 				SPA_PROP_INFO_params, SPA_POD_Bool(true)); | ||||
| 			break; | ||||
| 		case 10: | ||||
| 		case 11: | ||||
| 			param = spa_pod_builder_add_object(&b, | ||||
| 				SPA_TYPE_OBJECT_PropInfo, id, | ||||
| 				SPA_PROP_INFO_name, SPA_POD_String("channelmix.upmix"), | ||||
|  | @ -492,21 +500,22 @@ static int impl_node_enum_params(void *object, int seq, | |||
| 					SPA_FLAG_IS_SET(this->mix.options, CHANNELMIX_OPTION_UPMIX)), | ||||
| 				SPA_PROP_INFO_params, SPA_POD_Bool(true)); | ||||
| 			break; | ||||
| 		case 11: | ||||
| 		case 12: | ||||
| 			param = spa_pod_builder_add_object(&b, | ||||
| 				SPA_TYPE_OBJECT_PropInfo, id, | ||||
| 				SPA_PROP_INFO_name, SPA_POD_String("channelmix.lfe-cutoff"), | ||||
| 				SPA_PROP_INFO_description, SPA_POD_String("LFE cutoff frequency"), | ||||
| 				SPA_PROP_INFO_description, SPA_POD_String("LFE cutoff frequency (Hz)"), | ||||
| 				SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( | ||||
| 					this->mix.lfe_cutoff, 0.0, 1000.0), | ||||
| 				SPA_PROP_INFO_params, SPA_POD_Bool(true)); | ||||
| 			break; | ||||
| 		case 12: | ||||
| 		case 13: | ||||
| 			param = spa_pod_builder_add_object(&b, | ||||
| 				SPA_TYPE_OBJECT_PropInfo, id, | ||||
| 				SPA_PROP_INFO_name, SPA_POD_String("channelmix.disable"), | ||||
| 				SPA_PROP_INFO_description, SPA_POD_String("Disable Channel mixing"), | ||||
| 				SPA_PROP_INFO_type, SPA_POD_CHOICE_Bool(p->disabled), | ||||
| 				SPA_PROP_INFO_name, SPA_POD_String("channelmix.rear-delay"), | ||||
| 				SPA_PROP_INFO_description, SPA_POD_String("Rear channels delay (ms)"), | ||||
| 				SPA_PROP_INFO_type, SPA_POD_CHOICE_RANGE_Float( | ||||
| 					this->mix.rear_delay, 0.0, 1000.0), | ||||
| 				SPA_PROP_INFO_params, SPA_POD_Bool(true)); | ||||
| 			break; | ||||
| 		default: | ||||
|  | @ -547,6 +556,8 @@ static int impl_node_enum_params(void *object, int seq, | |||
| 				0); | ||||
| 			spa_pod_builder_prop(&b, SPA_PROP_params, 0); | ||||
| 			spa_pod_builder_push_struct(&b, &f[1]); | ||||
| 			spa_pod_builder_string(&b, "channelmix.disable"); | ||||
| 			spa_pod_builder_bool(&b, this->props.disabled); | ||||
| 			spa_pod_builder_string(&b, "channelmix.normalize"); | ||||
| 			spa_pod_builder_bool(&b, SPA_FLAG_IS_SET(this->mix.options, | ||||
| 						CHANNELMIX_OPTION_NORMALIZE)); | ||||
|  | @ -558,8 +569,8 @@ static int impl_node_enum_params(void *object, int seq, | |||
| 						CHANNELMIX_OPTION_UPMIX)); | ||||
| 			spa_pod_builder_string(&b, "channelmix.lfe-cutoff"); | ||||
| 			spa_pod_builder_float(&b, this->mix.lfe_cutoff); | ||||
| 			spa_pod_builder_string(&b, "channelmix.disable"); | ||||
| 			spa_pod_builder_bool(&b, this->props.disabled); | ||||
| 			spa_pod_builder_string(&b, "channelmix.rear-delay"); | ||||
| 			spa_pod_builder_float(&b, this->mix.rear_delay); | ||||
| 			spa_pod_builder_pop(&b, &f[1]); | ||||
| 			param = spa_pod_builder_pop(&b, &f[0]); | ||||
| 			break; | ||||
|  | @ -585,7 +596,9 @@ static int impl_node_enum_params(void *object, int seq, | |||
| 
 | ||||
| static int channelmix_set_param(struct impl *this, const char *k, const char *s) | ||||
| { | ||||
| 	if (spa_streq(k, "channelmix.normalize")) | ||||
| 	if (spa_streq(k, "channelmix.disable")) | ||||
| 		this->props.disabled = spa_atob(s); | ||||
| 	else if (spa_streq(k, "channelmix.normalize")) | ||||
| 		SPA_FLAG_UPDATE(this->mix.options, CHANNELMIX_OPTION_NORMALIZE, spa_atob(s)); | ||||
| 	else if (spa_streq(k, "channelmix.mix-lfe")) | ||||
| 		SPA_FLAG_UPDATE(this->mix.options, CHANNELMIX_OPTION_MIX_LFE, spa_atob(s)); | ||||
|  | @ -593,8 +606,8 @@ static int channelmix_set_param(struct impl *this, const char *k, const char *s) | |||
| 		SPA_FLAG_UPDATE(this->mix.options, CHANNELMIX_OPTION_UPMIX, spa_atob(s)); | ||||
| 	else if (spa_streq(k, "channelmix.lfe-cutoff")) | ||||
| 		spa_atof(s, &this->mix.lfe_cutoff); | ||||
| 	else if (spa_streq(k, "channelmix.disable")) | ||||
| 		this->props.disabled = spa_atob(s); | ||||
| 	else if (spa_streq(k, "channelmix.rear-delay")) | ||||
| 		spa_atof(s, &this->mix.rear_delay); | ||||
| 	else | ||||
| 		return 0; | ||||
| 	return 1; | ||||
|  | @ -1577,6 +1590,7 @@ impl_init(const struct spa_handle_factory *factory, | |||
| 
 | ||||
| 	this->mix.options = CHANNELMIX_OPTION_NORMALIZE; | ||||
| 	this->mix.log = this->log; | ||||
| 	this->mix.rear_delay = 12.0f; | ||||
| 
 | ||||
| 	for (i = 0; info && i < info->n_items; i++) { | ||||
| 		const char *k = info->items[i].key; | ||||
|  |  | |||
							
								
								
									
										72
									
								
								spa/plugins/audioconvert/delay.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								spa/plugins/audioconvert/delay.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,72 @@ | |||
| /* Spa
 | ||||
|  * | ||||
|  * Copyright © 2022 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. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef DELAY_H | ||||
| #define DELAY_H | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| static inline void delay_run(float *buffer, uint32_t *pos, | ||||
| 		uint32_t n_buffer, uint32_t delay, | ||||
| 		float *dst, const float *src, const float vol, uint32_t n_samples) | ||||
| { | ||||
| 	uint32_t i; | ||||
| 	uint32_t p = *pos; | ||||
| 
 | ||||
| 	for (i = 0; i < n_samples; i++) { | ||||
| 		buffer[p] = src[i]; | ||||
| 		dst[i] = buffer[(p - delay) & (n_buffer-1)] * vol; | ||||
| 		p = (p + 1) & (n_buffer-1); | ||||
| 	} | ||||
| 	*pos = p; | ||||
| } | ||||
| 
 | ||||
| static inline void delay_convolve_run(float *buffer, uint32_t *pos, | ||||
| 		uint32_t n_buffer, uint32_t delay, | ||||
| 		const float *taps, uint32_t n_taps, | ||||
| 		float *dst, const float *src, const float vol, uint32_t n_samples) | ||||
| { | ||||
| 	uint32_t i, j; | ||||
| 	uint32_t p = *pos; | ||||
| 
 | ||||
| 	for (i = 0; i < n_samples; i++) { | ||||
| 		float sum = 0.0f; | ||||
| 
 | ||||
| 		buffer[p] = src[i]; | ||||
| 		for (j = 0; j < n_taps; j++) | ||||
| 			sum += (taps[j] * buffer[((p - delay) - j) & (n_buffer-1)]); | ||||
| 		dst[i] = sum * vol; | ||||
| 
 | ||||
| 		p = (p + 1) & (n_buffer-1); | ||||
| 	} | ||||
| 	*pos = p; | ||||
| } | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| #endif /* DELAY_H */ | ||||
|  | @ -85,4 +85,5 @@ stream.properties = { | |||
|     #channelmix.mix-lfe    = true | ||||
|     #channelmix.upmix      = false | ||||
|     #channelmix.lfe-cutoff = 0 | ||||
|     #channelmix.rear-delay = 12.0 | ||||
| } | ||||
|  |  | |||
|  | @ -75,4 +75,5 @@ stream.properties = { | |||
|     #channelmix.mix-lfe    = false | ||||
|     #channelmix.upmix      = false | ||||
|     #channelmix.lfe-cutoff = 0 | ||||
|     #channelmix.rear-delay = 12.0 | ||||
| } | ||||
|  |  | |||
|  | @ -206,6 +206,7 @@ context.objects = [ | |||
|             #channelmix.mix-lfe     = false | ||||
|             #channelmix.upmix       = false | ||||
|             #channelmix.lfe-cutoff  = 0 | ||||
|             #channelmix.rear-delay  = 12.0 | ||||
|             channelmix.disable     = true | ||||
|             #node.param.Props      = { | ||||
|             #    params = [ | ||||
|  | @ -261,6 +262,7 @@ context.objects = [ | |||
|             #channelmix.mix-lfe    = false | ||||
|             #channelmix.upmix      = false | ||||
|             #channelmix.lfe-cutoff = 0 | ||||
|             #channelmix.rear-delay = 12.0 | ||||
|             channelmix.disable     = true | ||||
|             #node.param.Props      = { | ||||
|             #    params = [ | ||||
|  |  | |||
|  | @ -85,6 +85,7 @@ stream.properties = { | |||
|     #channelmix.mix-lfe    = false | ||||
|     #channelmix.upmix      = false | ||||
|     #channelmix.lfe-cutoff = 0 | ||||
|     #channelmix.rear-delay = 12.0 | ||||
| } | ||||
| 
 | ||||
| # client/stream specific properties | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Wim Taymans
						Wim Taymans