mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-10-29 05:40:27 -04:00 
			
		
		
		
	alsa: add enumeration of DSD formats
This commit is contained in:
		
							parent
							
								
									c0d3349849
								
							
						
					
					
						commit
						1b5d02dd31
					
				
					 2 changed files with 188 additions and 119 deletions
				
			
		|  | @ -535,6 +535,10 @@ impl_node_port_enum_params(void *object, int seq, | |||
| 			param = spa_format_audio_iec958_build(&b, id, | ||||
| 					&this->current_format.info.iec958); | ||||
| 			break; | ||||
| 		case SPA_MEDIA_SUBTYPE_dsd: | ||||
| 			param = spa_format_audio_dsd_build(&b, id, | ||||
| 					&this->current_format.info.dsd); | ||||
| 			break; | ||||
| 		default: | ||||
| 			return -EIO; | ||||
| 		} | ||||
|  | @ -664,6 +668,10 @@ static int port_set_format(void *object, | |||
| 			if (spa_format_audio_iec958_parse(format, &info.info.iec958) < 0) | ||||
| 				return -EINVAL; | ||||
| 			break; | ||||
| 		case SPA_MEDIA_SUBTYPE_dsd: | ||||
| 			if (spa_format_audio_dsd_parse(format, &info.info.dsd) < 0) | ||||
| 				return -EINVAL; | ||||
| 			break; | ||||
| 		default: | ||||
| 			return -EINVAL; | ||||
| 		} | ||||
|  |  | |||
|  | @ -374,21 +374,148 @@ static void sanitize_map(snd_pcm_chmap_t* map) | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int add_rate(struct state *state, uint32_t index, uint32_t *next, | ||||
| 		snd_pcm_hw_params_t *params, struct spa_pod_builder *b) | ||||
| { | ||||
| 	struct spa_pod_frame f[1]; | ||||
| 	int err, dir; | ||||
| 	unsigned int min, max; | ||||
| 	struct spa_pod_choice *choice; | ||||
| 	uint32_t rate; | ||||
| 
 | ||||
| 	CHECK(snd_pcm_hw_params_get_rate_min(params, &min, &dir), "get_rate_min"); | ||||
| 	CHECK(snd_pcm_hw_params_get_rate_max(params, &max, &dir), "get_rate_max"); | ||||
| 
 | ||||
| 	if (state->default_rate != 0) { | ||||
| 		if (min < state->default_rate) | ||||
| 			min = state->default_rate; | ||||
| 		if (max > state->default_rate) | ||||
| 			max = state->default_rate; | ||||
| 	} | ||||
| 
 | ||||
| 	spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); | ||||
| 
 | ||||
| 	spa_pod_builder_push_choice(b, &f[0], SPA_CHOICE_None, 0); | ||||
| 	choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[0]); | ||||
| 
 | ||||
| 	rate = state->position ? state->position->clock.rate.denom : DEFAULT_RATE; | ||||
| 
 | ||||
| 	spa_pod_builder_int(b, SPA_CLAMP(rate, min, max)); | ||||
| 	if (min != max) { | ||||
| 		spa_pod_builder_int(b, min); | ||||
| 		spa_pod_builder_int(b, max); | ||||
| 		choice->body.type = SPA_CHOICE_Range; | ||||
| 	} | ||||
| 	spa_pod_builder_pop(b, &f[0]); | ||||
| 
 | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int add_channels(struct state *state, uint32_t index, uint32_t *next, | ||||
| 		snd_pcm_hw_params_t *params, struct spa_pod_builder *b) | ||||
| { | ||||
| 	struct spa_pod_frame f[1]; | ||||
| 	size_t i; | ||||
| 	int err; | ||||
| 	snd_pcm_t *hndl = state->hndl; | ||||
| 	snd_pcm_chmap_query_t **maps; | ||||
| 	unsigned int min, max; | ||||
| 
 | ||||
| 	CHECK(snd_pcm_hw_params_get_channels_min(params, &min), "get_channels_min"); | ||||
| 	CHECK(snd_pcm_hw_params_get_channels_max(params, &max), "get_channels_max"); | ||||
| 	spa_log_debug(state->log, "channels (%d %d)", min, max); | ||||
| 
 | ||||
| 	if (state->default_channels != 0) { | ||||
| 		if (min < state->default_channels) | ||||
| 			min = state->default_channels; | ||||
| 		if (max > state->default_channels) | ||||
| 			max = state->default_channels; | ||||
| 	} | ||||
| 	min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS); | ||||
| 	max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS); | ||||
| 
 | ||||
| 	spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_channels, 0); | ||||
| 
 | ||||
| 	if (state->props.use_chmap && (maps = snd_pcm_query_chmaps(hndl)) != NULL) { | ||||
| 		uint32_t channel; | ||||
| 		snd_pcm_chmap_t* map; | ||||
| 
 | ||||
| skip_channels: | ||||
| 		if (maps[index] == NULL) { | ||||
| 			snd_pcm_free_chmaps(maps); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		map = &maps[index]->map; | ||||
| 
 | ||||
| 		spa_log_debug(state->log, "map %d channels (%d %d)", map->channels, min, max); | ||||
| 
 | ||||
| 		if (map->channels < min || map->channels > max) { | ||||
| 			index = (*next)++; | ||||
| 			goto skip_channels; | ||||
| 		} | ||||
| 
 | ||||
| 		sanitize_map(map); | ||||
| 		spa_pod_builder_int(b, map->channels); | ||||
| 
 | ||||
| 		spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0); | ||||
| 		spa_pod_builder_push_array(b, &f[0]); | ||||
| 		for (i = 0; i < map->channels; i++) { | ||||
| 			spa_log_debug(state->log, NAME" %p: position %zd %d", state, i, map->pos[i]); | ||||
| 			channel = chmap_position_to_channel(map->pos[i]); | ||||
| 			spa_pod_builder_id(b, channel); | ||||
| 		} | ||||
| 		spa_pod_builder_pop(b, &f[0]); | ||||
| 
 | ||||
| 		snd_pcm_free_chmaps(maps); | ||||
| 	} | ||||
| 	else { | ||||
| 		const struct channel_map *map = NULL; | ||||
| 		struct spa_pod_choice *choice; | ||||
| 
 | ||||
| 		if (index > 0) | ||||
| 			return 0; | ||||
| 
 | ||||
| 		spa_pod_builder_push_choice(b, &f[0], SPA_CHOICE_None, 0); | ||||
| 		choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[0]); | ||||
| 		spa_pod_builder_int(b, max); | ||||
| 		if (min != max) { | ||||
| 			spa_pod_builder_int(b, min); | ||||
| 			spa_pod_builder_int(b, max); | ||||
| 			choice->body.type = SPA_CHOICE_Range; | ||||
| 		} | ||||
| 		spa_pod_builder_pop(b, &f[0]); | ||||
| 
 | ||||
| 		if (min == max) { | ||||
| 			if (state->default_pos.channels == min) | ||||
| 				map = &state->default_pos; | ||||
| 			else if (min == max && min <= 8) | ||||
| 				map = &default_map[min]; | ||||
| 		} | ||||
| 		if (map) { | ||||
| 			spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0); | ||||
| 			spa_pod_builder_push_array(b, &f[0]); | ||||
| 			for (i = 0; i < map->channels; i++) { | ||||
| 				spa_log_debug(state->log, NAME" %p: position %zd %d", state, i, map->pos[i]); | ||||
| 				spa_pod_builder_id(b, map->pos[i]); | ||||
| 			} | ||||
| 			spa_pod_builder_pop(b, &f[0]); | ||||
| 		} | ||||
| 	} | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int enum_pcm_formats(struct state *state, uint32_t index, uint32_t *next, | ||||
| 		struct spa_pod **result, struct spa_pod_builder *b) | ||||
| { | ||||
| 	int err, dir; | ||||
| 	int res, err; | ||||
| 	size_t i, j; | ||||
| 	snd_pcm_t *hndl; | ||||
| 	snd_pcm_hw_params_t *params; | ||||
| 	snd_pcm_chmap_query_t **maps; | ||||
| 	struct spa_pod_frame f[2]; | ||||
| 	snd_pcm_format_mask_t *fmask; | ||||
| 	snd_pcm_access_mask_t *amask; | ||||
| 	unsigned int min, max; | ||||
| 	unsigned int rrate, rchannels; | ||||
| 	struct spa_pod_choice *choice; | ||||
| 	uint32_t rate; | ||||
| 
 | ||||
| 	hndl = state->hndl; | ||||
| 	snd_pcm_hw_params_alloca(¶ms); | ||||
|  | @ -484,110 +611,12 @@ static int enum_pcm_formats(struct state *state, uint32_t index, uint32_t *next, | |||
| 		choice->body.type = SPA_CHOICE_Enum; | ||||
| 	spa_pod_builder_pop(b, &f[1]); | ||||
| 
 | ||||
| 	CHECK(snd_pcm_hw_params_get_rate_min(params, &min, &dir), "get_rate_min"); | ||||
| 	CHECK(snd_pcm_hw_params_get_rate_max(params, &max, &dir), "get_rate_max"); | ||||
| 	if ((res = add_rate(state, index & 0xffff, next, params, b)) != 1) | ||||
| 		return res; | ||||
| 
 | ||||
| 	if (state->default_rate != 0) { | ||||
| 		if (min < state->default_rate) | ||||
| 			min = state->default_rate; | ||||
| 		if (max > state->default_rate) | ||||
| 			max = state->default_rate; | ||||
| 	} | ||||
| 	if ((res = add_channels(state, index & 0xffff, next, params, b)) != 1) | ||||
| 		return res; | ||||
| 
 | ||||
| 	spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); | ||||
| 
 | ||||
| 	spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0); | ||||
| 	choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]); | ||||
| 
 | ||||
| 	rate = state->position ? state->position->clock.rate.denom : DEFAULT_RATE; | ||||
| 
 | ||||
| 	spa_pod_builder_int(b, SPA_CLAMP(rate, min, max)); | ||||
| 	if (min != max) { | ||||
| 		spa_pod_builder_int(b, min); | ||||
| 		spa_pod_builder_int(b, max); | ||||
| 		choice->body.type = SPA_CHOICE_Range; | ||||
| 	} | ||||
| 	spa_pod_builder_pop(b, &f[1]); | ||||
| 
 | ||||
| 	CHECK(snd_pcm_hw_params_get_channels_min(params, &min), "get_channels_min"); | ||||
| 	CHECK(snd_pcm_hw_params_get_channels_max(params, &max), "get_channels_max"); | ||||
| 	spa_log_debug(state->log, "channels (%d %d)", min, max); | ||||
| 
 | ||||
| 	if (state->default_channels != 0) { | ||||
| 		if (min < state->default_channels) | ||||
| 			min = state->default_channels; | ||||
| 		if (max > state->default_channels) | ||||
| 			max = state->default_channels; | ||||
| 	} | ||||
| 	min = SPA_MIN(min, SPA_AUDIO_MAX_CHANNELS); | ||||
| 	max = SPA_MIN(max, SPA_AUDIO_MAX_CHANNELS); | ||||
| 
 | ||||
| 	spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_channels, 0); | ||||
| 
 | ||||
| 	if (state->props.use_chmap && (maps = snd_pcm_query_chmaps(hndl)) != NULL) { | ||||
| 		uint32_t channel; | ||||
| 		snd_pcm_chmap_t* map; | ||||
| 
 | ||||
| skip_channels: | ||||
| 		if (maps[index] == NULL) { | ||||
| 			snd_pcm_free_chmaps(maps); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		map = &maps[index]->map; | ||||
| 
 | ||||
| 		spa_log_debug(state->log, "map %d channels (%d %d)", map->channels, min, max); | ||||
| 
 | ||||
| 		if (map->channels < min || map->channels > max) { | ||||
| 			index = (*next)++; | ||||
| 			goto skip_channels; | ||||
| 		} | ||||
| 
 | ||||
| 		sanitize_map(map); | ||||
| 		spa_pod_builder_int(b, map->channels); | ||||
| 
 | ||||
| 		spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0); | ||||
| 		spa_pod_builder_push_array(b, &f[1]); | ||||
| 		for (j = 0; j < map->channels; j++) { | ||||
| 			spa_log_debug(state->log, NAME" %p: position %zd %d", state, j, map->pos[j]); | ||||
| 			channel = chmap_position_to_channel(map->pos[j]); | ||||
| 			spa_pod_builder_id(b, channel); | ||||
| 		} | ||||
| 		spa_pod_builder_pop(b, &f[1]); | ||||
| 
 | ||||
| 		snd_pcm_free_chmaps(maps); | ||||
| 	} | ||||
| 	else { | ||||
| 		const struct channel_map *map = NULL; | ||||
| 
 | ||||
| 		if (index > 0) | ||||
| 			return 0; | ||||
| 
 | ||||
| 		spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0); | ||||
| 		choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]); | ||||
| 		spa_pod_builder_int(b, max); | ||||
| 		if (min != max) { | ||||
| 			spa_pod_builder_int(b, min); | ||||
| 			spa_pod_builder_int(b, max); | ||||
| 			choice->body.type = SPA_CHOICE_Range; | ||||
| 		} | ||||
| 		spa_pod_builder_pop(b, &f[1]); | ||||
| 
 | ||||
| 		if (min == max) { | ||||
| 			if (state->default_pos.channels == min) | ||||
| 				map = &state->default_pos; | ||||
| 			else if (min == max && min <= 8) | ||||
| 				map = &default_map[min]; | ||||
| 		} | ||||
| 		if (map) { | ||||
| 			spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_position, 0); | ||||
| 			spa_pod_builder_push_array(b, &f[1]); | ||||
| 			for (j = 0; j < map->channels; j++) { | ||||
| 				spa_log_debug(state->log, NAME" %p: position %zd %d", state, j, map->pos[j]); | ||||
| 				spa_pod_builder_id(b, map->pos[j]); | ||||
| 			} | ||||
| 			spa_pod_builder_pop(b, &f[1]); | ||||
| 		} | ||||
| 	} | ||||
| 	*result = spa_pod_builder_pop(b, &f[0]); | ||||
| 	return 1; | ||||
| } | ||||
|  | @ -619,11 +648,10 @@ static bool codec_supported(uint32_t codec, unsigned int chmax, unsigned int rma | |||
| static int enum_iec958_formats(struct state *state, uint32_t index, uint32_t *next, | ||||
| 		struct spa_pod **result, struct spa_pod_builder *b) | ||||
| { | ||||
| 	int err, dir; | ||||
| 	int res, err, dir; | ||||
| 	snd_pcm_t *hndl; | ||||
| 	snd_pcm_hw_params_t *params; | ||||
| 	struct spa_pod_frame f[2]; | ||||
| 	struct spa_pod_choice *choice; | ||||
| 	unsigned int rmin, rmax; | ||||
| 	unsigned int chmin, chmax; | ||||
| 	uint32_t i, c, codecs[16], n_codecs; | ||||
|  | @ -676,23 +704,50 @@ static int enum_iec958_formats(struct state *state, uint32_t index, uint32_t *ne | |||
| 	} | ||||
| 	spa_pod_builder_pop(b, &f[1]); | ||||
| 
 | ||||
| 	spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_rate, 0); | ||||
| 	spa_pod_builder_push_choice(b, &f[1], SPA_CHOICE_None, 0); | ||||
| 	choice = (struct spa_pod_choice*)spa_pod_builder_frame(b, &f[1]); | ||||
| 
 | ||||
| 	spa_pod_builder_int(b, SPA_CLAMP(DEFAULT_RATE, rmin, rmax)); | ||||
| 	if (rmin != rmax) { | ||||
| 		spa_pod_builder_int(b, rmin); | ||||
| 		spa_pod_builder_int(b, rmax); | ||||
| 		choice->body.type = SPA_CHOICE_Range; | ||||
| 	} | ||||
| 	spa_pod_builder_pop(b, &f[1]); | ||||
| 	if ((res = add_rate(state, index & 0xffff, next, params, b)) != 1) | ||||
| 		return res; | ||||
| 
 | ||||
| 	(*next)++; | ||||
| 	*result = spa_pod_builder_pop(b, &f[0]); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static int enum_dsd_formats(struct state *state, uint32_t index, uint32_t *next, | ||||
| 		struct spa_pod **result, struct spa_pod_builder *b) | ||||
| { | ||||
| 	int res, err; | ||||
| 	snd_pcm_t *hndl; | ||||
| 	snd_pcm_hw_params_t *params; | ||||
| 	struct spa_pod_frame f[2]; | ||||
| 
 | ||||
| 	if ((index & 0xffff) > 0) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	hndl = state->hndl; | ||||
| 	snd_pcm_hw_params_alloca(¶ms); | ||||
| 	CHECK(snd_pcm_hw_params_any(hndl, params), "Broken configuration: no configurations available"); | ||||
| 
 | ||||
| 	CHECK(snd_pcm_hw_params_set_rate_resample(hndl, params, 0), "set_rate_resample"); | ||||
| 
 | ||||
| 	spa_pod_builder_push_object(b, &f[0], SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat); | ||||
| 	spa_pod_builder_add(b, | ||||
| 			SPA_FORMAT_mediaType,    SPA_POD_Id(SPA_MEDIA_TYPE_audio), | ||||
| 			SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_dsd), | ||||
| 			0); | ||||
| 
 | ||||
| 	spa_pod_builder_prop(b, SPA_FORMAT_AUDIO_bitorder, 0); | ||||
| 	spa_pod_builder_id(b, SPA_PARAM_BITORDER_msb); | ||||
| 
 | ||||
| 	if ((res = add_rate(state, index & 0xffff, next, params, b)) != 1) | ||||
| 		return res; | ||||
| 
 | ||||
| 	if ((res = add_channels(state, index & 0xffff, next, params, b)) != 1) | ||||
| 		return res; | ||||
| 
 | ||||
| 	*result = spa_pod_builder_pop(b, &f[0]); | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| int | ||||
| spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num, | ||||
| 		     const struct spa_pod *filter) | ||||
|  | @ -729,6 +784,12 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num, | |||
| 			goto next; | ||||
| 		} | ||||
| 	} | ||||
| 	else if (result.index < 0x30000) { | ||||
| 		if ((res = enum_dsd_formats(state, result.index, &result.next, &fmt, &b)) != 1) { | ||||
| 			result.next = 0x30000; | ||||
| 			goto next; | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 		goto enum_end; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Wim Taymans
						Wim Taymans