Takashi Iwai
8a90d87ca6
- backport from upstream tree: * lots of patches to support the new chmap API * fix segfault in rate plugin error path * add a couple of test programs * fix inifinte loop in htimestamp of dmix & co OBS-URL: https://build.opensuse.org/request/show/138456 OBS-URL: https://build.opensuse.org/package/show/multimedia:libs/alsa?expand=0&rev=113
208 lines
6.0 KiB
Diff
208 lines
6.0 KiB
Diff
From 01dc0e6825b5620510faaefaef40ff8cfb692b6e Mon Sep 17 00:00:00 2001
|
|
From: Takashi Iwai <tiwai@suse.de>
|
|
Date: Fri, 7 Sep 2012 14:15:04 +0200
|
|
Subject: [PATCH 10/30] PCM: Implement snd_pcm_query_chmaps_from_hw()
|
|
|
|
This is a function similar like snd_pcm_query_chmaps() but performs
|
|
the query without a PCM handle. The card, device and substream
|
|
numbers are passed as well as stream direction.
|
|
|
|
Signed-off-by: Takashi Iwai <tiwai@suse.de>
|
|
---
|
|
include/pcm.h | 3 +
|
|
src/pcm/pcm_hw.c | 117 +++++++++++++++++++++++++++++++++++--------------------
|
|
2 files changed, 79 insertions(+), 41 deletions(-)
|
|
|
|
--- a/include/pcm.h
|
|
+++ b/include/pcm.h
|
|
@@ -526,6 +526,9 @@ typedef struct snd_pcm_chmap_query {
|
|
|
|
|
|
snd_pcm_chmap_query_t **snd_pcm_query_chmaps(snd_pcm_t *pcm);
|
|
+snd_pcm_chmap_query_t **snd_pcm_query_chmaps_from_hw(int card, int dev,
|
|
+ int subdev,
|
|
+ snd_pcm_stream_t stream);
|
|
void snd_pcm_free_chmaps(snd_pcm_chmap_query_t **maps);
|
|
snd_pcm_chmap_t *snd_pcm_get_chmap(snd_pcm_t *pcm);
|
|
int snd_pcm_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map);
|
|
--- a/src/pcm/pcm_hw.c
|
|
+++ b/src/pcm/pcm_hw.c
|
|
@@ -1006,80 +1006,82 @@ static int snd_pcm_hw_htimestamp(snd_pcm
|
|
return 0;
|
|
}
|
|
|
|
-static void fill_chmap_ctl_id(snd_pcm_t *pcm, snd_ctl_elem_id_t *id)
|
|
+static void __fill_chmap_ctl_id(snd_ctl_elem_id_t *id, int dev, int subdev,
|
|
+ int stream)
|
|
{
|
|
- snd_pcm_hw_t *hw = pcm->private_data;
|
|
snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_PCM);
|
|
- if (pcm->stream == SND_PCM_STREAM_PLAYBACK)
|
|
+ if (stream == SND_PCM_STREAM_PLAYBACK)
|
|
snd_ctl_elem_id_set_name(id, "Playback Channel Map");
|
|
else
|
|
snd_ctl_elem_id_set_name(id, "Capture Channel Map");
|
|
- snd_ctl_elem_id_set_device(id, hw->device);
|
|
- snd_ctl_elem_id_set_index(id, hw->subdevice);
|
|
-}
|
|
-
|
|
-static int is_chmap_type(int type)
|
|
-{
|
|
- return (type >= SND_CTL_TLVT_CHMAP_FIXED &&
|
|
- type <= SND_CTL_TLVT_CHMAP_PAIRED);
|
|
-}
|
|
-
|
|
-enum { CHMAP_CTL_QUERY, CHMAP_CTL_GET, CHMAP_CTL_SET };
|
|
-
|
|
-static int chmap_caps(snd_pcm_hw_t *hw, int type)
|
|
-{
|
|
- if (hw->chmap_caps & (1 << type))
|
|
- return 1;
|
|
- if (hw->chmap_caps & (1 << (type + 8)))
|
|
- return 0;
|
|
- return 1;
|
|
+ snd_ctl_elem_id_set_device(id, dev);
|
|
+ snd_ctl_elem_id_set_index(id, subdev);
|
|
}
|
|
|
|
-static void chmap_caps_set_ok(snd_pcm_hw_t *hw, int type)
|
|
+static void fill_chmap_ctl_id(snd_pcm_t *pcm, snd_ctl_elem_id_t *id)
|
|
{
|
|
- hw->chmap_caps |= (1 << type);
|
|
+ snd_pcm_hw_t *hw = pcm->private_data;
|
|
+ return __fill_chmap_ctl_id(id, hw->device, hw->subdevice, pcm->stream);
|
|
}
|
|
|
|
-static void chmap_caps_set_error(snd_pcm_hw_t *hw, int type)
|
|
+static int is_chmap_type(int type)
|
|
{
|
|
- hw->chmap_caps |= (1 << (type + 8));
|
|
+ return (type >= SND_CTL_TLVT_CHMAP_FIXED &&
|
|
+ type <= SND_CTL_TLVT_CHMAP_PAIRED);
|
|
}
|
|
|
|
-static snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm)
|
|
+/**
|
|
+ * \!brief Query the available channel maps
|
|
+ * \param card the card number
|
|
+ * \param dev the PCM device number
|
|
+ * \param subdev the PCM substream index
|
|
+ * \param stream the direction of PCM stream
|
|
+ * \return the NULL-terminated array of integer pointers, or NULL at error.
|
|
+ *
|
|
+ * This function works like snd_pcm_query_chmaps() but it takes the card,
|
|
+ * device, substream and stream numbers instead of the already opened
|
|
+ * snd_pcm_t instance, so that you can query available channel maps of
|
|
+ * a PCM before actually opening it.
|
|
+ *
|
|
+ * As the parameters stand, the query is performed only to the hw PCM
|
|
+ * devices, not the abstracted PCM object in alsa-lib.
|
|
+ */
|
|
+snd_pcm_chmap_query_t **
|
|
+snd_pcm_query_chmaps_from_hw(int card, int dev, int subdev,
|
|
+ snd_pcm_stream_t stream)
|
|
{
|
|
- snd_pcm_hw_t *hw = pcm->private_data;
|
|
snd_ctl_t *ctl;
|
|
snd_ctl_elem_id_t *id;
|
|
unsigned int tlv[256], *start;
|
|
snd_pcm_chmap_query_t **map;
|
|
int i, ret, nums;
|
|
|
|
- if (!chmap_caps(hw, CHMAP_CTL_QUERY))
|
|
- return NULL;
|
|
-
|
|
- ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
|
|
+ ret = snd_ctl_hw_open(&ctl, NULL, card, 0);
|
|
if (ret < 0) {
|
|
SYSMSG("Cannot open the associated CTL\n");
|
|
- goto error;
|
|
+ return NULL;
|
|
}
|
|
|
|
snd_ctl_elem_id_alloca(&id);
|
|
- fill_chmap_ctl_id(pcm, id);
|
|
+ __fill_chmap_ctl_id(id, dev, subdev, stream);
|
|
ret = snd_ctl_elem_tlv_read(ctl, id, tlv, sizeof(tlv));
|
|
snd_ctl_close(ctl);
|
|
if (ret < 0) {
|
|
SYSMSG("Cannot read Channel Map TLV\n");
|
|
- goto error;
|
|
+ return NULL;
|
|
}
|
|
|
|
#if 0
|
|
for (i = 0; i < 32; i++)
|
|
fprintf(stderr, "%02x: %08x\n", i, tlv[i]);
|
|
#endif
|
|
+ /* FIXME: the parser below assumes that the TLV only contains
|
|
+ * chmap-related blocks
|
|
+ */
|
|
if (tlv[0] != SND_CTL_TLVT_CONTAINER) {
|
|
if (!is_chmap_type(tlv[0])) {
|
|
SYSMSG("Invalid TLV type %d\n", tlv[0]);
|
|
- goto error;
|
|
+ return NULL;
|
|
}
|
|
start = tlv;
|
|
nums = 1;
|
|
@@ -1092,7 +1094,7 @@ static snd_pcm_chmap_query_t **snd_pcm_h
|
|
for (p = start; size > 0; ) {
|
|
if (!is_chmap_type(p[0])) {
|
|
SYSMSG("Invalid TLV type %d\n", p[0]);
|
|
- goto error;
|
|
+ return NULL;
|
|
}
|
|
nums++;
|
|
size -= p[1] + 8;
|
|
@@ -1113,12 +1115,45 @@ static snd_pcm_chmap_query_t **snd_pcm_h
|
|
memcpy(map[i]->map.pos, start + 2, start[1]);
|
|
start += start[1] / 4 + 2;
|
|
}
|
|
- chmap_caps_set_ok(hw, CHMAP_CTL_QUERY);
|
|
return map;
|
|
+}
|
|
+
|
|
+enum { CHMAP_CTL_QUERY, CHMAP_CTL_GET, CHMAP_CTL_SET };
|
|
+
|
|
+static int chmap_caps(snd_pcm_hw_t *hw, int type)
|
|
+{
|
|
+ if (hw->chmap_caps & (1 << type))
|
|
+ return 1;
|
|
+ if (hw->chmap_caps & (1 << (type + 8)))
|
|
+ return 0;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static void chmap_caps_set_ok(snd_pcm_hw_t *hw, int type)
|
|
+{
|
|
+ hw->chmap_caps |= (1 << type);
|
|
+}
|
|
|
|
- error:
|
|
- chmap_caps_set_error(hw, CHMAP_CTL_QUERY);
|
|
- return NULL;
|
|
+static void chmap_caps_set_error(snd_pcm_hw_t *hw, int type)
|
|
+{
|
|
+ hw->chmap_caps |= (1 << (type + 8));
|
|
+}
|
|
+
|
|
+static snd_pcm_chmap_query_t **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm)
|
|
+{
|
|
+ snd_pcm_hw_t *hw = pcm->private_data;
|
|
+ snd_pcm_chmap_query_t **map;
|
|
+
|
|
+ if (!chmap_caps(hw, CHMAP_CTL_QUERY))
|
|
+ return NULL;
|
|
+
|
|
+ map = snd_pcm_query_chmaps_from_hw(hw->card, hw->device,
|
|
+ hw->subdevice, pcm->stream);
|
|
+ if (map)
|
|
+ chmap_caps_set_ok(hw, CHMAP_CTL_QUERY);
|
|
+ else
|
|
+ chmap_caps_set_error(hw, CHMAP_CTL_QUERY);
|
|
+ return map;
|
|
}
|
|
|
|
static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
|