diff mbox

[RFC,21/23] aplay: add a parser for channel map API

Message ID 20170817120004.15326-22-o-takashi@sakamocchi.jp (mailing list archive)
State New, archived
Headers show

Commit Message

Takashi Sakamoto Aug. 17, 2017, noon UTC
Current implementation of aplay uses channel map API, by an option
'--chmap' (-m). This API allows applications to get/set channel
position by getting position array; e.g. 'FR,FL'.

However, current implementation of ALSA PCM cure disallows applications
to set it as they prefer. This commit adds partly support for this API.
---
 aplay/options.c   | 28 +++++++++++++++++++++++---
 aplay/options.h   |  1 +
 aplay/xfer-alsa.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
 aplay/xfer-alsa.h |  2 ++
 4 files changed, 86 insertions(+), 5 deletions(-)
diff mbox

Patch

diff --git a/aplay/options.c b/aplay/options.c
index c16269d..7663703 100644
--- a/aplay/options.c
+++ b/aplay/options.c
@@ -199,7 +199,8 @@  static int apply_policies(struct context_options *opts,
 			  const char *cntr_format_literal,
 			  const char *node_literal,
 			  const char *sample_format_literal,
-			  const char *vu_mode_literal)
+			  const char *vu_mode_literal,
+			  const char *chmap_literal)
 {
 	int err;
 
@@ -232,6 +233,20 @@  static int apply_policies(struct context_options *opts,
 			return err;
 	}
 
+	if (chmap_literal) {
+		snd_pcm_chmap_t *chmap;
+		chmap = snd_pcm_chmap_parse_string(chmap_literal);
+		if (chmap == NULL) {
+			printf(_("Unable to parse channel map string: '%s'\n"),
+			       chmap_literal);
+			return -EINVAL;
+		}
+		free(chmap);
+		opts->chmap_literal = strdup(chmap_literal);
+		if (opts->chmap_literal == NULL)
+			return -ENOMEM;
+	}
+
 	if (opts->samples_per_frame > 0) {
 		if (opts->samples_per_frame < 1 ||
 		    opts->samples_per_frame > 256) {
@@ -316,7 +331,7 @@  static int apply_policies(struct context_options *opts,
 int context_options_init(struct context_options *opts, int argc,
 			 char *const *argv, snd_pcm_stream_t direction)
 {
-	static const char *s_opts = "hqid:vt:D:c:f:r:MNF:A:R:T:B:IV:";
+	static const char *s_opts = "hqid:vt:D:c:f:r:MNF:A:R:T:B:IV:m:";
 	static const struct option l_opts[] = {
 		/* For generic purposes. */
 		{"help",		0, 0, 'h'},
@@ -353,12 +368,14 @@  int context_options_init(struct context_options *opts, int argc,
 		{"dump-hw-params",	0, 0, OPT_DUMP_HWPARAMS},
 		{"fatal-errors",	0, 0, OPT_FATAL_ERRORS},
 		{"vumeter",		1, 0, 'V'},
+		{"chmap",		1, 0, 'm'},
 		{NULL,			0, 0, 0},
 	};
 	const char *cntr_format_literal = NULL;
 	const char *node_literal = NULL;
 	const char *sample_format_literal = NULL;
 	const char *vu_mode_literal = NULL;
+	const char *chmap_literal = NULL;
 	int c;
 	int err = 0;
 
@@ -428,6 +445,8 @@  int context_options_init(struct context_options *opts, int argc,
 			opts->dump_hw_params = true;
 		else if (c == OPT_FATAL_ERRORS)
 			opts->fatal_errors = true;
+		else if (c == 'm')
+			chmap_literal = optarg;
 		else
 			continue;
 
@@ -441,7 +460,7 @@  int context_options_init(struct context_options *opts, int argc,
 
 	return apply_policies(opts, direction, cntr_format_literal,
 			      node_literal, sample_format_literal,
-			      vu_mode_literal);
+			      vu_mode_literal, chmap_literal);
 }
 
 /*
@@ -614,6 +633,9 @@  void context_options_destroy(struct context_options *opts)
 	}
 	if (opts->node)
 		free(opts->node);
+	if (opts->chmap_literal)
+		free(opts->chmap_literal);
 	opts->paths = NULL;
 	opts->node = NULL;
+	opts->chmap_literal = NULL;
 }
diff --git a/aplay/options.h b/aplay/options.h
index 2536275..3bece52 100644
--- a/aplay/options.h
+++ b/aplay/options.h
@@ -53,6 +53,7 @@  struct context_options {
 	bool fatal_errors;
 
 	enum vumeter_mode vu_mode;
+	char *chmap_literal;
 
 	char **paths;
 	unsigned int path_count;
diff --git a/aplay/xfer-alsa.c b/aplay/xfer-alsa.c
index 2092f3e..e939005 100644
--- a/aplay/xfer-alsa.c
+++ b/aplay/xfer-alsa.c
@@ -53,7 +53,18 @@  static int set_access_hw_param(snd_pcm_t *handle,
 	return err;
 }
 
-static void dump_available_hw_params(snd_pcm_hw_params_t *hw_params,
+static void dump_chmap(const snd_pcm_chmap_t *chmap)
+{
+	int i;
+
+	printf("    channels: %u\n", chmap->channels);
+	for (i = 0; i < chmap->channels; ++i)
+		printf("      ch%u: %s\n",
+		       i, snd_pcm_chmap_name(chmap->pos[i]));
+}
+
+static void dump_available_hw_params(snd_pcm_t *handle,
+				     snd_pcm_hw_params_t *hw_params,
 				     const char *const node)
 {
 	unsigned int min_i, max_i;
@@ -61,6 +72,7 @@  static void dump_available_hw_params(snd_pcm_hw_params_t *hw_params,
 	snd_pcm_access_mask_t *access_mask;
 	snd_pcm_format_mask_t *format_mask;
 	snd_pcm_subformat_mask_t *subformat_mask;
+	snd_pcm_chmap_query_t **maps;
 	int i;
 	int err;
 
@@ -158,6 +170,19 @@  static void dump_available_hw_params(snd_pcm_hw_params_t *hw_params,
 			continue;
 		printf("    '%s'\n", snd_pcm_subformat_name(i));
 	}
+
+	maps = snd_pcm_query_chmaps(handle);
+	if (maps == NULL)
+		return;
+
+	printf("  available channel maps:\n");
+	for (i = 0; maps[i] != NULL; ++i) {
+		printf("    %u: %s\n", i,
+		       snd_pcm_chmap_type_name(maps[i]->type));
+		dump_chmap(&maps[i]->map);
+	}
+	snd_pcm_free_chmaps(maps);
+
 	printf("\n");
 }
 
@@ -204,8 +229,19 @@  static int xfer_alsa_init(struct xfer_context *xfer,
 	if (err < 0)
 		return err;
 
+	if (opts->chmap_literal != NULL) {
+		state->chmap = snd_pcm_chmap_parse_string(opts->chmap_literal);
+		if (state->chmap == NULL)
+			return -ENOMEM;
+
+		if (xfer->verbose) {
+			printf("Chmap argument:\n");
+			dump_chmap(state->chmap);
+		}
+	}
+
 	if (xfer->verbose)
-		dump_available_hw_params(state->hw_params, node);
+		dump_available_hw_params(state->handle, state->hw_params, node);
 
 	return set_access_hw_param(state->handle, state->hw_params, opts);
 }
@@ -262,6 +298,14 @@  static int configure_requested_params(struct alsa_state *state,
 	}
 
 	if (samples_per_frame > 0) {
+		if (state->chmap) {
+			if (samples_per_frame != state->chmap->channels) {
+				printf(_("Mismatch between channel number and "
+					 "given map: %u %u\n"),
+				samples_per_frame, state->chmap->channels);
+			}
+		}
+
 		err = snd_pcm_hw_params_set_channels(state->handle,
 						     state->hw_params,
 						     samples_per_frame);
@@ -319,6 +363,7 @@  static int retrieve_actual_params(snd_pcm_hw_params_t *hw_params,
 static void dump_sw_params(struct alsa_state *state)
 {
 	snd_pcm_uframes_t val_l;
+	snd_pcm_chmap_t *chmap;
 	int val_i;
 	int err;
 
@@ -354,6 +399,13 @@  static void dump_sw_params(struct alsa_state *state)
 		return;
 	printf("  silence-size: %lu\n", val_l);
 
+	chmap = snd_pcm_get_chmap(state->handle);
+	if (chmap != NULL) {
+		printf("  current-chmap:\n");
+		dump_chmap(chmap);
+		free(chmap);
+	}
+
 	printf("\n");
 }
 
@@ -539,6 +591,10 @@  static void xfer_alsa_destroy(struct xfer_context *xfer)
 		snd_pcm_sw_params_free(state->sw_params);
 	state->hw_params = NULL;
 	state->sw_params = NULL;
+
+	if (state->chmap)
+		free(state->chmap);
+	state->chmap = NULL;
 }
 
 const struct xfer_data xfer_alsa = {
diff --git a/aplay/xfer-alsa.h b/aplay/xfer-alsa.h
index 8702685..0257702 100644
--- a/aplay/xfer-alsa.h
+++ b/aplay/xfer-alsa.h
@@ -38,6 +38,8 @@  struct alsa_state {
 	void *private_data;
 
 	bool verbose;
+
+	snd_pcm_chmap_t *chmap;
 };
 
 struct xfer_alsa_io_ops {