Added new channel-mapping API functions.
Not all plugins are covered, especially the route, multi and external
plugins don't work yet.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
/** Mute state */
#define SND_CTL_TLV_DB_GAIN_MUTE -9999999
+/** TLV type - fixed channel map positions */
+#define SND_CTL_TLVT_CHMAP_FIXED 0x00101
+/** TLV type - freely swappable channel map positions */
+#define SND_CTL_TLVT_CHMAP_VAR 0x00102
+/** TLV type - pair-wise swappable channel map positions */
+#define SND_CTL_TLVT_CHMAP_PAIRED 0x00103
+
/** CTL type */
typedef enum _snd_ctl_type {
/** Kernel level CTL */
int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2);
int snd_pcm_unlink(snd_pcm_t *pcm);
+enum {
+ SND_CHMAP_NONE = 0, /** unspecified channel position */
+ SND_CHMAP_FIXED, /** fixed channel position */
+ SND_CHMAP_VAR, /** freely swappable channel position */
+ SND_CHMAP_PAIRED, /** pair-wise swappable channel position */
+};
+
+int **snd_pcm_query_chmaps(snd_pcm_t *pcm);
+void snd_pcm_free_chmaps(int **maps);
+int *snd_pcm_get_chmap(snd_pcm_t *pcm);
+int snd_pcm_set_chmap(snd_pcm_t *pcm, const int *map);
+
//int snd_pcm_mixer_element(snd_pcm_t *pcm, snd_mixer_t *mixer, snd_mixer_elem_t **elem);
/*
SNDRV_PCM_TSTAMP_TYPE_LAST = SNDRV_PCM_TSTAMP_TYPE_MONOTONIC,
};
+/* channel positions */
+enum {
+ SNDRV_CHMAP_UNKNOWN = 0,
+ SNDRV_CHMAP_FL, /* front left */
+ SNDRV_CHMAP_FC, /* front center */
+ SNDRV_CHMAP_FR, /* front right */
+ SNDRV_CHMAP_FLC, /* front left center */
+ SNDRV_CHMAP_FRC, /* front right center */
+ SNDRV_CHMAP_RL, /* rear left */
+ SNDRV_CHMAP_RC, /* rear center */
+ SNDRV_CHMAP_RR, /* rear right */
+ SNDRV_CHMAP_RLC, /* rear left center */
+ SNDRV_CHMAP_RRC, /* rear right center */
+ SNDRV_CHMAP_SL, /* side left */
+ SNDRV_CHMAP_SR, /* side right */
+ SNDRV_CHMAP_LFE, /* LFE */
+ SNDRV_CHMAP_FLW, /* front left wide */
+ SNDRV_CHMAP_FRW, /* front right wide */
+ SNDRV_CHMAP_FLH, /* front left high */
+ SNDRV_CHMAP_FCH, /* front center high */
+ SNDRV_CHMAP_FRH, /* front right high */
+ SNDRV_CHMAP_TC, /* top center */
+ SNDRV_CHMAP_LAST = SNDRV_CHMAP_TC,
+};
+
enum {
SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int),
SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info),
#endif /* DOC_HIDDEN */
+/**
+ * \!brief Query the available channel maps
+ * \param pcm PCM handle to query
+ * \return the NULL-terminated array of integer pointers, each of
+ * which contains the channel map. A channel map is represented by an
+ * integer array, beginning with the channel map type, followed by the
+ * number of channels, and the position of each channel.
+ */
+int **snd_pcm_query_chmaps(snd_pcm_t *pcm)
+{
+ if (!pcm->ops->query_chmaps)
+ return NULL;
+ return pcm->ops->query_chmaps(pcm);
+}
+
+/**
+ * \!brief Release the channel map array allocated via #snd_pcm_query_chmaps
+ * \param maps the array pointer to release
+ */
+void snd_pcm_free_chmaps(int **maps)
+{
+ int **p;
+ if (!maps)
+ return;
+ for (p = maps; *p; p++)
+ free(*p);
+ free(maps);
+}
+
+/**
+ * \!brief Get the current channel map
+ * \param pcm PCM instance
+ * \return the current channel map, or NULL if error
+ */
+int *snd_pcm_get_chmap(snd_pcm_t *pcm)
+{
+ if (!pcm->ops->get_chmap)
+ return NULL;
+ return pcm->ops->get_chmap(pcm);
+}
+
+/**
+ * \!brief Configure the current channel map
+ * \param pcm PCM instance
+ * \param map the channel map to write
+ * \return zero if succeeded, or a negative error code
+ */
+int snd_pcm_set_chmap(snd_pcm_t *pcm, const int *map)
+{
+ if (!pcm->ops->set_chmap)
+ return -ENXIO;
+ return pcm->ops->set_chmap(pcm, map);
+}
+
+
/*
* basic helpers
*/
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**
return 0;
}
+int **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_pcm_query_chmaps(dmix->spcm);
+}
+
+int *snd_pcm_direct_get_chmap(snd_pcm_t *pcm)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_pcm_get_chmap(dmix->spcm);
+}
+
+int snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const int *map)
+{
+ snd_pcm_direct_t *dmix = pcm->private_data;
+ return snd_pcm_set_chmap(dmix->spcm, map);
+}
+
int snd_pcm_direct_resume(snd_pcm_t *pcm)
{
snd_pcm_direct_t *dmix = pcm->private_data;
snd1_pcm_direct_open_secondary_client
#define snd_pcm_direct_parse_open_conf \
snd1_pcm_direct_parse_open_conf
+#define snd_pcm_direct_query_chmaps \
+ snd1_pcm_direct_query_chmaps
+#define snd_pcm_direct_get_chmap \
+ snd1_pcm_direct_get_chmap
+#define snd_pcm_direct_set_chmap \
+ snd1_pcm_direct_set_chmap
int snd_pcm_direct_semaphore_create_or_connect(snd_pcm_direct_t *dmix);
int snd_pcm_direct_set_timer_params(snd_pcm_direct_t *dmix);
int snd_pcm_direct_open_secondary_client(snd_pcm_t **spcmp, snd_pcm_direct_t *dmix, const char *client_name);
+int **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm);
+int *snd_pcm_direct_get_chmap(snd_pcm_t *pcm);
+int snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const int *map);
+
int snd_timer_async(snd_timer_t *timer, int sig, pid_t pid);
struct timespec snd_pcm_hw_fast_tstamp(snd_pcm_t *pcm);
.async = snd_pcm_direct_async,
.mmap = snd_pcm_direct_mmap,
.munmap = snd_pcm_direct_munmap,
+ .query_chmaps = snd_pcm_direct_query_chmaps,
+ .get_chmap = snd_pcm_direct_get_chmap,
+ .set_chmap = snd_pcm_direct_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_dmix_fast_ops = {
.async = snd_pcm_direct_async,
.mmap = snd_pcm_direct_mmap,
.munmap = snd_pcm_direct_munmap,
+ .get_chmap = snd_pcm_direct_get_chmap,
+ .set_chmap = snd_pcm_direct_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_dshare_fast_ops = {
.async = snd_pcm_direct_async,
.mmap = snd_pcm_direct_mmap,
.munmap = snd_pcm_direct_munmap,
+ .query_chmaps = snd_pcm_direct_query_chmaps,
+ .get_chmap = snd_pcm_direct_get_chmap,
+ .set_chmap = snd_pcm_direct_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_dsnoop_fast_ops = {
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_file_fast_ops = {
return 0;
}
+int **snd_pcm_generic_query_chmaps(snd_pcm_t *pcm)
+{
+ snd_pcm_generic_t *generic = pcm->private_data;
+ return snd_pcm_query_chmaps(generic->slave);
+}
+
+int *snd_pcm_generic_get_chmap(snd_pcm_t *pcm)
+{
+ snd_pcm_generic_t *generic = pcm->private_data;
+ return snd_pcm_get_chmap(generic->slave);
+}
+
+int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const int *map)
+{
+ snd_pcm_generic_t *generic = pcm->private_data;
+ return snd_pcm_set_chmap(generic->slave, map);
+}
+
#endif /* DOC_HIDDEN */
snd1_pcm_generic_mmap
#define snd_pcm_generic_munmap \
snd1_pcm_generic_munmap
+#define snd_pcm_generic_query_chmaps \
+ snd1_pcm_generic_query_chmaps
+#define snd_pcm_generic_get_chmap \
+ snd1_pcm_generic_get_chmap
+#define snd_pcm_generic_set_chmap \
+ snd1_pcm_generic_set_chmap
int snd_pcm_generic_close(snd_pcm_t *pcm);
int snd_pcm_generic_nonblock(snd_pcm_t *pcm, int nonblock);
snd_htimestamp_t *tstamp);
int snd_pcm_generic_mmap(snd_pcm_t *pcm);
int snd_pcm_generic_munmap(snd_pcm_t *pcm);
+int **snd_pcm_generic_query_chmaps(snd_pcm_t *pcm);
+int *snd_pcm_generic_get_chmap(snd_pcm_t *pcm);
+int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const int *map);
+
+
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_hooks_fast_ops = {
return 0;
}
+static void fill_chmap_ctl_id(snd_pcm_t *pcm, snd_ctl_elem_id_t *id)
+{
+ 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)
+ 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);
+}
+
+static int **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm)
+{
+ snd_pcm_hw_t *hw = pcm->private_data;
+ snd_ctl_t *ctl;
+ snd_ctl_elem_id_t *id;
+ unsigned int tlv[256], *start;
+ int **map;
+ int i, ret, nums;
+
+ ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
+ if (ret < 0) {
+ SYSMSG("Cannot open the associated CTL\n");
+ return NULL;
+ }
+
+ snd_ctl_elem_id_alloca(&id);
+ fill_chmap_ctl_id(pcm, id);
+ 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");
+ return NULL;
+ }
+
+#if 0
+ for (i = 0; i < 32; i++)
+ fprintf(stderr, "%02x: %08x\n", i, tlv[i]);
+#endif
+ if (tlv[0] != SND_CTL_TLVT_CONTAINER) {
+ if (!is_chmap_type(tlv[0])) {
+ SYSMSG("Invalid TLV type %d\n", tlv[0]);
+ return NULL;
+ }
+ start = tlv;
+ nums = 1;
+ } else {
+ unsigned int *p;
+ int size;
+ start = tlv + 2;
+ size = tlv[1];
+ nums = 0;
+ for (p = start; size > 0; ) {
+ if (!is_chmap_type(p[0])) {
+ SYSMSG("Invalid TLV type %d\n", p[0]);
+ return NULL;
+ }
+ nums++;
+ size -= p[1] + 8;
+ p += p[1] / 4 + 2;
+ }
+ }
+ map = calloc(nums + 1, sizeof(int *));
+ if (!map)
+ return NULL;
+ for (i = 0; i < nums; i++) {
+ map[i] = malloc(start[1] + 8);
+ if (!map[i])
+ goto nomem;
+ map[i][0] = start[0] - 0x100;
+ map[i][1] = start[1] / 4;
+ memcpy(map[i] + 2, start + 2, start[1]);
+ start += start[1] / 4 + 2;
+ }
+ return map;
+
+ nomem:
+ for (; i >= 0; i--)
+ free(map[i]);
+ free(map);
+ return NULL;
+}
+
+static int *snd_pcm_hw_get_chmap(snd_pcm_t *pcm)
+{
+ snd_pcm_hw_t *hw = pcm->private_data;
+ int *map;
+ snd_ctl_t *ctl;
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_value_t *val;
+ unsigned int i;
+ int ret;
+
+ switch (FAST_PCM_STATE(hw)) {
+ case SNDRV_PCM_STATE_PREPARED:
+ case SNDRV_PCM_STATE_RUNNING:
+ case SNDRV_PCM_STATE_XRUN:
+ case SNDRV_PCM_STATE_DRAINING:
+ case SNDRV_PCM_STATE_PAUSED:
+ case SNDRV_PCM_STATE_SUSPENDED:
+ break;
+ default:
+ SYSMSG("Invalid PCM state for chmap_get: %s\n",
+ snd_pcm_state_name(FAST_PCM_STATE(hw)));
+ return NULL;
+ }
+ map = malloc(pcm->channels + 1);
+ if (!map)
+ return NULL;
+ *map = pcm->channels;
+ ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
+ if (ret < 0) {
+ free(map);
+ SYSMSG("Cannot open the associated CTL\n");
+ return NULL;
+ }
+ snd_ctl_elem_value_alloca(&val);
+ snd_ctl_elem_id_alloca(&id);
+ fill_chmap_ctl_id(pcm, id);
+ snd_ctl_elem_value_set_id(val, id);
+ ret = snd_ctl_elem_read(ctl, val);
+ if (ret < 0) {
+ snd_ctl_close(ctl);
+ free(map);
+ SYSMSG("Cannot read Channel Map ctl\n");
+ return NULL;
+ }
+ for (i = 0; i < pcm->channels; i++)
+ map[i + 1] = snd_ctl_elem_value_get_integer(val, i);
+ snd_ctl_close(ctl);
+ return map;
+}
+
+static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const int *map)
+{
+ snd_pcm_hw_t *hw = pcm->private_data;
+ snd_ctl_t *ctl;
+ snd_ctl_elem_id_t *id;
+ snd_ctl_elem_value_t *val;
+ int i, ret;
+
+ if (*map < 0 || *map > 128) {
+ SYSMSG("Invalid number of channels %d\n", *map);
+ return -EINVAL;
+ }
+ if (FAST_PCM_STATE(hw) != SNDRV_PCM_STATE_PREPARED) {
+ SYSMSG("Invalid PCM state for chmap_set: %s\n",
+ snd_pcm_state_name(FAST_PCM_STATE(hw)));
+ return -EBADFD;
+ }
+ ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0);
+ if (ret < 0) {
+ SYSMSG("Cannot open the associated CTL\n");
+ return ret;
+ }
+ snd_ctl_elem_id_alloca(&id);
+ snd_ctl_elem_value_alloca(&val);
+ fill_chmap_ctl_id(pcm, id);
+ snd_ctl_elem_value_set_id(val, id);
+ for (i = 0; i < *map; i++)
+ snd_ctl_elem_value_set_integer(val, i, map[i + 1]);
+ ret = snd_ctl_elem_write(ctl, val);
+ snd_ctl_close(ctl);
+ if (ret < 0)
+ SYSMSG("Cannot write Channel Map ctl\n");
+ return ret;
+}
+
static void snd_pcm_hw_dump(snd_pcm_t *pcm, snd_output_t *out)
{
snd_pcm_hw_t *hw = pcm->private_data;
.async = snd_pcm_hw_async,
.mmap = snd_pcm_hw_mmap,
.munmap = snd_pcm_hw_munmap,
+ .query_chmaps = snd_pcm_hw_query_chmaps,
+ .get_chmap = snd_pcm_hw_get_chmap,
+ .set_chmap = snd_pcm_hw_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
static int snd_pcm_ladspa_check_file(snd_pcm_ladspa_plugin_t * const plugin,
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
void (*dump)(snd_pcm_t *pcm, snd_output_t *out);
int (*mmap)(snd_pcm_t *pcm);
int (*munmap)(snd_pcm_t *pcm);
+ int **(*query_chmaps)(snd_pcm_t *pcm);
+ int *(*get_chmap)(snd_pcm_t *pcm);
+ int (*set_chmap)(snd_pcm_t *pcm, const int *map);
} snd_pcm_ops_t;
typedef struct {
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_meter_fast_ops = {
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
static const snd_pcm_fast_ops_t snd_pcm_mmap_emul_fast_ops = {
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**
.async = snd_pcm_generic_async,
.mmap = snd_pcm_generic_mmap,
.munmap = snd_pcm_generic_munmap,
+ .query_chmaps = snd_pcm_generic_query_chmaps,
+ .get_chmap = snd_pcm_generic_get_chmap,
+ .set_chmap = snd_pcm_generic_set_chmap,
};
/**