diff --git a/0001-Implement-the-channel-mapping-API.patch b/0001-Implement-the-channel-mapping-API.patch new file mode 100644 index 0000000..f345c82 --- /dev/null +++ b/0001-Implement-the-channel-mapping-API.patch @@ -0,0 +1,685 @@ +From 3c4a22ea49a881cdbfe2d50eef94b17e38104734 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 25 Jul 2012 15:05:15 +0200 +Subject: [PATCH 01/30] Implement the channel mapping API + +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 +--- + include/control.h | 7 + + include/pcm.h | 12 +++ + include/sound/asound.h | 25 ++++++ + src/pcm/pcm.c | 55 ++++++++++++++ + src/pcm/pcm_adpcm.c | 3 + src/pcm/pcm_alaw.c | 3 + src/pcm/pcm_copy.c | 3 + src/pcm/pcm_direct.c | 18 ++++ + src/pcm/pcm_direct.h | 10 ++ + src/pcm/pcm_dmix.c | 3 + src/pcm/pcm_dshare.c | 2 + src/pcm/pcm_dsnoop.c | 3 + src/pcm/pcm_file.c | 3 + src/pcm/pcm_generic.c | 18 ++++ + src/pcm/pcm_generic.h | 11 ++ + src/pcm/pcm_hooks.c | 3 + src/pcm/pcm_hw.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++ + src/pcm/pcm_iec958.c | 3 + src/pcm/pcm_ladspa.c | 3 + src/pcm/pcm_lfloat.c | 3 + src/pcm/pcm_linear.c | 3 + src/pcm/pcm_local.h | 3 + src/pcm/pcm_meter.c | 3 + src/pcm/pcm_mmap_emul.c | 3 + src/pcm/pcm_mulaw.c | 2 + src/pcm/pcm_rate.c | 3 + src/pcm/pcm_softvol.c | 3 + 27 files changed, 386 insertions(+) + +--- a/include/control.h ++++ b/include/control.h +@@ -182,6 +182,13 @@ typedef enum _snd_ctl_event_type { + /** 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 */ +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -474,6 +474,18 @@ int snd_pcm_wait(snd_pcm_t *pcm, int tim + 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); + + /* +--- a/include/sound/asound.h ++++ b/include/sound/asound.h +@@ -477,6 +477,31 @@ enum { + 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), +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7302,6 +7302,61 @@ OBSOLETE1(snd_pcm_sw_params_get_silence_ + + #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 + */ +--- a/src/pcm/pcm_adpcm.c ++++ b/src/pcm/pcm_adpcm.c +@@ -531,6 +531,9 @@ static const snd_pcm_ops_t snd_pcm_adpcm + .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, + }; + + /** +--- a/src/pcm/pcm_alaw.c ++++ b/src/pcm/pcm_alaw.c +@@ -404,6 +404,9 @@ static const snd_pcm_ops_t snd_pcm_alaw_ + .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, + }; + + /** +--- a/src/pcm/pcm_copy.c ++++ b/src/pcm/pcm_copy.c +@@ -165,6 +165,9 @@ static const snd_pcm_ops_t snd_pcm_copy_ + .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, + }; + + /** +--- a/src/pcm/pcm_direct.c ++++ b/src/pcm/pcm_direct.c +@@ -789,6 +789,24 @@ int snd_pcm_direct_munmap(snd_pcm_t *pcm + 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; +--- a/src/pcm/pcm_direct.h ++++ b/src/pcm/pcm_direct.h +@@ -235,6 +235,12 @@ struct snd_pcm_direct { + 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); + +@@ -290,6 +296,10 @@ void snd_pcm_direct_clear_timer_queue(sn + 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); + +--- a/src/pcm/pcm_dmix.c ++++ b/src/pcm/pcm_dmix.c +@@ -892,6 +892,9 @@ static const snd_pcm_ops_t snd_pcm_dmix_ + .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 = { +--- a/src/pcm/pcm_dshare.c ++++ b/src/pcm/pcm_dshare.c +@@ -573,6 +573,8 @@ static const snd_pcm_ops_t snd_pcm_dshar + .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 = { +--- a/src/pcm/pcm_dsnoop.c ++++ b/src/pcm/pcm_dsnoop.c +@@ -488,6 +488,9 @@ static const snd_pcm_ops_t snd_pcm_dsnoo + .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 = { +--- a/src/pcm/pcm_file.c ++++ b/src/pcm/pcm_file.c +@@ -669,6 +669,9 @@ static const snd_pcm_ops_t snd_pcm_file_ + .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 = { +--- a/src/pcm/pcm_generic.c ++++ b/src/pcm/pcm_generic.c +@@ -323,4 +323,22 @@ int snd_pcm_generic_munmap(snd_pcm_t *pc + 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 */ +--- a/src/pcm/pcm_generic.h ++++ b/src/pcm/pcm_generic.h +@@ -103,6 +103,12 @@ typedef struct { + 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); +@@ -149,3 +155,8 @@ int snd_pcm_generic_real_htimestamp(snd_ + 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); ++ ++ +--- a/src/pcm/pcm_hooks.c ++++ b/src/pcm/pcm_hooks.c +@@ -165,6 +165,9 @@ static const snd_pcm_ops_t snd_pcm_hooks + .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 = { +--- a/src/pcm/pcm_hw.c ++++ b/src/pcm/pcm_hw.c +@@ -1004,6 +1004,181 @@ 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) ++{ ++ 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; +@@ -1037,6 +1212,9 @@ static const snd_pcm_ops_t snd_pcm_hw_op + .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 = { +--- a/src/pcm/pcm_iec958.c ++++ b/src/pcm/pcm_iec958.c +@@ -429,6 +429,9 @@ static const snd_pcm_ops_t snd_pcm_iec95 + .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, + }; + + /** +--- a/src/pcm/pcm_ladspa.c ++++ b/src/pcm/pcm_ladspa.c +@@ -1084,6 +1084,9 @@ static const snd_pcm_ops_t snd_pcm_ladsp + .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, +--- a/src/pcm/pcm_lfloat.c ++++ b/src/pcm/pcm_lfloat.c +@@ -363,6 +363,9 @@ static const snd_pcm_ops_t snd_pcm_lfloa + .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, + }; + + /** +--- a/src/pcm/pcm_linear.c ++++ b/src/pcm/pcm_linear.c +@@ -435,6 +435,9 @@ static const snd_pcm_ops_t snd_pcm_linea + .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, + }; + + +--- a/src/pcm/pcm_local.h ++++ b/src/pcm/pcm_local.h +@@ -143,6 +143,9 @@ typedef struct { + 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 { +--- a/src/pcm/pcm_meter.c ++++ b/src/pcm/pcm_meter.c +@@ -514,6 +514,9 @@ static const snd_pcm_ops_t snd_pcm_meter + .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 = { +--- a/src/pcm/pcm_mmap_emul.c ++++ b/src/pcm/pcm_mmap_emul.c +@@ -368,6 +368,9 @@ static const snd_pcm_ops_t snd_pcm_mmap_ + .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 = { +--- a/src/pcm/pcm_mulaw.c ++++ b/src/pcm/pcm_mulaw.c +@@ -419,6 +419,8 @@ static const snd_pcm_ops_t snd_pcm_mulaw + .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, + }; + + /** +--- a/src/pcm/pcm_rate.c ++++ b/src/pcm/pcm_rate.c +@@ -1249,6 +1249,9 @@ static const snd_pcm_ops_t snd_pcm_rate_ + .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, + }; + + /** +--- a/src/pcm/pcm_softvol.c ++++ b/src/pcm/pcm_softvol.c +@@ -818,6 +818,9 @@ static const snd_pcm_ops_t snd_pcm_softv + .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, + }; + + /** diff --git a/0002-Implement-get_chmap-set_chmap-for-PCM-plug-route-and.patch b/0002-Implement-get_chmap-set_chmap-for-PCM-plug-route-and.patch new file mode 100644 index 0000000..1211991 --- /dev/null +++ b/0002-Implement-get_chmap-set_chmap-for-PCM-plug-route-and.patch @@ -0,0 +1,150 @@ +From 3fb013065fee01ba7ac7c64fa48149f0e124fe26 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 25 Jul 2012 15:36:16 +0200 +Subject: [PATCH 02/30] Implement get_chmap/set_chmap for PCM plug, route and + multi plugins + +Still incomplete implementations. The query and set ops are missing +for route and multi plugins. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_multi.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/pcm/pcm_plug.c | 3 ++ + src/pcm/pcm_route.c | 30 ++++++++++++++++++++++++++ + 3 files changed, 93 insertions(+) + +--- a/src/pcm/pcm_multi.c ++++ b/src/pcm/pcm_multi.c +@@ -739,6 +739,63 @@ static int snd_pcm_multi_mmap(snd_pcm_t + return 0; + } + ++static int *snd_pcm_multi_get_chmap(snd_pcm_t *pcm) ++{ ++ snd_pcm_multi_t *multi = pcm->private_data; ++ int *map; ++ unsigned int i, idx; ++ ++ map = malloc(pcm->channels + 4); ++ if (!map) ++ return NULL; ++ idx = 0; ++ for (i = 0; i < multi->slaves_count; ++i) { ++ int c, *slave_map; ++ slave_map = snd_pcm_get_chmap(multi->slaves[i].pcm); ++ if (!slave_map) { ++ free(map); ++ return NULL; ++ } ++ for (c = 0; c < *slave_map; c++) { ++ if (idx >= pcm->channels) ++ break; ++ map[idx++] = slave_map[c + 1]; ++ } ++ free(slave_map); ++ } ++ return map; ++} ++ ++static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const int *map) ++{ ++ snd_pcm_multi_t *multi = pcm->private_data; ++ unsigned int i, idx, chs; ++ int err; ++ ++ chs = *map; ++ if (chs != pcm->channels) ++ return -EINVAL; ++ map++; ++ for (i = 0; i < multi->slaves_count; ++i) { ++ int *slave_map; ++ unsigned int slave_chs; ++ slave_chs = multi->slaves[i].channels_count; ++ if (idx + slave_chs > chs) ++ break; ++ slave_map = malloc(slave_chs * 4 + 4); ++ if (!slave_map) ++ return -ENOMEM; ++ *slave_map = slave_chs; ++ memcpy(slave_map, map + idx, slave_chs * 4); ++ err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_map); ++ free(slave_map); ++ if (err < 0) ++ return err; ++ idx += slave_chs; ++ } ++ return 0; ++} ++ + static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out) + { + snd_pcm_multi_t *multi = pcm->private_data; +@@ -775,6 +832,9 @@ static const snd_pcm_ops_t snd_pcm_multi + .async = snd_pcm_multi_async, + .mmap = snd_pcm_multi_mmap, + .munmap = snd_pcm_multi_munmap, ++ .query_chmaps = NULL, /* NYI */ ++ .get_chmap = snd_pcm_multi_get_chmap, ++ .set_chmap = snd_pcm_multi_set_chmap, + }; + + static const snd_pcm_fast_ops_t snd_pcm_multi_fast_ops = { +--- a/src/pcm/pcm_plug.c ++++ b/src/pcm/pcm_plug.c +@@ -1084,6 +1084,9 @@ static const snd_pcm_ops_t snd_pcm_plug_ + .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, + }; + + /** +--- a/src/pcm/pcm_route.c ++++ b/src/pcm/pcm_route.c +@@ -703,6 +703,33 @@ snd_pcm_route_read_areas(snd_pcm_t *pcm, + return size; + } + ++static int *snd_pcm_route_get_chmap(snd_pcm_t *pcm) ++{ ++ snd_pcm_route_t *route = pcm->private_data; ++ int *map, *slave_map; ++ unsigned int src, dst; ++ ++ slave_map = snd_pcm_generic_get_chmap(pcm); ++ if (!slave_map) ++ return NULL; ++ map = calloc(4, route->schannels + 1); ++ if (!map) { ++ free(slave_map); ++ return NULL; ++ } ++ *map = route->schannels; ++ for (dst = 0; dst < route->params.ndsts; dst++) { ++ snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst]; ++ for (src = 0; src < d->nsrcs; src++) { ++ int c = d->srcs[src].channel; ++ if (c < route->schannels && !map[c + 1]) ++ map[c + 1] = slave_map[dst + 1]; ++ } ++ } ++ free(slave_map); ++ return map; ++} ++ + static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out) + { + snd_pcm_route_t *route = pcm->private_data; +@@ -760,6 +787,9 @@ static const snd_pcm_ops_t snd_pcm_route + .async = snd_pcm_generic_async, + .mmap = snd_pcm_generic_mmap, + .munmap = snd_pcm_generic_munmap, ++ .query_chmaps = NULL, /* NYI */ ++ .get_chmap = snd_pcm_route_get_chmap, ++ .set_chmap = NULL, /* NYI */ + }; + + static int route_load_ttable(snd_pcm_route_params_t *params, snd_pcm_stream_t stream, diff --git a/0003-Implement-get_chmap-set_chmap-for-PCM-extplug-ioplug.patch b/0003-Implement-get_chmap-set_chmap-for-PCM-extplug-ioplug.patch new file mode 100644 index 0000000..f7eedcd --- /dev/null +++ b/0003-Implement-get_chmap-set_chmap-for-PCM-extplug-ioplug.patch @@ -0,0 +1,179 @@ +From 662f79d4ec6b52bbaab28d5a9b60cc8bcdf042f9 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 25 Jul 2012 15:54:45 +0200 +Subject: [PATCH 03/30] Implement get_chmap/set_chmap for PCM extplug/ioplug + plugins + +Added the new ops for both external plugins, so the protocol numbers +are incremented. + +Signed-off-by: Takashi Iwai +--- + include/pcm_extplug.h | 14 +++++++++++++- + include/pcm_ioplug.h | 16 ++++++++++++++-- + src/pcm/pcm_extplug.c | 33 +++++++++++++++++++++++++++++++++ + src/pcm/pcm_ioplug.c | 33 +++++++++++++++++++++++++++++++++ + 4 files changed, 93 insertions(+), 3 deletions(-) + +--- a/include/pcm_extplug.h ++++ b/include/pcm_extplug.h +@@ -55,7 +55,7 @@ typedef struct snd_pcm_extplug_callback + */ + #define SND_PCM_EXTPLUG_VERSION_MAJOR 1 /**< Protocol major version */ + #define SND_PCM_EXTPLUG_VERSION_MINOR 0 /**< Protocol minor version */ +-#define SND_PCM_EXTPLUG_VERSION_TINY 1 /**< Protocol tiny version */ ++#define SND_PCM_EXTPLUG_VERSION_TINY 2 /**< Protocol tiny version */ + /** + * Filter-plugin protocol version + */ +@@ -151,6 +151,18 @@ struct snd_pcm_extplug_callback { + * init; optional initialization called at prepare or reset + */ + int (*init)(snd_pcm_extplug_t *ext); ++ /** ++ * query the channel maps; optional; since v1.0.2 ++ */ ++ int **(*query_chmaps)(snd_pcm_extplug_t *ext); ++ /** ++ * get the channel map; optional; since v1.0.2 ++ */ ++ int *(*get_chmap)(snd_pcm_extplug_t *ext); ++ /** ++ * set the channel map; optional; since v1.0.2 ++ */ ++ int (*set_chmap)(snd_pcm_extplug_t *ext, const int *map); + }; + + +--- a/include/pcm_ioplug.h ++++ b/include/pcm_ioplug.h +@@ -66,7 +66,7 @@ typedef struct snd_pcm_ioplug_callback s + */ + #define SND_PCM_IOPLUG_VERSION_MAJOR 1 /**< Protocol major version */ + #define SND_PCM_IOPLUG_VERSION_MINOR 0 /**< Protocol minor version */ +-#define SND_PCM_IOPLUG_VERSION_TINY 1 /**< Protocol tiny version */ ++#define SND_PCM_IOPLUG_VERSION_TINY 2 /**< Protocol tiny version */ + /** + * IO-plugin protocol version + */ +@@ -186,9 +186,21 @@ struct snd_pcm_ioplug_callback { + */ + void (*dump)(snd_pcm_ioplug_t *io, snd_output_t *out); + /** +- * get the delay for the running PCM; optional ++ * get the delay for the running PCM; optional; since v1.0.1 + */ + int (*delay)(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp); ++ /** ++ * query the channel maps; optional; since v1.0.2 ++ */ ++ int **(*query_chmaps)(snd_pcm_ioplug_t *io); ++ /** ++ * get the channel map; optional; since v1.0.2 ++ */ ++ int *(*get_chmap)(snd_pcm_ioplug_t *io); ++ /** ++ * set the channel map; optional; since v1.0.2 ++ */ ++ int (*set_chmap)(snd_pcm_ioplug_t *io, const int *map); + }; + + +--- a/src/pcm/pcm_extplug.c ++++ b/src/pcm/pcm_extplug.c +@@ -425,6 +425,36 @@ static int snd_pcm_extplug_close(snd_pcm + return 0; + } + ++static int **snd_pcm_extplug_query_chmaps(snd_pcm_t *pcm) ++{ ++ extplug_priv_t *ext = pcm->private_data; ++ ++ if (ext->data->version >= 0x010002 && ++ ext->data->callback->query_chmaps) ++ return ext->data->callback->query_chmaps(ext->data); ++ return snd_pcm_generic_query_chmaps(pcm); ++} ++ ++static int *snd_pcm_extplug_get_chmap(snd_pcm_t *pcm) ++{ ++ extplug_priv_t *ext = pcm->private_data; ++ ++ if (ext->data->version >= 0x010002 && ++ ext->data->callback->get_chmap) ++ return ext->data->callback->get_chmap(ext->data); ++ return snd_pcm_generic_get_chmap(pcm); ++} ++ ++static int snd_pcm_extplug_set_chmap(snd_pcm_t *pcm, const int *map) ++{ ++ extplug_priv_t *ext = pcm->private_data; ++ ++ if (ext->data->version >= 0x010002 && ++ ext->data->callback->set_chmap) ++ return ext->data->callback->set_chmap(ext->data, map); ++ return snd_pcm_generic_set_chmap(pcm, map); ++} ++ + static const snd_pcm_ops_t snd_pcm_extplug_ops = { + .close = snd_pcm_extplug_close, + .info = snd_pcm_generic_info, +@@ -438,6 +468,9 @@ static const snd_pcm_ops_t snd_pcm_extpl + .async = snd_pcm_generic_async, + .mmap = snd_pcm_generic_mmap, + .munmap = snd_pcm_generic_munmap, ++ .query_chmaps = snd_pcm_extplug_query_chmaps, ++ .get_chmap = snd_pcm_extplug_get_chmap, ++ .set_chmap = snd_pcm_extplug_set_chmap, + }; + + #endif /* !DOC_HIDDEN */ +--- a/src/pcm/pcm_ioplug.c ++++ b/src/pcm/pcm_ioplug.c +@@ -710,6 +710,36 @@ static int snd_pcm_ioplug_munmap(snd_pcm + return 0; + } + ++static int **snd_pcm_ioplug_query_chmaps(snd_pcm_t *pcm) ++{ ++ ioplug_priv_t *io = pcm->private_data; ++ ++ if (io->data->version >= 0x010002 && ++ io->data->callback->query_chmaps) ++ return io->data->callback->query_chmaps(io->data); ++ return NULL; ++} ++ ++static int *snd_pcm_ioplug_get_chmap(snd_pcm_t *pcm) ++{ ++ ioplug_priv_t *io = pcm->private_data; ++ ++ if (io->data->version >= 0x010002 && ++ io->data->callback->get_chmap) ++ return io->data->callback->get_chmap(io->data); ++ return NULL; ++} ++ ++static int snd_pcm_ioplug_set_chmap(snd_pcm_t *pcm, const int *map) ++{ ++ ioplug_priv_t *io = pcm->private_data; ++ ++ if (io->data->version >= 0x010002 && ++ io->data->callback->set_chmap) ++ return io->data->callback->set_chmap(io->data, map); ++ return -ENXIO; ++} ++ + static void snd_pcm_ioplug_dump(snd_pcm_t *pcm, snd_output_t *out) + { + ioplug_priv_t *io = pcm->private_data; +@@ -760,6 +790,9 @@ static const snd_pcm_ops_t snd_pcm_ioplu + .dump = snd_pcm_ioplug_dump, + .mmap = snd_pcm_ioplug_mmap, + .munmap = snd_pcm_ioplug_munmap, ++ .query_chmaps = snd_pcm_ioplug_query_chmaps, ++ .get_chmap = snd_pcm_ioplug_get_chmap, ++ .set_chmap = snd_pcm_ioplug_set_chmap, + }; + + static const snd_pcm_fast_ops_t snd_pcm_ioplug_fast_ops = { diff --git a/0004-Add-test-chmap-program.patch b/0004-Add-test-chmap-program.patch new file mode 100644 index 0000000..388d01a --- /dev/null +++ b/0004-Add-test-chmap-program.patch @@ -0,0 +1,288 @@ +From 3fc13d6f5b08edee49b106cd711d51bf3aef6ab7 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 30 Jul 2012 15:50:44 +0200 +Subject: [PATCH 04/30] Add test/chmap program + +Signed-off-by: Takashi Iwai +--- + test/Makefile.am | 3 + test/chmap.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 256 insertions(+), 1 deletion(-) + create mode 100644 test/chmap.c + +--- a/test/Makefile.am ++++ b/test/Makefile.am +@@ -2,7 +2,7 @@ SUBDIRS=. lsb + + check_PROGRAMS=control pcm pcm_min latency seq \ + playmidi1 timer rawmidi midiloop \ +- oldapi queue_timer namehint client_event_filter ++ oldapi queue_timer namehint client_event_filter chmap + + control_LDADD=../src/libasound.la + pcm_LDADD=../src/libasound.la +@@ -18,6 +18,7 @@ queue_timer_LDADD=../src/libasound.la + namehint_LDADD=../src/libasound.la + client_event_filter_LDADD=../src/libasound.la + code_CFLAGS=-Wall -pipe -g -O2 ++chmap_LDADD=../src/libasound.la + + INCLUDES=-I$(top_srcdir)/include + AM_CFLAGS=-Wall -pipe -g +--- /dev/null ++++ b/test/chmap.c +@@ -0,0 +1,254 @@ ++/* ++ * channel mapping API test program ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "../include/asoundlib.h" ++ ++static void usage(void) ++{ ++ printf("usage: chmap [options] query\n" ++ " chmap [options] get\n" ++ " chmap [options] set CH0 CH1 CH2...\n" ++ "options:\n" ++ " -D device Specify PCM device to handle\n" ++ " -f format PCM format\n" ++ " -c channels Channels\n" ++ " -r rate Sample rate\n"); ++} ++ ++static const char * const chname[] = { ++ "Unknown", ++ "FL", "FC", "FR", ++ "FLC", "FRC", "RL", "RC", "RR", ++ "RLC", "RRC", "SL", "SR", "LFE", ++ "FLW", "FRW", "FLH", ++ "FCH", "FCH", "FRH", ++ "TC" ++}; ++ ++#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) ++ ++static void print_channels(int channels, int *map) ++{ ++ int i; ++ printf(" "); ++ for (i = 0; i < channels; i++) { ++ unsigned int c = *map++; ++ if (c >= ARRAY_SIZE(chname)) ++ printf(" Ch%d", c); ++ else ++ printf(" %s", chname[c]); ++ } ++ printf("\n"); ++} ++ ++static int to_channel(const char *name) ++{ ++ unsigned int i; ++ ++ if (isdigit(*name)) ++ return atoi(name); ++ for (i = 0; i < ARRAY_SIZE(chname); i++) ++ if (!strcmp(chname[i], name)) ++ return i; ++ return 0; ++} ++ ++static const char *chmap_type(int type) ++{ ++ switch (type) { ++ case SND_CHMAP_NONE: ++ return "None"; ++ case SND_CHMAP_FIXED: ++ return "Fixed"; ++ case SND_CHMAP_VAR: ++ return "Variable"; ++ case SND_CHMAP_PAIRED: ++ return "Paired"; ++ default: ++ return "Unknown"; ++ } ++} ++ ++static int query_chmaps(snd_pcm_t *pcm) ++{ ++ int **maps = snd_pcm_query_chmaps(pcm); ++ int **p, *v; ++ ++ if (!maps) { ++ printf("Cannot query maps\n"); ++ return 1; ++ } ++ for (p = maps; (v = *p) != NULL; p++) { ++ printf("Type = %s, Channels = %d\n", chmap_type(v[0]), v[1]); ++ print_channels(v[1], v + 2); ++ } ++ snd_pcm_free_chmaps(maps); ++ return 0; ++} ++ ++static int setup_pcm(snd_pcm_t *pcm, int format, int channels, int rate) ++{ ++ snd_pcm_hw_params_t *params; ++ ++ snd_pcm_hw_params_alloca(¶ms); ++ if (snd_pcm_hw_params_any(pcm, params) < 0) { ++ printf("Cannot init hw_params\n"); ++ return -1; ++ } ++ if (format != SND_PCM_FORMAT_UNKNOWN) { ++ if (snd_pcm_hw_params_set_format(pcm, params, format) < 0) { ++ printf("Cannot set format %s\n", ++ snd_pcm_format_name(format)); ++ return -1; ++ } ++ } ++ if (channels > 0) { ++ if (snd_pcm_hw_params_set_channels(pcm, params, channels) < 0) { ++ printf("Cannot set channels %d\n", channels); ++ return -1; ++ } ++ } ++ if (rate > 0) { ++ if (snd_pcm_hw_params_set_rate_near(pcm, params, (unsigned int *)&rate, 0) < 0) { ++ printf("Cannot set rate %d\n", rate); ++ return -1; ++ } ++ } ++ if (snd_pcm_hw_params(pcm, params) < 0) { ++ printf("Cannot set hw_params\n"); ++ return -1; ++ } ++ return 0; ++} ++ ++static int get_chmap(snd_pcm_t *pcm, int format, int channels, int rate) ++{ ++ int *map; ++ ++ if (setup_pcm(pcm, format, channels, rate)) ++ return 1; ++ map = snd_pcm_get_chmap(pcm); ++ if (!map) { ++ printf("Cannot get chmap\n"); ++ return 1; ++ } ++ printf("Channels = %d\n", *map); ++ print_channels(*map, map + 1); ++ free(map); ++ return 0; ++} ++ ++static int set_chmap(snd_pcm_t *pcm, int format, int channels, int rate, ++ int nargs, char **arg) ++{ ++ int i; ++ int *map; ++ ++ if (channels && channels != nargs) { ++ printf("Inconsistent channels %d vs %d\n", channels, nargs); ++ return 1; ++ } ++ if (!channels) { ++ if (!nargs) { ++ printf("No channels are given\n"); ++ return 1; ++ } ++ channels = nargs; ++ } ++ if (setup_pcm(pcm, format, channels, rate)) ++ return 1; ++ map = malloc(sizeof(int) * channels + 1); ++ if (!map) { ++ printf("cannot malloc\n"); ++ return 1; ++ } ++ *map = channels; ++ for (i = 0; i < channels; i++) ++ map[i + 1] = to_channel(arg[i]); ++ if (snd_pcm_set_chmap(pcm, map) < 0) { ++ printf("Cannot set chmap\n"); ++ return 1; ++ } ++ free(map); ++ ++ map = snd_pcm_get_chmap(pcm); ++ if (!map) { ++ printf("Cannot get chmap\n"); ++ return 1; ++ } ++ printf("Get channels = %d\n", *map); ++ print_channels(*map, map + 1); ++ free(map); ++ return 0; ++} ++ ++int main(int argc, char **argv) ++{ ++ char *device = NULL; ++ int stream = SND_PCM_STREAM_PLAYBACK; ++ int format = SND_PCM_FORMAT_UNKNOWN; ++ int channels = 0; ++ int rate = 0; ++ snd_pcm_t *pcm; ++ int c; ++ ++ while ((c = getopt(argc, argv, "D:s:f:c:r:")) != -1) { ++ switch (c) { ++ case 'D': ++ device = optarg; ++ break; ++ case 's': ++ if (*optarg == 'c' || *optarg == 'C') ++ stream = SND_PCM_STREAM_CAPTURE; ++ else ++ stream = SND_PCM_STREAM_PLAYBACK; ++ break; ++ case 'f': ++ format = snd_pcm_format_value(optarg); ++ break; ++ case 'c': ++ channels = atoi(optarg); ++ break; ++ case 'r': ++ rate = atoi(optarg); ++ break; ++ default: ++ usage(); ++ return 1; ++ } ++ } ++ ++ if (argc <= optind) { ++ usage(); ++ return 1; ++ } ++ ++ if (!device) { ++ printf("No device is specified\n"); ++ return 1; ++ } ++ ++ if (snd_pcm_open(&pcm, device, stream, SND_PCM_NONBLOCK) < 0) { ++ printf("Cannot open PCM stream %s for %s\n", device, ++ snd_pcm_stream_name(stream)); ++ return 1; ++ } ++ ++ switch (*argv[optind]) { ++ case 'q': ++ return query_chmaps(pcm); ++ case 'g': ++ return get_chmap(pcm, format, channels, rate); ++ case 's': ++ return set_chmap(pcm, format, channels, rate, ++ argc - optind - 1, argv + optind + 1); ++ } ++ usage(); ++ return 1; ++} diff --git a/0005-Cache-the-chmap-operation-errors.patch b/0005-Cache-the-chmap-operation-errors.patch new file mode 100644 index 0000000..07f0a97 --- /dev/null +++ b/0005-Cache-the-chmap-operation-errors.patch @@ -0,0 +1,188 @@ +From 34f6545520de73be55ee6c29a7ebd3c016fa9f06 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 30 Jul 2012 18:21:43 +0200 +Subject: [PATCH 05/30] Cache the chmap operation errors + +... not to retry the same error again. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_hw.c | 66 +++++++++++++++++++++++++++++++++++++++++++++---------- + 1 file changed, 54 insertions(+), 12 deletions(-) + +--- a/src/pcm/pcm_hw.c ++++ b/src/pcm/pcm_hw.c +@@ -105,6 +105,8 @@ typedef struct { + snd_pcm_format_t format; + int rate; + int channels; ++ /* for chmap */ ++ unsigned int chmap_caps; + } snd_pcm_hw_t; + + #define SNDRV_FILE_PCM_STREAM_PLAYBACK ALSA_DEVICE_DIRECTORY "pcmC%iD%ip" +@@ -1022,6 +1024,27 @@ static int is_chmap_type(int type) + 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; ++} ++ ++static void chmap_caps_set_ok(snd_pcm_hw_t *hw, int type) ++{ ++ hw->chmap_caps |= (1 << type); ++} ++ ++static void chmap_caps_set_error(snd_pcm_hw_t *hw, int type) ++{ ++ hw->chmap_caps |= (1 << (type + 8)); ++} ++ + static int **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm) + { + snd_pcm_hw_t *hw = pcm->private_data; +@@ -1031,10 +1054,13 @@ static int **snd_pcm_hw_query_chmaps(snd + int **map; + int i, ret, nums; + ++ if (!chmap_caps(hw, CHMAP_CTL_QUERY)) ++ return NULL; ++ + ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); + if (ret < 0) { + SYSMSG("Cannot open the associated CTL\n"); +- return NULL; ++ goto error; + } + + snd_ctl_elem_id_alloca(&id); +@@ -1043,7 +1069,7 @@ static int **snd_pcm_hw_query_chmaps(snd + snd_ctl_close(ctl); + if (ret < 0) { + SYSMSG("Cannot read Channel Map TLV\n"); +- return NULL; ++ goto error; + } + + #if 0 +@@ -1053,7 +1079,7 @@ static int **snd_pcm_hw_query_chmaps(snd + if (tlv[0] != SND_CTL_TLVT_CONTAINER) { + if (!is_chmap_type(tlv[0])) { + SYSMSG("Invalid TLV type %d\n", tlv[0]); +- return NULL; ++ goto error; + } + start = tlv; + nums = 1; +@@ -1066,7 +1092,7 @@ static int **snd_pcm_hw_query_chmaps(snd + for (p = start; size > 0; ) { + if (!is_chmap_type(p[0])) { + SYSMSG("Invalid TLV type %d\n", p[0]); +- return NULL; ++ goto error; + } + nums++; + size -= p[1] + 8; +@@ -1078,19 +1104,20 @@ static int **snd_pcm_hw_query_chmaps(snd + return NULL; + for (i = 0; i < nums; i++) { + map[i] = malloc(start[1] + 8); +- if (!map[i]) +- goto nomem; ++ if (!map[i]) { ++ snd_pcm_free_chmaps(map); ++ return NULL; ++ } + 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; + } ++ chmap_caps_set_ok(hw, CHMAP_CTL_QUERY); + return map; + +- nomem: +- for (; i >= 0; i--) +- free(map[i]); +- free(map); ++ error: ++ chmap_caps_set_error(hw, CHMAP_CTL_QUERY); + return NULL; + } + +@@ -1104,6 +1131,9 @@ static int *snd_pcm_hw_get_chmap(snd_pcm + unsigned int i; + int ret; + ++ if (!chmap_caps(hw, CHMAP_CTL_GET)) ++ return NULL; ++ + switch (FAST_PCM_STATE(hw)) { + case SNDRV_PCM_STATE_PREPARED: + case SNDRV_PCM_STATE_RUNNING: +@@ -1125,6 +1155,7 @@ static int *snd_pcm_hw_get_chmap(snd_pcm + if (ret < 0) { + free(map); + SYSMSG("Cannot open the associated CTL\n"); ++ chmap_caps_set_error(hw, CHMAP_CTL_GET); + return NULL; + } + snd_ctl_elem_value_alloca(&val); +@@ -1132,15 +1163,16 @@ static int *snd_pcm_hw_get_chmap(snd_pcm + fill_chmap_ctl_id(pcm, id); + snd_ctl_elem_value_set_id(val, id); + ret = snd_ctl_elem_read(ctl, val); ++ snd_ctl_close(ctl); + if (ret < 0) { +- snd_ctl_close(ctl); + free(map); + SYSMSG("Cannot read Channel Map ctl\n"); ++ chmap_caps_set_error(hw, CHMAP_CTL_GET); + return NULL; + } + for (i = 0; i < pcm->channels; i++) + map[i + 1] = snd_ctl_elem_value_get_integer(val, i); +- snd_ctl_close(ctl); ++ chmap_caps_set_ok(hw, CHMAP_CTL_GET); + return map; + } + +@@ -1152,6 +1184,9 @@ static int snd_pcm_hw_set_chmap(snd_pcm_ + snd_ctl_elem_value_t *val; + int i, ret; + ++ if (!chmap_caps(hw, CHMAP_CTL_SET)) ++ return -ENXIO; ++ + if (*map < 0 || *map > 128) { + SYSMSG("Invalid number of channels %d\n", *map); + return -EINVAL; +@@ -1164,6 +1199,7 @@ static int snd_pcm_hw_set_chmap(snd_pcm_ + ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); + if (ret < 0) { + SYSMSG("Cannot open the associated CTL\n"); ++ chmap_caps_set_error(hw, CHMAP_CTL_SET); + return ret; + } + snd_ctl_elem_id_alloca(&id); +@@ -1174,6 +1210,12 @@ static int snd_pcm_hw_set_chmap(snd_pcm_ + 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) ++ chmap_caps_set_ok(hw, CHMAP_CTL_SET); ++ else if (ret == -ENOENT || ret == -EPERM || ret == -ENXIO) { ++ chmap_caps_set_error(hw, CHMAP_CTL_SET); ++ ret = -ENXIO; ++ } + if (ret < 0) + SYSMSG("Cannot write Channel Map ctl\n"); + return ret; diff --git a/0006-Define-channel-map-position-enum-in-pcm.h.patch b/0006-Define-channel-map-position-enum-in-pcm.h.patch new file mode 100644 index 0000000..63d0f28 --- /dev/null +++ b/0006-Define-channel-map-position-enum-in-pcm.h.patch @@ -0,0 +1,56 @@ +From 915b26bfe8af876d467a37c336c6bf22d5e370fe Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 21 Aug 2012 12:16:39 +0200 +Subject: [PATCH 06/30] Define channel map position enum in pcm.h + +The original definition is in sound/asound.h, but we need to export to +alsa-lib users, too. + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 28 +++++++++++++++++++++++++++- + 1 file changed, 27 insertions(+), 1 deletion(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -474,13 +474,39 @@ int snd_pcm_wait(snd_pcm_t *pcm, int tim + int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2); + int snd_pcm_unlink(snd_pcm_t *pcm); + +-enum { ++/** channel map list type */ ++enum snd_pcm_chmap_type { + 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 */ + }; + ++/** channel positions */ ++enum snd_pcm_chmap_position { ++ SND_CHMAP_UNKNOWN = 0, /** unspecified */ ++ SND_CHMAP_FL, /** front left */ ++ SND_CHMAP_FC, /** front center */ ++ SND_CHMAP_FR, /** front right */ ++ SND_CHMAP_FLC, /** front left center */ ++ SND_CHMAP_FRC, /* front right center */ ++ SND_CHMAP_RL, /** rear left */ ++ SND_CHMAP_RC, /** rear center */ ++ SND_CHMAP_RR, /** rear right */ ++ SND_CHMAP_RLC, /** rear left center */ ++ SND_CHMAP_RRC, /** rear right center */ ++ SND_CHMAP_SL, /** side left */ ++ SND_CHMAP_SR, /** side right */ ++ SND_CHMAP_LFE, /** LFE */ ++ SND_CHMAP_FLW, /** front left wide */ ++ SND_CHMAP_FRW, /** front right wide */ ++ SND_CHMAP_FLH, /** front left high */ ++ SND_CHMAP_FCH, /** front center high */ ++ SND_CHMAP_FRH, /** front right high */ ++ SND_CHMAP_TC, /** top center */ ++ SND_CHMAP_LAST = SND_CHMAP_TC, /** last entry */ ++}; ++ + 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); diff --git a/0007-Follow-channel-position-definitions-to-mixer-channel.patch b/0007-Follow-channel-position-definitions-to-mixer-channel.patch new file mode 100644 index 0000000..ea67002 --- /dev/null +++ b/0007-Follow-channel-position-definitions-to-mixer-channel.patch @@ -0,0 +1,110 @@ +From 0f36270dd315b9baf3a93f5586776b91c0bcf589 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 21 Aug 2012 15:07:44 +0200 +Subject: [PATCH 07/30] Follow channel position definitions to mixer channel + in mixer.h + +mixer.h already contains some channel position definitions. +To be more consistent over all systems, better to follow the same +order for the new channel map, too. But since UNKNOWN channel must be +zero but the definition in mixer.h contains -1 as UNKNOWN, simply +shift the value with 1. + +If the conversion is required between SND_CHMAP and SND_MIXER_SCHN, +just increment/decrement 1. Eventually I'll provide helper functions +for that... + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 14 +++++++------- + include/sound/asound.h | 16 +++++++++------- + test/chmap.c | 11 ++++------- + 3 files changed, 20 insertions(+), 21 deletions(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -486,18 +486,18 @@ enum snd_pcm_chmap_type { + enum snd_pcm_chmap_position { + SND_CHMAP_UNKNOWN = 0, /** unspecified */ + SND_CHMAP_FL, /** front left */ +- SND_CHMAP_FC, /** front center */ + SND_CHMAP_FR, /** front right */ +- SND_CHMAP_FLC, /** front left center */ +- SND_CHMAP_FRC, /* front right center */ + SND_CHMAP_RL, /** rear left */ +- SND_CHMAP_RC, /** rear center */ + SND_CHMAP_RR, /** rear right */ +- SND_CHMAP_RLC, /** rear left center */ +- SND_CHMAP_RRC, /** rear right center */ ++ SND_CHMAP_FC, /** front center */ ++ SND_CHMAP_LFE, /** LFE */ + SND_CHMAP_SL, /** side left */ + SND_CHMAP_SR, /** side right */ +- SND_CHMAP_LFE, /** LFE */ ++ SND_CHMAP_RC, /** rear center */ ++ SND_CHMAP_FLC, /** front left center */ ++ SND_CHMAP_FRC, /** front right center */ ++ SND_CHMAP_RLC, /** rear left center */ ++ SND_CHMAP_RRC, /** rear right center */ + SND_CHMAP_FLW, /** front left wide */ + SND_CHMAP_FRW, /** front right wide */ + SND_CHMAP_FLH, /** front left high */ +--- a/include/sound/asound.h ++++ b/include/sound/asound.h +@@ -479,20 +479,22 @@ enum { + + /* channel positions */ + enum { ++ /* this follows the alsa-lib mixer channel value + 1*/ + 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_FC, /* front center */ ++ SNDRV_CHMAP_LFE, /* LFE */ + SNDRV_CHMAP_SL, /* side left */ + SNDRV_CHMAP_SR, /* side right */ +- SNDRV_CHMAP_LFE, /* LFE */ ++ SNDRV_CHMAP_RC, /* rear center */ ++ /* new definitions */ ++ SNDRV_CHMAP_FLC, /* front left center */ ++ SNDRV_CHMAP_FRC, /* front right center */ ++ SNDRV_CHMAP_RLC, /* rear left center */ ++ SNDRV_CHMAP_RRC, /* rear right center */ + SNDRV_CHMAP_FLW, /* front left wide */ + SNDRV_CHMAP_FRW, /* front right wide */ + SNDRV_CHMAP_FLH, /* front left high */ +--- a/test/chmap.c ++++ b/test/chmap.c +@@ -23,12 +23,9 @@ static void usage(void) + + static const char * const chname[] = { + "Unknown", +- "FL", "FC", "FR", +- "FLC", "FRC", "RL", "RC", "RR", +- "RLC", "RRC", "SL", "SR", "LFE", +- "FLW", "FRW", "FLH", +- "FCH", "FCH", "FRH", +- "TC" ++ "FL", "FR", "RL", "RR", "FC", "LFE", "SL", "SR", "RC", ++ "FLC", "FRC", "RLC", "RRC", "FLW", "FRW", "FLH", ++ "FCH", "FCH", "FRH", "TC" + }; + + #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +@@ -56,7 +53,7 @@ static int to_channel(const char *name) + for (i = 0; i < ARRAY_SIZE(chname); i++) + if (!strcmp(chname[i], name)) + return i; +- return 0; ++ return SND_CHMAP_UNKNOWN; + } + + static const char *chmap_type(int type) diff --git a/0008-Add-SND_CHMAP_NA-and-bit-flag-definitions.patch b/0008-Add-SND_CHMAP_NA-and-bit-flag-definitions.patch new file mode 100644 index 0000000..d994bf8 --- /dev/null +++ b/0008-Add-SND_CHMAP_NA-and-bit-flag-definitions.patch @@ -0,0 +1,88 @@ +From 48c2c90f19f07d02082d9fb84fb36ac1fcfa84e1 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 31 Aug 2012 13:53:22 -0700 +Subject: [PATCH 08/30] Add SND_CHMAP_NA and bit flag definitions + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 7 ++++++- + include/sound/asound.h | 9 +++++++-- + test/chmap.c | 14 ++++++++++---- + 3 files changed, 23 insertions(+), 7 deletions(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -504,9 +504,14 @@ enum snd_pcm_chmap_position { + SND_CHMAP_FCH, /** front center high */ + SND_CHMAP_FRH, /** front right high */ + SND_CHMAP_TC, /** top center */ +- SND_CHMAP_LAST = SND_CHMAP_TC, /** last entry */ ++ SND_CHMAP_NA, /** N/A, silent */ ++ SND_CHMAP_LAST = SND_CHMAP_NA, /** last entry */ + }; + ++#define SND_CHMAP_POSITION_MASK 0xffff /** bitmask for channel position */ ++#define SND_CHMAP_PHASE_INVERSE (0x01 << 16) /* the channel is phase inverted */ ++#define SND_CHMAP_DRIVER_SPEC (0x02 << 16) /* non-standard channel value */ ++ + 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); +--- a/include/sound/asound.h ++++ b/include/sound/asound.h +@@ -479,7 +479,7 @@ enum { + + /* channel positions */ + enum { +- /* this follows the alsa-lib mixer channel value + 1*/ ++ /* this follows the alsa-lib mixer channel value + 1 */ + SNDRV_CHMAP_UNKNOWN = 0, + SNDRV_CHMAP_FL, /* front left */ + SNDRV_CHMAP_FR, /* front right */ +@@ -501,9 +501,14 @@ enum { + SNDRV_CHMAP_FCH, /* front center high */ + SNDRV_CHMAP_FRH, /* front right high */ + SNDRV_CHMAP_TC, /* top center */ +- SNDRV_CHMAP_LAST = SNDRV_CHMAP_TC, ++ SNDRV_CHMAP_NA, /* N/A, silent */ ++ SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA, + }; + ++#define SNDRV_CHMAP_POSITION_MASK 0xffff ++#define SNDRV_CHMAP_PHASE_INVERSE (0x01 << 16) ++#define SNDRV_CHMAP_DRIVER_SPEC (0x02 << 16) ++ + enum { + SNDRV_PCM_IOCTL_PVERSION = _IOR('A', 0x00, int), + SNDRV_PCM_IOCTL_INFO = _IOR('A', 0x01, struct sndrv_pcm_info), +--- a/test/chmap.c ++++ b/test/chmap.c +@@ -25,7 +25,8 @@ static const char * const chname[] = { + "Unknown", + "FL", "FR", "RL", "RR", "FC", "LFE", "SL", "SR", "RC", + "FLC", "FRC", "RLC", "RRC", "FLW", "FRW", "FLH", +- "FCH", "FCH", "FRH", "TC" ++ "FCH", "FCH", "FRH", "TC", ++ "N/A", + }; + + #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +@@ -36,10 +37,15 @@ static void print_channels(int channels, + printf(" "); + for (i = 0; i < channels; i++) { + unsigned int c = *map++; +- if (c >= ARRAY_SIZE(chname)) +- printf(" Ch%d", c); ++ unsigned int pos = c & SND_CHMAP_POSITION_MASK; ++ if (c & SND_CHMAP_DRIVER_SPEC) ++ printf(" %d", p); ++ else if (p >= ARRAY_SIZE(chname)) ++ printf(" Ch%d", p); + else +- printf(" %s", chname[c]); ++ printf(" %s", chname[p]); ++ if (c & SND_CHMAP_PHASE_INVERSE) ++ printf("[INV]"); + } + printf("\n"); + } diff --git a/0009-PCM-Introduce-snd_pcm_chmap_t-and-snd_pcm_chmap_quer.patch b/0009-PCM-Introduce-snd_pcm_chmap_t-and-snd_pcm_chmap_quer.patch new file mode 100644 index 0000000..c4c1e61 --- /dev/null +++ b/0009-PCM-Introduce-snd_pcm_chmap_t-and-snd_pcm_chmap_quer.patch @@ -0,0 +1,587 @@ +From 9c1a0ce72d71e4728d45dcd3986dd0ef0201dd67 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 4 Sep 2012 17:26:43 +0200 +Subject: [PATCH 09/30] PCM: Introduce snd_pcm_chmap_t and + snd_pcm_chmap_query_t + +Instead of passing ambiguous integer array, define snd_pcm_chmap_t and +snd_pcm_chmap_query_t so that user can understand more easily which +element is for what. + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 21 +++++++++++++++++---- + include/pcm_extplug.h | 6 +++--- + include/pcm_ioplug.h | 6 +++--- + src/pcm/pcm.c | 10 +++++----- + src/pcm/pcm_direct.c | 6 +++--- + src/pcm/pcm_direct.h | 6 +++--- + src/pcm/pcm_extplug.c | 6 +++--- + src/pcm/pcm_generic.c | 6 +++--- + src/pcm/pcm_generic.h | 6 +++--- + src/pcm/pcm_hw.c | 31 ++++++++++++++++--------------- + src/pcm/pcm_ioplug.c | 6 +++--- + src/pcm/pcm_local.h | 6 +++--- + src/pcm/pcm_multi.c | 25 ++++++++++++++----------- + src/pcm/pcm_route.c | 10 +++++----- + test/chmap.c | 35 ++++++++++++++++++----------------- + 15 files changed, 102 insertions(+), 84 deletions(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -512,10 +512,23 @@ enum snd_pcm_chmap_position { + #define SND_CHMAP_PHASE_INVERSE (0x01 << 16) /* the channel is phase inverted */ + #define SND_CHMAP_DRIVER_SPEC (0x02 << 16) /* non-standard channel value */ + +-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); ++/** the channel map header */ ++typedef struct snd_pcm_chmap { ++ unsigned int channels; ++ unsigned int pos[0]; ++} snd_pcm_chmap_t; ++ ++/** the header of array items returned from snd_pcm_query_chmaps() */ ++typedef struct snd_pcm_chmap_query { ++ enum snd_pcm_chmap_type type; ++ snd_pcm_chmap_t map; ++} snd_pcm_chmap_query_t; ++ ++ ++snd_pcm_chmap_query_t **snd_pcm_query_chmaps(snd_pcm_t *pcm); ++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); + + //int snd_pcm_mixer_element(snd_pcm_t *pcm, snd_mixer_t *mixer, snd_mixer_elem_t **elem); + +--- a/include/pcm_extplug.h ++++ b/include/pcm_extplug.h +@@ -154,15 +154,15 @@ struct snd_pcm_extplug_callback { + /** + * query the channel maps; optional; since v1.0.2 + */ +- int **(*query_chmaps)(snd_pcm_extplug_t *ext); ++ snd_pcm_chmap_query_t **(*query_chmaps)(snd_pcm_extplug_t *ext); + /** + * get the channel map; optional; since v1.0.2 + */ +- int *(*get_chmap)(snd_pcm_extplug_t *ext); ++ snd_pcm_chmap_t *(*get_chmap)(snd_pcm_extplug_t *ext); + /** + * set the channel map; optional; since v1.0.2 + */ +- int (*set_chmap)(snd_pcm_extplug_t *ext, const int *map); ++ int (*set_chmap)(snd_pcm_extplug_t *ext, const snd_pcm_chmap_t *map); + }; + + +--- a/include/pcm_ioplug.h ++++ b/include/pcm_ioplug.h +@@ -192,15 +192,15 @@ struct snd_pcm_ioplug_callback { + /** + * query the channel maps; optional; since v1.0.2 + */ +- int **(*query_chmaps)(snd_pcm_ioplug_t *io); ++ snd_pcm_chmap_query_t **(*query_chmaps)(snd_pcm_ioplug_t *io); + /** + * get the channel map; optional; since v1.0.2 + */ +- int *(*get_chmap)(snd_pcm_ioplug_t *io); ++ snd_pcm_chmap_t *(*get_chmap)(snd_pcm_ioplug_t *io); + /** + * set the channel map; optional; since v1.0.2 + */ +- int (*set_chmap)(snd_pcm_ioplug_t *io, const int *map); ++ int (*set_chmap)(snd_pcm_ioplug_t *io, const snd_pcm_chmap_t *map); + }; + + +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7310,7 +7310,7 @@ OBSOLETE1(snd_pcm_sw_params_get_silence_ + * 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) ++snd_pcm_chmap_query_t **snd_pcm_query_chmaps(snd_pcm_t *pcm) + { + if (!pcm->ops->query_chmaps) + return NULL; +@@ -7321,9 +7321,9 @@ int **snd_pcm_query_chmaps(snd_pcm_t *pc + * \!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) ++void snd_pcm_free_chmaps(snd_pcm_chmap_query_t **maps) + { +- int **p; ++ snd_pcm_chmap_query_t **p = maps; + if (!maps) + return; + for (p = maps; *p; p++) +@@ -7336,7 +7336,7 @@ void snd_pcm_free_chmaps(int **maps) + * \param pcm PCM instance + * \return the current channel map, or NULL if error + */ +-int *snd_pcm_get_chmap(snd_pcm_t *pcm) ++snd_pcm_chmap_t *snd_pcm_get_chmap(snd_pcm_t *pcm) + { + if (!pcm->ops->get_chmap) + return NULL; +@@ -7349,7 +7349,7 @@ int *snd_pcm_get_chmap(snd_pcm_t *pcm) + * \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) ++int snd_pcm_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) + { + if (!pcm->ops->set_chmap) + return -ENXIO; +--- a/src/pcm/pcm_direct.c ++++ b/src/pcm/pcm_direct.c +@@ -789,19 +789,19 @@ int snd_pcm_direct_munmap(snd_pcm_t *pcm + return 0; + } + +-int **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm) ++snd_pcm_chmap_query_t **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_chmap_t *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) ++int snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) + { + snd_pcm_direct_t *dmix = pcm->private_data; + return snd_pcm_set_chmap(dmix->spcm, map); +--- a/src/pcm/pcm_direct.h ++++ b/src/pcm/pcm_direct.h +@@ -296,9 +296,9 @@ void snd_pcm_direct_clear_timer_queue(sn + 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); ++snd_pcm_chmap_query_t **snd_pcm_direct_query_chmaps(snd_pcm_t *pcm); ++snd_pcm_chmap_t *snd_pcm_direct_get_chmap(snd_pcm_t *pcm); ++int snd_pcm_direct_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *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); +--- a/src/pcm/pcm_extplug.c ++++ b/src/pcm/pcm_extplug.c +@@ -425,7 +425,7 @@ static int snd_pcm_extplug_close(snd_pcm + return 0; + } + +-static int **snd_pcm_extplug_query_chmaps(snd_pcm_t *pcm) ++static snd_pcm_chmap_query_t **snd_pcm_extplug_query_chmaps(snd_pcm_t *pcm) + { + extplug_priv_t *ext = pcm->private_data; + +@@ -435,7 +435,7 @@ static int **snd_pcm_extplug_query_chmap + return snd_pcm_generic_query_chmaps(pcm); + } + +-static int *snd_pcm_extplug_get_chmap(snd_pcm_t *pcm) ++static snd_pcm_chmap_t *snd_pcm_extplug_get_chmap(snd_pcm_t *pcm) + { + extplug_priv_t *ext = pcm->private_data; + +@@ -445,7 +445,7 @@ static int *snd_pcm_extplug_get_chmap(sn + return snd_pcm_generic_get_chmap(pcm); + } + +-static int snd_pcm_extplug_set_chmap(snd_pcm_t *pcm, const int *map) ++static int snd_pcm_extplug_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) + { + extplug_priv_t *ext = pcm->private_data; + +--- a/src/pcm/pcm_generic.c ++++ b/src/pcm/pcm_generic.c +@@ -323,19 +323,19 @@ int snd_pcm_generic_munmap(snd_pcm_t *pc + return 0; + } + +-int **snd_pcm_generic_query_chmaps(snd_pcm_t *pcm) ++snd_pcm_chmap_query_t **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_chmap_t *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) ++int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) + { + snd_pcm_generic_t *generic = pcm->private_data; + return snd_pcm_set_chmap(generic->slave, map); +--- a/src/pcm/pcm_generic.h ++++ b/src/pcm/pcm_generic.h +@@ -155,8 +155,8 @@ int snd_pcm_generic_real_htimestamp(snd_ + 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); ++snd_pcm_chmap_query_t **snd_pcm_generic_query_chmaps(snd_pcm_t *pcm); ++snd_pcm_chmap_t *snd_pcm_generic_get_chmap(snd_pcm_t *pcm); ++int snd_pcm_generic_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map); + + +--- a/src/pcm/pcm_hw.c ++++ b/src/pcm/pcm_hw.c +@@ -1045,13 +1045,13 @@ static void chmap_caps_set_error(snd_pcm + hw->chmap_caps |= (1 << (type + 8)); + } + +-static int **snd_pcm_hw_query_chmaps(snd_pcm_t *pcm) ++static snd_pcm_chmap_query_t **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; ++ snd_pcm_chmap_query_t **map; + int i, ret, nums; + + if (!chmap_caps(hw, CHMAP_CTL_QUERY)) +@@ -1108,9 +1108,9 @@ static int **snd_pcm_hw_query_chmaps(snd + snd_pcm_free_chmaps(map); + return NULL; + } +- map[i][0] = start[0] - 0x100; +- map[i][1] = start[1] / 4; +- memcpy(map[i] + 2, start + 2, start[1]); ++ map[i]->type = start[0] - 0x100; ++ map[i]->map.channels = start[1] / 4; ++ memcpy(map[i]->map.pos, start + 2, start[1]); + start += start[1] / 4 + 2; + } + chmap_caps_set_ok(hw, CHMAP_CTL_QUERY); +@@ -1121,10 +1121,10 @@ static int **snd_pcm_hw_query_chmaps(snd + return NULL; + } + +-static int *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) ++static snd_pcm_chmap_t *snd_pcm_hw_get_chmap(snd_pcm_t *pcm) + { + snd_pcm_hw_t *hw = pcm->private_data; +- int *map; ++ snd_pcm_chmap_t *map; + snd_ctl_t *ctl; + snd_ctl_elem_id_t *id; + snd_ctl_elem_value_t *val; +@@ -1150,7 +1150,7 @@ static int *snd_pcm_hw_get_chmap(snd_pcm + map = malloc(pcm->channels + 1); + if (!map) + return NULL; +- *map = pcm->channels; ++ map->channels = pcm->channels; + ret = snd_ctl_hw_open(&ctl, NULL, hw->card, 0); + if (ret < 0) { + free(map); +@@ -1171,24 +1171,25 @@ static int *snd_pcm_hw_get_chmap(snd_pcm + return NULL; + } + for (i = 0; i < pcm->channels; i++) +- map[i + 1] = snd_ctl_elem_value_get_integer(val, i); ++ map->pos[i] = snd_ctl_elem_value_get_integer(val, i); + chmap_caps_set_ok(hw, CHMAP_CTL_GET); + return map; + } + +-static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const int *map) ++static int snd_pcm_hw_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *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; ++ unsigned int i; ++ int ret; + + if (!chmap_caps(hw, CHMAP_CTL_SET)) + return -ENXIO; + +- if (*map < 0 || *map > 128) { +- SYSMSG("Invalid number of channels %d\n", *map); ++ if (map->channels > 128) { ++ SYSMSG("Invalid number of channels %d\n", map->channels); + return -EINVAL; + } + if (FAST_PCM_STATE(hw) != SNDRV_PCM_STATE_PREPARED) { +@@ -1206,8 +1207,8 @@ static int snd_pcm_hw_set_chmap(snd_pcm_ + 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]); ++ for (i = 0; i < map->channels; i++) ++ snd_ctl_elem_value_set_integer(val, i, map->pos[i]); + ret = snd_ctl_elem_write(ctl, val); + snd_ctl_close(ctl); + if (ret >= 0) +--- a/src/pcm/pcm_ioplug.c ++++ b/src/pcm/pcm_ioplug.c +@@ -710,7 +710,7 @@ static int snd_pcm_ioplug_munmap(snd_pcm + return 0; + } + +-static int **snd_pcm_ioplug_query_chmaps(snd_pcm_t *pcm) ++static snd_pcm_chmap_query_t **snd_pcm_ioplug_query_chmaps(snd_pcm_t *pcm) + { + ioplug_priv_t *io = pcm->private_data; + +@@ -720,7 +720,7 @@ static int **snd_pcm_ioplug_query_chmaps + return NULL; + } + +-static int *snd_pcm_ioplug_get_chmap(snd_pcm_t *pcm) ++static snd_pcm_chmap_t *snd_pcm_ioplug_get_chmap(snd_pcm_t *pcm) + { + ioplug_priv_t *io = pcm->private_data; + +@@ -730,7 +730,7 @@ static int *snd_pcm_ioplug_get_chmap(snd + return NULL; + } + +-static int snd_pcm_ioplug_set_chmap(snd_pcm_t *pcm, const int *map) ++static int snd_pcm_ioplug_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) + { + ioplug_priv_t *io = pcm->private_data; + +--- a/src/pcm/pcm_local.h ++++ b/src/pcm/pcm_local.h +@@ -143,9 +143,9 @@ typedef struct { + 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_chmap_query_t **(*query_chmaps)(snd_pcm_t *pcm); ++ snd_pcm_chmap_t *(*get_chmap)(snd_pcm_t *pcm); ++ int (*set_chmap)(snd_pcm_t *pcm, const snd_pcm_chmap_t *map); + } snd_pcm_ops_t; + + typedef struct { +--- a/src/pcm/pcm_multi.c ++++ b/src/pcm/pcm_multi.c +@@ -739,10 +739,10 @@ static int snd_pcm_multi_mmap(snd_pcm_t + return 0; + } + +-static int *snd_pcm_multi_get_chmap(snd_pcm_t *pcm) ++static snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm) + { + snd_pcm_multi_t *multi = pcm->private_data; +- int *map; ++ snd_pcm_chmap_t *map; + unsigned int i, idx; + + map = malloc(pcm->channels + 4); +@@ -750,34 +750,37 @@ static int *snd_pcm_multi_get_chmap(snd_ + return NULL; + idx = 0; + for (i = 0; i < multi->slaves_count; ++i) { +- int c, *slave_map; ++ unsigned int c; ++ snd_pcm_chmap_t *slave_map; + slave_map = snd_pcm_get_chmap(multi->slaves[i].pcm); + if (!slave_map) { + free(map); + return NULL; + } +- for (c = 0; c < *slave_map; c++) { ++ for (c = 0; c < slave_map->channels; c++) { + if (idx >= pcm->channels) + break; +- map[idx++] = slave_map[c + 1]; ++ map->pos[idx++] = slave_map->pos[c]; + } + free(slave_map); + } + return map; + } + +-static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const int *map) ++static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) + { + snd_pcm_multi_t *multi = pcm->private_data; ++ const unsigned int *pos; + unsigned int i, idx, chs; + int err; + +- chs = *map; ++ chs = map->channels; + if (chs != pcm->channels) + return -EINVAL; +- map++; ++ pos = map->pos; ++ idx = 0; + for (i = 0; i < multi->slaves_count; ++i) { +- int *slave_map; ++ snd_pcm_chmap_t *slave_map; + unsigned int slave_chs; + slave_chs = multi->slaves[i].channels_count; + if (idx + slave_chs > chs) +@@ -785,8 +788,8 @@ static int snd_pcm_multi_set_chmap(snd_p + slave_map = malloc(slave_chs * 4 + 4); + if (!slave_map) + return -ENOMEM; +- *slave_map = slave_chs; +- memcpy(slave_map, map + idx, slave_chs * 4); ++ slave_map->channels = slave_chs; ++ memcpy(slave_map->pos, pos + idx, slave_chs * 4); + err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_map); + free(slave_map); + if (err < 0) +--- a/src/pcm/pcm_route.c ++++ b/src/pcm/pcm_route.c +@@ -703,10 +703,10 @@ snd_pcm_route_read_areas(snd_pcm_t *pcm, + return size; + } + +-static int *snd_pcm_route_get_chmap(snd_pcm_t *pcm) ++static snd_pcm_chmap_t *snd_pcm_route_get_chmap(snd_pcm_t *pcm) + { + snd_pcm_route_t *route = pcm->private_data; +- int *map, *slave_map; ++ snd_pcm_chmap_t *map, *slave_map; + unsigned int src, dst; + + slave_map = snd_pcm_generic_get_chmap(pcm); +@@ -717,13 +717,13 @@ static int *snd_pcm_route_get_chmap(snd_ + free(slave_map); + return NULL; + } +- *map = route->schannels; ++ map->channels = route->schannels; + for (dst = 0; dst < route->params.ndsts; dst++) { + snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst]; + for (src = 0; src < d->nsrcs; src++) { + int c = d->srcs[src].channel; +- if (c < route->schannels && !map[c + 1]) +- map[c + 1] = slave_map[dst + 1]; ++ if (c < route->schannels && !map->pos[c]) ++ map->pos[c] = slave_map->pos[dst]; + } + } + free(slave_map); +--- a/test/chmap.c ++++ b/test/chmap.c +@@ -31,13 +31,14 @@ static const char * const chname[] = { + + #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +-static void print_channels(int channels, int *map) ++static void print_channels(const snd_pcm_chmap_t *map) + { +- int i; ++ unsigned int i; ++ + printf(" "); +- for (i = 0; i < channels; i++) { +- unsigned int c = *map++; +- unsigned int pos = c & SND_CHMAP_POSITION_MASK; ++ for (i = 0; i < map->channels; i++) { ++ unsigned int c = map->pos[i]; ++ unsigned int p = c & SND_CHMAP_POSITION_MASK; + if (c & SND_CHMAP_DRIVER_SPEC) + printf(" %d", p); + else if (p >= ARRAY_SIZE(chname)) +@@ -80,16 +81,16 @@ static const char *chmap_type(int type) + + static int query_chmaps(snd_pcm_t *pcm) + { +- int **maps = snd_pcm_query_chmaps(pcm); +- int **p, *v; ++ snd_pcm_chmap_query_t **maps = snd_pcm_query_chmaps(pcm); ++ snd_pcm_chmap_query_t **p, *v; + + if (!maps) { + printf("Cannot query maps\n"); + return 1; + } + for (p = maps; (v = *p) != NULL; p++) { +- printf("Type = %s, Channels = %d\n", chmap_type(v[0]), v[1]); +- print_channels(v[1], v + 2); ++ printf("Type = %s, Channels = %d\n", chmap_type(v->type), v->map.channels); ++ print_channels(&v->map); + } + snd_pcm_free_chmaps(maps); + return 0; +@@ -132,7 +133,7 @@ static int setup_pcm(snd_pcm_t *pcm, int + + static int get_chmap(snd_pcm_t *pcm, int format, int channels, int rate) + { +- int *map; ++ snd_pcm_chmap_t *map; + + if (setup_pcm(pcm, format, channels, rate)) + return 1; +@@ -141,8 +142,8 @@ static int get_chmap(snd_pcm_t *pcm, int + printf("Cannot get chmap\n"); + return 1; + } +- printf("Channels = %d\n", *map); +- print_channels(*map, map + 1); ++ printf("Channels = %d\n", map->channels); ++ print_channels(map); + free(map); + return 0; + } +@@ -151,7 +152,7 @@ static int set_chmap(snd_pcm_t *pcm, int + int nargs, char **arg) + { + int i; +- int *map; ++ snd_pcm_chmap_t *map; + + if (channels && channels != nargs) { + printf("Inconsistent channels %d vs %d\n", channels, nargs); +@@ -171,9 +172,9 @@ static int set_chmap(snd_pcm_t *pcm, int + printf("cannot malloc\n"); + return 1; + } +- *map = channels; ++ map->channels = channels; + for (i = 0; i < channels; i++) +- map[i + 1] = to_channel(arg[i]); ++ map->pos[i] = to_channel(arg[i]); + if (snd_pcm_set_chmap(pcm, map) < 0) { + printf("Cannot set chmap\n"); + return 1; +@@ -185,8 +186,8 @@ static int set_chmap(snd_pcm_t *pcm, int + printf("Cannot get chmap\n"); + return 1; + } +- printf("Get channels = %d\n", *map); +- print_channels(*map, map + 1); ++ printf("Get channels = %d\n", map->channels); ++ print_channels(map); + free(map); + return 0; + } diff --git a/0010-PCM-Implement-snd_pcm_query_chmaps_from_hw.patch b/0010-PCM-Implement-snd_pcm_query_chmaps_from_hw.patch new file mode 100644 index 0000000..8ce024a --- /dev/null +++ b/0010-PCM-Implement-snd_pcm_query_chmaps_from_hw.patch @@ -0,0 +1,207 @@ +From 01dc0e6825b5620510faaefaef40ff8cfb692b6e Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +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 +--- + 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) diff --git a/0011-Fix-duplicated-channel-entry-in-test-chmap.c.patch b/0011-Fix-duplicated-channel-entry-in-test-chmap.c.patch new file mode 100644 index 0000000..d6c6b82 --- /dev/null +++ b/0011-Fix-duplicated-channel-entry-in-test-chmap.c.patch @@ -0,0 +1,21 @@ +From ec96740d9919b5e0c050a4b91aebee4b1dbb8298 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 7 Sep 2012 14:31:39 +0200 +Subject: [PATCH 11/30] Fix duplicated channel entry in test/chmap.c + +Signed-off-by: Takashi Iwai +--- + test/chmap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/test/chmap.c ++++ b/test/chmap.c +@@ -25,7 +25,7 @@ static const char * const chname[] = { + "Unknown", + "FL", "FR", "RL", "RR", "FC", "LFE", "SL", "SR", "RC", + "FLC", "FRC", "RLC", "RRC", "FLW", "FRW", "FLH", +- "FCH", "FCH", "FRH", "TC", ++ "FCH", "FRH", "TC", + "N/A", + }; + diff --git a/0012-PCM-Fix-prefix-for-snd_pcm_chmap_type-enum-members.patch b/0012-PCM-Fix-prefix-for-snd_pcm_chmap_type-enum-members.patch new file mode 100644 index 0000000..8f9b29f --- /dev/null +++ b/0012-PCM-Fix-prefix-for-snd_pcm_chmap_type-enum-members.patch @@ -0,0 +1,32 @@ +From 6950a1030c4f878b51104268e3cd13401d56bf60 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 10 Sep 2012 16:59:36 +0200 +Subject: [PATCH 12/30] PCM: Fix prefix for snd_pcm_chmap_type enum members + +Add _TYPE prefix to distinguish from the channel position. +Also add SND_CHMAP_TYPE_LAST entry pointing the last one like other +enums. + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -476,10 +476,11 @@ int snd_pcm_unlink(snd_pcm_t *pcm); + + /** channel map list type */ + enum snd_pcm_chmap_type { +- 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 */ ++ SND_CHMAP_TYPE_NONE = 0,/** unspecified channel position */ ++ SND_CHMAP_TYPE_FIXED, /** fixed channel position */ ++ SND_CHMAP_TYPE_VAR, /** freely swappable channel position */ ++ SND_CHMAP_TYPE_PAIRED, /** pair-wise swappable channel position */ ++ SND_CHMAP_TYPE_LAST = SND_CHMAP_TYPE_PAIRED, /** last entry */ + }; + + /** channel positions */ diff --git a/0013-PCM-Add-string-conversion-helper-functions-for-chmap.patch b/0013-PCM-Add-string-conversion-helper-functions-for-chmap.patch new file mode 100644 index 0000000..0f91b81 --- /dev/null +++ b/0013-PCM-Add-string-conversion-helper-functions-for-chmap.patch @@ -0,0 +1,286 @@ +From b4c64e815a051e711798be1d772f123d19c1ff6e Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 10 Sep 2012 18:07:36 +0200 +Subject: [PATCH 13/30] PCM: Add string conversion helper functions for chmap + +Added a few helper functions between chmap and string. + snd_pcm_chmap_type_name() -- a string of the given chmap type + snd_pcm_chmap_name() -- a string of the given channel position + snd_pcm_chmap_print() -- print channel map on the given buffer + snd_pcm_chmap_from_string() -- get a channel position from string + snd_pcm_parse_string() -- parse the whole channel map from string + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 6 ++ + src/pcm/pcm.c | 142 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + test/chmap.c | 69 ++++------------------------ + 3 files changed, 160 insertions(+), 57 deletions(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -534,6 +534,12 @@ void snd_pcm_free_chmaps(snd_pcm_chmap_q + 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); + ++const char *snd_pcm_chmap_type_name(enum snd_pcm_chmap_type val); ++const char *snd_pcm_chmap_name(enum snd_pcm_chmap_position val); ++int snd_pcm_chmap_print(const snd_pcm_chmap_t *map, size_t maxlen, char *buf); ++unsigned int snd_pcm_chmap_from_string(const char *str); ++snd_pcm_chmap_t *snd_pcm_chmap_parse_string(const char *str); ++ + //int snd_pcm_mixer_element(snd_pcm_t *pcm, snd_mixer_t *mixer, snd_mixer_elem_t **elem); + + /* +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -633,6 +633,7 @@ playback devices. + #include + #include + #include ++#include + #include + #include + #include +@@ -7356,6 +7357,147 @@ int snd_pcm_set_chmap(snd_pcm_t *pcm, co + return pcm->ops->set_chmap(pcm, map); + } + ++/* ++ */ ++#define _NAME(n) [SND_CHMAP_TYPE_##n] = #n ++static const char *chmap_type_names[SND_CHMAP_TYPE_LAST + 1] = { ++ _NAME(NONE), _NAME(FIXED), _NAME(VAR), _NAME(PAIRED), ++}; ++#undef _NAME ++ ++const char *snd_pcm_chmap_type_name(enum snd_pcm_chmap_type val) ++{ ++ if (val <= SND_CHMAP_TYPE_LAST) ++ return chmap_type_names[val]; ++ else ++ return NULL; ++} ++ ++#define _NAME(n) [SND_CHMAP_##n] = #n ++static const char *chmap_names[SND_CHMAP_LAST + 1] = { ++ _NAME(UNKNOWN), ++ _NAME(FL), _NAME(FR), ++ _NAME(RL), _NAME(RR), ++ _NAME(FC), _NAME(LFE), ++ _NAME(SL), _NAME(SR), ++ _NAME(RC), _NAME(FLC), _NAME(FRC), _NAME(RLC), _NAME(RRC), ++ _NAME(FLW), _NAME(FRW), ++ _NAME(FLH), _NAME(FCH), _NAME(FRH), _NAME(TC), ++ _NAME(NA), ++}; ++#undef _NAME ++ ++const char *snd_pcm_chmap_name(enum snd_pcm_chmap_position val) ++{ ++ if (val <= SND_CHMAP_LAST) ++ return chmap_names[val]; ++ else ++ return NULL; ++} ++ ++int snd_pcm_chmap_print(const snd_pcm_chmap_t *map, size_t maxlen, char *buf) ++{ ++ unsigned int i, len = 0; ++ ++ for (i = 0; i < map->channels; i++) { ++ unsigned int p = map->pos[i] & SND_CHMAP_POSITION_MASK; ++ if (i > 0) { ++ len += snprintf(buf + len, maxlen - len, " "); ++ if (len >= maxlen) ++ return -ENOMEM; ++ } ++ if (map->pos[i] & SND_CHMAP_DRIVER_SPEC) ++ len += snprintf(buf + len, maxlen, "%d", p); ++ else { ++ const char *name = chmap_names[p]; ++ if (name) ++ len += snprintf(buf + len, maxlen - len, ++ "%s", name); ++ else ++ len += snprintf(buf + len, maxlen - len, ++ "Ch%d", p); ++ } ++ if (len >= maxlen) ++ return -ENOMEM; ++ if (map->pos[i] & SND_CHMAP_PHASE_INVERSE) { ++ len += snprintf(buf + len, maxlen - len, "[INV]"); ++ if (len >= maxlen) ++ return -ENOMEM; ++ } ++ } ++ return len; ++} ++ ++static int str_to_chmap(const char *str, int len) ++{ ++ int val; ++ ++ if (isdigit(*str)) { ++ val = atoi(str); ++ if (val < 0) ++ return -1; ++ return val | SND_CHMAP_DRIVER_SPEC; ++ } else if (str[0] == 'C' && str[1] == 'h') { ++ val = atoi(str + 2); ++ if (val < 0) ++ return -1; ++ return val; ++ } else { ++ for (val = 0; val <= SND_CHMAP_LAST; val++) { ++ if (!strncmp(str, chmap_names[val], len)) ++ return val; ++ } ++ return -1; ++ } ++} ++ ++unsigned int snd_pcm_chmap_from_string(const char *str) ++{ ++ return str_to_chmap(str, strlen(str)); ++} ++ ++snd_pcm_chmap_t *snd_pcm_chmap_parse_string(const char *str) ++{ ++ int i, ch = 0; ++ int tmp_map[64]; ++ snd_pcm_chmap_t *map; ++ ++ for (;;) { ++ const char *p; ++ int len, val; ++ ++ if (ch >= (int)(sizeof(tmp_map) / sizeof(tmp_map[0]))) ++ return NULL; ++ for (p = str; *p && isalnum(*p); p++) ++ ; ++ len = p - str; ++ if (!len) ++ return NULL; ++ val = str_to_chmap(str, len); ++ if (val < 0) ++ return NULL; ++ str += len; ++ if (*str == '[') { ++ if (!strncmp(str, "[INV]", 5)) { ++ val |= SND_CHMAP_PHASE_INVERSE; ++ str += 5; ++ } ++ } ++ tmp_map[ch] = val; ++ ch++; ++ for (; *str && !isalnum(*str); str++) ++ ; ++ if (!*str) ++ break; ++ } ++ map = malloc(sizeof(*map) + ch * sizeof(int)); ++ if (!map) ++ return NULL; ++ map->channels = ch; ++ for (i = 0; i < ch; i++) ++ map->pos[i] = tmp_map[i]; ++ return map; ++} + + /* + * basic helpers +--- a/test/chmap.c ++++ b/test/chmap.c +@@ -21,62 +21,11 @@ static void usage(void) + " -r rate Sample rate\n"); + } + +-static const char * const chname[] = { +- "Unknown", +- "FL", "FR", "RL", "RR", "FC", "LFE", "SL", "SR", "RC", +- "FLC", "FRC", "RLC", "RRC", "FLW", "FRW", "FLH", +- "FCH", "FRH", "TC", +- "N/A", +-}; +- +-#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) +- + static void print_channels(const snd_pcm_chmap_t *map) + { +- unsigned int i; +- +- printf(" "); +- for (i = 0; i < map->channels; i++) { +- unsigned int c = map->pos[i]; +- unsigned int p = c & SND_CHMAP_POSITION_MASK; +- if (c & SND_CHMAP_DRIVER_SPEC) +- printf(" %d", p); +- else if (p >= ARRAY_SIZE(chname)) +- printf(" Ch%d", p); +- else +- printf(" %s", chname[p]); +- if (c & SND_CHMAP_PHASE_INVERSE) +- printf("[INV]"); +- } +- printf("\n"); +-} +- +-static int to_channel(const char *name) +-{ +- unsigned int i; +- +- if (isdigit(*name)) +- return atoi(name); +- for (i = 0; i < ARRAY_SIZE(chname); i++) +- if (!strcmp(chname[i], name)) +- return i; +- return SND_CHMAP_UNKNOWN; +-} +- +-static const char *chmap_type(int type) +-{ +- switch (type) { +- case SND_CHMAP_NONE: +- return "None"; +- case SND_CHMAP_FIXED: +- return "Fixed"; +- case SND_CHMAP_VAR: +- return "Variable"; +- case SND_CHMAP_PAIRED: +- return "Paired"; +- default: +- return "Unknown"; +- } ++ char tmp[128]; ++ if (snd_pcm_chmap_print(map, sizeof(tmp), tmp) > 0) ++ printf(" %s\n", tmp); + } + + static int query_chmaps(snd_pcm_t *pcm) +@@ -89,7 +38,9 @@ static int query_chmaps(snd_pcm_t *pcm) + return 1; + } + for (p = maps; (v = *p) != NULL; p++) { +- printf("Type = %s, Channels = %d\n", chmap_type(v->type), v->map.channels); ++ printf("Type = %s, Channels = %d\n", ++ snd_pcm_chmap_type_name(v->type), ++ v->map.channels); + print_channels(&v->map); + } + snd_pcm_free_chmaps(maps); +@@ -173,8 +124,12 @@ static int set_chmap(snd_pcm_t *pcm, int + return 1; + } + map->channels = channels; +- for (i = 0; i < channels; i++) +- map->pos[i] = to_channel(arg[i]); ++ for (i = 0; i < channels; i++) { ++ int val = snd_pcm_chmap_from_string(arg[i]); ++ if (val < 0) ++ val = SND_CHMAP_UNKNOWN; ++ map->pos[i] = val; ++ } + if (snd_pcm_set_chmap(pcm, map) < 0) { + printf("Cannot set chmap\n"); + return 1; diff --git a/0014-PCM-Add-SND_CHMAP_API_VERSION-definition.patch b/0014-PCM-Add-SND_CHMAP_API_VERSION-definition.patch new file mode 100644 index 0000000..a7ba6d9 --- /dev/null +++ b/0014-PCM-Add-SND_CHMAP_API_VERSION-definition.patch @@ -0,0 +1,25 @@ +From fe81684745e5c121e2d110bf6955e3f2858d2c7c Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 11 Sep 2012 11:33:31 +0200 +Subject: [PATCH 14/30] PCM: Add SND_CHMAP_API_VERSION definition + +Just to make it easier for apps to support chmap conditionally via +simple ifdefs. + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -474,6 +474,9 @@ int snd_pcm_wait(snd_pcm_t *pcm, int tim + int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2); + int snd_pcm_unlink(snd_pcm_t *pcm); + ++/** channel mapping API version number */ ++#define SND_CHMAP_API_VERSION ((1 << 16) | (0 << 8) | 0) ++ + /** channel map list type */ + enum snd_pcm_chmap_type { + SND_CHMAP_TYPE_NONE = 0,/** unspecified channel position */ diff --git a/0015-PCM-Add-snd_pcm_chmap_long_name.patch b/0015-PCM-Add-snd_pcm_chmap_long_name.patch new file mode 100644 index 0000000..af3b0c5 --- /dev/null +++ b/0015-PCM-Add-snd_pcm_chmap_long_name.patch @@ -0,0 +1,65 @@ +From 25f3259b48e921886bda58075936de3d53b84bc1 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Tue, 11 Sep 2012 12:48:46 +0200 +Subject: [PATCH 15/30] PCM: Add snd_pcm_chmap_long_name() + +Just return a more verbose name than snd_pcm_chmap_name(), but +including white spaces. + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 1 + + src/pcm/pcm.c | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 33 insertions(+) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -539,6 +539,7 @@ int snd_pcm_set_chmap(snd_pcm_t *pcm, co + + const char *snd_pcm_chmap_type_name(enum snd_pcm_chmap_type val); + const char *snd_pcm_chmap_name(enum snd_pcm_chmap_position val); ++const char *snd_pcm_chmap_long_name(enum snd_pcm_chmap_position val); + int snd_pcm_chmap_print(const snd_pcm_chmap_t *map, size_t maxlen, char *buf); + unsigned int snd_pcm_chmap_from_string(const char *str); + snd_pcm_chmap_t *snd_pcm_chmap_parse_string(const char *str); +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7395,6 +7395,38 @@ const char *snd_pcm_chmap_name(enum snd_ + return NULL; + } + ++static const char *chmap_long_names[SND_CHMAP_LAST + 1] = { ++ [SND_CHMAP_UNKNOWN] = "Unknown", ++ [SND_CHMAP_FL] = "Front Left", ++ [SND_CHMAP_FR] = "Front Right", ++ [SND_CHMAP_RL] = "Rear Left", ++ [SND_CHMAP_RR] = "Rear Right", ++ [SND_CHMAP_FC] = "Front Center", ++ [SND_CHMAP_LFE] = "LFE", ++ [SND_CHMAP_SL] = "Side Left", ++ [SND_CHMAP_SR] = "Side Right", ++ [SND_CHMAP_RC] = "Rear Center", ++ [SND_CHMAP_FLC] = "Front Left Center", ++ [SND_CHMAP_FRC] = "Front Right Center", ++ [SND_CHMAP_RLC] = "Rear Left Center", ++ [SND_CHMAP_RRC] = "Rear Right Center", ++ [SND_CHMAP_FLW] = "Front Left Wide", ++ [SND_CHMAP_FRW] = "Front Right Wide", ++ [SND_CHMAP_FLH] = "Front Left High", ++ [SND_CHMAP_FCH] = "Front Center High", ++ [SND_CHMAP_FRH] = "Front Right High", ++ [SND_CHMAP_TC] = "Top Center", ++ [SND_CHMAP_NA] = "Unused", ++}; ++ ++const char *snd_pcm_chmap_long_name(enum snd_pcm_chmap_position val) ++{ ++ if (val <= SND_CHMAP_LAST) ++ return chmap_long_names[val]; ++ else ++ return NULL; ++} ++ + int snd_pcm_chmap_print(const snd_pcm_chmap_t *map, size_t maxlen, char *buf) + { + unsigned int i, len = 0; diff --git a/0016-PCM-Add-query_chmaps-support-to-multi-plugin.patch b/0016-PCM-Add-query_chmaps-support-to-multi-plugin.patch new file mode 100644 index 0000000..188ada4 --- /dev/null +++ b/0016-PCM-Add-query_chmaps-support-to-multi-plugin.patch @@ -0,0 +1,198 @@ +From 81135aac49c8a3b547181d44f78ffe2594db5dbe Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 12:56:14 +0200 +Subject: [PATCH 16/30] PCM: Add query_chmaps support to multi plugin + +Also fix some bugs in get_chmap(). + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_multi.c | 152 +++++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 111 insertions(+), 41 deletions(-) + +--- a/src/pcm/pcm_multi.c ++++ b/src/pcm/pcm_multi.c +@@ -739,64 +739,134 @@ static int snd_pcm_multi_mmap(snd_pcm_t + return 0; + } + ++static snd_pcm_chmap_query_t **snd_pcm_multi_query_chmaps(snd_pcm_t *pcm) ++{ ++ snd_pcm_multi_t *multi = pcm->private_data; ++ snd_pcm_chmap_query_t **slave_maps[multi->slaves_count]; ++ snd_pcm_chmap_query_t **maps; ++ unsigned int i; ++ int err = -ENOMEM; ++ ++ memset(slave_maps, 0, sizeof(slave_maps)); ++ maps = calloc(2, sizeof(*maps)); ++ if (!maps) ++ return NULL; ++ maps[0] = calloc(multi->channels_count + 2, sizeof(int *)); ++ if (!maps[0]) ++ goto error; ++ maps[0]->type = SND_CHMAP_TYPE_FIXED; ++ maps[0]->map.channels = multi->channels_count; ++ ++ for (i = 0; i < multi->slaves_count; i++) { ++ slave_maps[i] = snd_pcm_query_chmaps(multi->slaves[i].pcm); ++ if (!slave_maps[i]) ++ goto error; ++ } ++ ++ for (i = 0; i < multi->channels_count; i++) { ++ snd_pcm_multi_channel_t *bind = &multi->channels[i]; ++ unsigned int slave_channels = ++ multi->slaves[bind->slave_idx].channels_count; ++ snd_pcm_chmap_query_t **p; ++ ++ for (p = slave_maps[bind->slave_idx]; *p; p++) { ++ if ((*p)->map.channels == slave_channels) { ++ maps[0]->map.pos[i] = ++ (*p)->map.pos[bind->slave_channel]; ++ break; ++ } ++ } ++ } ++ err = 0; ++ ++ error: ++ for (i = 0; i < multi->slaves_count; i++) { ++ if (slave_maps[i]) ++ snd_pcm_free_chmaps(slave_maps[i]); ++ } ++ ++ if (err) { ++ snd_pcm_free_chmaps(maps); ++ return NULL; ++ } ++ ++ return maps; ++} ++ + static snd_pcm_chmap_t *snd_pcm_multi_get_chmap(snd_pcm_t *pcm) + { + snd_pcm_multi_t *multi = pcm->private_data; + snd_pcm_chmap_t *map; +- unsigned int i, idx; ++ snd_pcm_chmap_t *slave_maps[multi->slaves_count]; ++ unsigned int i; ++ int err = -ENOMEM; + +- map = malloc(pcm->channels + 4); ++ memset(slave_maps, 0, sizeof(slave_maps)); ++ map = calloc(multi->channels_count + 1, sizeof(int)); + if (!map) + return NULL; +- idx = 0; +- for (i = 0; i < multi->slaves_count; ++i) { +- unsigned int c; +- snd_pcm_chmap_t *slave_map; +- slave_map = snd_pcm_get_chmap(multi->slaves[i].pcm); +- if (!slave_map) { +- free(map); +- return NULL; +- } +- for (c = 0; c < slave_map->channels; c++) { +- if (idx >= pcm->channels) +- break; +- map->pos[idx++] = slave_map->pos[c]; +- } +- free(slave_map); ++ ++ for (i = 0; i < multi->slaves_count; i++) { ++ slave_maps[i] = snd_pcm_get_chmap(multi->slaves[i].pcm); ++ if (!slave_maps[i]) ++ goto error; + } ++ ++ map->channels = multi->channels_count; ++ for (i = 0; i < multi->channels_count; i++) { ++ snd_pcm_multi_channel_t *bind = &multi->channels[i]; ++ map->pos[i] = slave_maps[bind->slave_idx]->pos[bind->slave_channel]; ++ } ++ err = 0; ++ ++ error: ++ for (i = 0; i < multi->slaves_count; i++) ++ free(slave_maps[i]); ++ ++ if (err) { ++ free(map); ++ return NULL; ++ } ++ + return map; + } + + static int snd_pcm_multi_set_chmap(snd_pcm_t *pcm, const snd_pcm_chmap_t *map) + { + snd_pcm_multi_t *multi = pcm->private_data; +- const unsigned int *pos; +- unsigned int i, idx, chs; +- int err; ++ snd_pcm_chmap_t *slave_maps[multi->slaves_count]; ++ unsigned int i; ++ int err = 0; + +- chs = map->channels; +- if (chs != pcm->channels) ++ if (map->channels != multi->channels_count) + return -EINVAL; +- pos = map->pos; +- idx = 0; +- for (i = 0; i < multi->slaves_count; ++i) { +- snd_pcm_chmap_t *slave_map; +- unsigned int slave_chs; +- slave_chs = multi->slaves[i].channels_count; +- if (idx + slave_chs > chs) +- break; +- slave_map = malloc(slave_chs * 4 + 4); +- if (!slave_map) +- return -ENOMEM; +- slave_map->channels = slave_chs; +- memcpy(slave_map->pos, pos + idx, slave_chs * 4); +- err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_map); +- free(slave_map); ++ ++ for (i = 0; i < multi->slaves_count; i++) { ++ slave_maps[i] = calloc(multi->slaves[i].channels_count + 1, ++ sizeof(int)); ++ if (!slave_maps[i]) { ++ err = -ENOMEM; ++ goto error; ++ } ++ } ++ ++ for (i = 0; i < multi->channels_count; i++) { ++ snd_pcm_multi_channel_t *bind = &multi->channels[i]; ++ slave_maps[bind->slave_idx]->pos[bind->slave_channel] = ++ map->pos[i]; ++ } ++ ++ for (i = 0; i < multi->slaves_count; i++) { ++ err = snd_pcm_set_chmap(multi->slaves[i].pcm, slave_maps[i]); + if (err < 0) +- return err; +- idx += slave_chs; ++ goto error; + } +- return 0; ++ ++ error: ++ for (i = 0; i < multi->slaves_count; i++) ++ free(slave_maps[i]); ++ ++ return err; + } + + static void snd_pcm_multi_dump(snd_pcm_t *pcm, snd_output_t *out) +@@ -835,7 +905,7 @@ static const snd_pcm_ops_t snd_pcm_multi + .async = snd_pcm_multi_async, + .mmap = snd_pcm_multi_mmap, + .munmap = snd_pcm_multi_munmap, +- .query_chmaps = NULL, /* NYI */ ++ .query_chmaps = snd_pcm_multi_query_chmaps, + .get_chmap = snd_pcm_multi_get_chmap, + .set_chmap = snd_pcm_multi_set_chmap, + }; diff --git a/0017-PCM-Add-chmap-options-to-hw-and-null-plugins.patch b/0017-PCM-Add-chmap-options-to-hw-and-null-plugins.patch new file mode 100644 index 0000000..1714fc5 --- /dev/null +++ b/0017-PCM-Add-chmap-options-to-hw-and-null-plugins.patch @@ -0,0 +1,249 @@ +From e1975d20f5d52b370b5e78f5eb5f80a33f55a656 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 14:47:17 +0200 +Subject: [PATCH 17/30] PCM: Add chmap options to hw and null plugins + +Add a config definition "chmap" to override (or enhance) the channel +maps. So far, only a single channel map can be provided, and the +channel count consistency isn't strictly tested at all. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm.c | 29 +++++++++++++++++++++++++++++ + src/pcm/pcm_hw.c | 28 ++++++++++++++++++++++++++++ + src/pcm/pcm_local.h | 5 +++++ + src/pcm/pcm_null.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- + 4 files changed, 111 insertions(+), 1 deletion(-) + +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7531,6 +7531,35 @@ snd_pcm_chmap_t *snd_pcm_chmap_parse_str + return map; + } + ++snd_pcm_chmap_query_t ** ++_snd_pcm_make_single_query_chmaps(snd_pcm_chmap_t *src) ++{ ++ snd_pcm_chmap_query_t **maps; ++ ++ maps = calloc(2, sizeof(*maps)); ++ if (!maps) ++ return NULL; ++ *maps = malloc((src->channels + 2) * sizeof(int)); ++ if (!*maps) { ++ free(maps); ++ return NULL; ++ } ++ (*maps)->type = SND_CHMAP_TYPE_FIXED; ++ memcpy(&(*maps)->map, src, (src->channels + 1) * sizeof(int)); ++ return maps; ++} ++ ++snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd_pcm_chmap_t *src) ++{ ++ snd_pcm_chmap_t *map; ++ ++ map = malloc((src->channels + 1) * sizeof(int)); ++ if (!map) ++ return NULL; ++ memcpy(map, src, (src->channels + 1) * sizeof(int)); ++ return map; ++} ++ + /* + * basic helpers + */ +--- a/src/pcm/pcm_hw.c ++++ b/src/pcm/pcm_hw.c +@@ -107,6 +107,7 @@ typedef struct { + int channels; + /* for chmap */ + unsigned int chmap_caps; ++ snd_pcm_chmap_t *chmap_override; + } snd_pcm_hw_t; + + #define SNDRV_FILE_PCM_STREAM_PLAYBACK ALSA_DEVICE_DIRECTORY "pcmC%iD%ip" +@@ -1144,6 +1145,9 @@ static snd_pcm_chmap_query_t **snd_pcm_h + snd_pcm_hw_t *hw = pcm->private_data; + snd_pcm_chmap_query_t **map; + ++ if (hw->chmap_override) ++ return _snd_pcm_make_single_query_chmaps(hw->chmap_override); ++ + if (!chmap_caps(hw, CHMAP_CTL_QUERY)) + return NULL; + +@@ -1166,6 +1170,9 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_c + unsigned int i; + int ret; + ++ if (hw->chmap_override) ++ return _snd_pcm_copy_chmap(hw->chmap_override); ++ + if (!chmap_caps(hw, CHMAP_CTL_GET)) + return NULL; + +@@ -1220,6 +1227,9 @@ static int snd_pcm_hw_set_chmap(snd_pcm_ + unsigned int i; + int ret; + ++ if (hw->chmap_override) ++ return -ENXIO; ++ + if (!chmap_caps(hw, CHMAP_CTL_SET)) + return -ENXIO; + +@@ -1593,6 +1603,7 @@ pcm.name { + [format STR] # Restrict only to the given format + [channels INT] # Restrict only to the given channels + [rate INT] # Restrict only to the given rate ++ [chmap MAP] # Override channel map + } + \endcode + +@@ -1629,6 +1640,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c + snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; + snd_config_t *n; + int nonblock = 1; /* non-block per default */ ++ snd_pcm_chmap_t *chmap = NULL; + snd_pcm_hw_t *hw; + + /* look for defaults.pcm.nonblock definition */ +@@ -1719,6 +1731,20 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c + channels = val; + continue; + } ++ if (strcmp(id, "chmap") == 0) { ++ err = snd_config_get_string(n, &str); ++ if (err < 0) { ++ SNDERR("Invalid type for %s", id); ++ return -EINVAL; ++ } ++ free(chmap); ++ chmap = snd_pcm_chmap_parse_string(str); ++ if (!chmap) { ++ SNDERR("Invalid channel map for %s", id); ++ return -EINVAL; ++ } ++ continue; ++ } + SNDERR("Unknown field %s", id); + return -EINVAL; + } +@@ -1750,6 +1776,8 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c + hw->channels = channels; + if (rate > 0) + hw->rate = rate; ++ if (chmap) ++ hw->chmap_override = chmap; + + return 0; + } +--- a/src/pcm/pcm_local.h ++++ b/src/pcm/pcm_local.h +@@ -973,3 +973,8 @@ static inline void gettimestamp(snd_htim + } + #endif + } ++ ++snd_pcm_chmap_query_t ** ++_snd_pcm_make_single_query_chmaps(snd_pcm_chmap_t *src); ++snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd_pcm_chmap_t *src); ++ +--- a/src/pcm/pcm_null.c ++++ b/src/pcm/pcm_null.c +@@ -44,6 +44,7 @@ typedef struct { + snd_pcm_uframes_t appl_ptr; + snd_pcm_uframes_t hw_ptr; + int poll_fd; ++ snd_pcm_chmap_t *chmap; + } snd_pcm_null_t; + #endif + +@@ -268,6 +269,24 @@ static int snd_pcm_null_sw_params(snd_pc + return 0; + } + ++static snd_pcm_chmap_query_t **snd_pcm_null_query_chmaps(snd_pcm_t *pcm) ++{ ++ snd_pcm_null_t *null = pcm->private_data; ++ ++ if (null->chmap) ++ return _snd_pcm_make_single_query_chmaps(null->chmap); ++ return NULL; ++} ++ ++static snd_pcm_chmap_t *snd_pcm_null_get_chmap(snd_pcm_t *pcm) ++{ ++ snd_pcm_null_t *null = pcm->private_data; ++ ++ if (null->chmap) ++ return _snd_pcm_copy_chmap(null->chmap); ++ return NULL; ++} ++ + static void snd_pcm_null_dump(snd_pcm_t *pcm, snd_output_t *out) + { + snd_output_printf(out, "Null PCM\n"); +@@ -290,6 +309,9 @@ static const snd_pcm_ops_t snd_pcm_null_ + .async = snd_pcm_null_async, + .mmap = snd_pcm_generic_mmap, + .munmap = snd_pcm_generic_munmap, ++ .query_chmaps = snd_pcm_null_query_chmaps, ++ .get_chmap = snd_pcm_null_get_chmap, ++ .set_chmap = NULL, + }; + + static const snd_pcm_fast_ops_t snd_pcm_null_fast_ops = { +@@ -385,6 +407,7 @@ and /dev/full (capture, must be readable + \code + pcm.name { + type null # Null PCM ++ [chmap MAP] + } + \endcode + +@@ -415,6 +438,10 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, + snd_pcm_stream_t stream, int mode) + { + snd_config_iterator_t i, next; ++ snd_pcm_null_t *null; ++ snd_pcm_chmap_t *chmap = NULL; ++ int err; ++ + snd_config_for_each(i, next, conf) { + snd_config_t *n = snd_config_iterator_entry(i); + const char *id; +@@ -422,10 +449,31 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, + continue; + if (snd_pcm_conf_generic_id(id)) + continue; ++ if (strcmp(id, "chmap") == 0) { ++ const char *str; ++ err = snd_config_get_string(n, &str); ++ if (err < 0) { ++ SNDERR("Invalid type for %s", id); ++ return -EINVAL; ++ } ++ free(chmap); ++ chmap = snd_pcm_chmap_parse_string(str); ++ if (!chmap) { ++ SNDERR("Invalid channel map for %s", id); ++ return -EINVAL; ++ } ++ continue; ++ } + SNDERR("Unknown field %s", id); + return -EINVAL; + } +- return snd_pcm_null_open(pcmp, name, stream, mode); ++ err = snd_pcm_null_open(pcmp, name, stream, mode); ++ if (err < 0) ++ return err; ++ ++ null = (*pcmp)->private_data; ++ null->chmap = chmap; ++ return 0; + } + #ifndef DOC_HIDDEN + SND_DLSYM_BUILD_VERSION(_snd_pcm_null_open, SND_PCM_DLSYM_VERSION); diff --git a/0018-PCM-Add-the-missing-query_chmaps-for-route-plugin.patch b/0018-PCM-Add-the-missing-query_chmaps-for-route-plugin.patch new file mode 100644 index 0000000..94189c0 --- /dev/null +++ b/0018-PCM-Add-the-missing-query_chmaps-for-route-plugin.patch @@ -0,0 +1,85 @@ +From a102028a5a747ecdf1d6fcd16aef0994477a951b Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 15:09:57 +0200 +Subject: [PATCH 18/30] PCM: Add the missing query_chmaps for route plugin + +Also fix the channel count in get_chmap for route plugin. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_route.c | 24 +++++++++++++++++++----- + 1 file changed, 19 insertions(+), 5 deletions(-) + +--- a/src/pcm/pcm_route.c ++++ b/src/pcm/pcm_route.c +@@ -67,6 +67,7 @@ typedef struct { + int use_getput; + unsigned int src_size; + snd_pcm_format_t dst_sfmt; ++ unsigned int nsrcs; + unsigned int ndsts; + snd_pcm_route_ttable_dst_t *dsts; + } snd_pcm_route_params_t; +@@ -707,22 +708,23 @@ static snd_pcm_chmap_t *snd_pcm_route_ge + { + snd_pcm_route_t *route = pcm->private_data; + snd_pcm_chmap_t *map, *slave_map; +- unsigned int src, dst; ++ unsigned int src, dst, nsrcs; + + slave_map = snd_pcm_generic_get_chmap(pcm); + if (!slave_map) + return NULL; +- map = calloc(4, route->schannels + 1); ++ nsrcs = route->params.nsrcs; ++ map = calloc(4, nsrcs + 1); + if (!map) { + free(slave_map); + return NULL; + } +- map->channels = route->schannels; ++ map->channels = nsrcs; + for (dst = 0; dst < route->params.ndsts; dst++) { + snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst]; + for (src = 0; src < d->nsrcs; src++) { + int c = d->srcs[src].channel; +- if (c < route->schannels && !map->pos[c]) ++ if (c < nsrcs && !map->pos[c]) + map->pos[c] = slave_map->pos[dst]; + } + } +@@ -730,6 +732,17 @@ static snd_pcm_chmap_t *snd_pcm_route_ge + return map; + } + ++static snd_pcm_chmap_query_t **snd_pcm_route_query_chmaps(snd_pcm_t *pcm) ++{ ++ snd_pcm_chmap_query_t **maps; ++ snd_pcm_chmap_t *map = snd_pcm_route_get_chmap(pcm); ++ if (!map) ++ return NULL; ++ maps = _snd_pcm_make_single_query_chmaps(map); ++ free(map); ++ return maps; ++} ++ + static void snd_pcm_route_dump(snd_pcm_t *pcm, snd_output_t *out) + { + snd_pcm_route_t *route = pcm->private_data; +@@ -787,7 +800,7 @@ static const snd_pcm_ops_t snd_pcm_route + .async = snd_pcm_generic_async, + .mmap = snd_pcm_generic_mmap, + .munmap = snd_pcm_generic_munmap, +- .query_chmaps = NULL, /* NYI */ ++ .query_chmaps = snd_pcm_route_query_chmaps, + .get_chmap = snd_pcm_route_get_chmap, + .set_chmap = NULL, /* NYI */ + }; +@@ -812,6 +825,7 @@ static int route_load_ttable(snd_pcm_rou + dmul = tt_ssize; + } + params->ndsts = dused; ++ params->nsrcs = sused; + dptr = calloc(dused, sizeof(*params->dsts)); + if (!dptr) + return -ENOMEM; diff --git a/0019-Add-chmap-override-definitions-for-Emu10k1-Audigy-an.patch b/0019-Add-chmap-override-definitions-for-Emu10k1-Audigy-an.patch new file mode 100644 index 0000000..fe766a3 --- /dev/null +++ b/0019-Add-chmap-override-definitions-for-Emu10k1-Audigy-an.patch @@ -0,0 +1,104 @@ +From 5ba11d69dafbd4502469fd957b4580169779664c Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 15:15:32 +0200 +Subject: [PATCH 19/30] Add chmap override definitions for Emu10k1, Audigy and + Audigy2 cards + +These cards won't provide the channel maps from the driver itself +because of the dynamic routing. For simplicity, define chmaps in the +configurations, so that chmap querying of individual stereo streams +and combined multi streams works properly. + +Signed-off-by: Takashi Iwai +--- + src/conf/cards/Audigy.conf | 3 +++ + src/conf/cards/Audigy2.conf | 4 ++++ + src/conf/cards/EMU10K1.conf | 3 +++ + 3 files changed, 10 insertions(+) + +--- a/src/conf/cards/Audigy.conf ++++ b/src/conf/cards/Audigy.conf +@@ -13,6 +13,7 @@ Audigy.pcm.front.0 { + slave.pcm { + type hw + card $CARD ++ chmap "FL,FR" + } + hooks.0 { + type ctl_elems +@@ -65,6 +66,7 @@ Audigy.pcm.rear.0 { + slave.pcm { + type hw + card $CARD ++ chmap "RL,RR" + } + hooks.0 { + type ctl_elems +@@ -100,6 +102,7 @@ Audigy.pcm.center_lfe.0 { + slave.pcm { + type hw + card $CARD ++ chmap "FC,LFE" + } + hooks.0 { + type ctl_elems +--- a/src/conf/cards/Audigy2.conf ++++ b/src/conf/cards/Audigy2.conf +@@ -13,6 +13,7 @@ Audigy2.pcm.front.0 { + slave.pcm { + type hw + card $CARD ++ chmap "FL,FR" + } + hooks.0 { + type ctl_elems +@@ -65,6 +66,7 @@ Audigy2.pcm.rear.0 { + slave.pcm { + type hw + card $CARD ++ chmap "RL,RR" + } + hooks.0 { + type ctl_elems +@@ -100,6 +102,7 @@ Audigy2.pcm.center_lfe.0 { + slave.pcm { + type hw + card $CARD ++ chmap "FC,LFE" + } + hooks.0 { + type ctl_elems +@@ -151,6 +154,7 @@ Audigy2.pcm.side.0 { + slave.pcm { + type hw + card $CARD ++ chmap "SL,SR" + } + hooks.0 { + type ctl_elems +--- a/src/conf/cards/EMU10K1.conf ++++ b/src/conf/cards/EMU10K1.conf +@@ -15,6 +15,7 @@ EMU10K1.pcm.front.0 { + slave.pcm { + type hw + card $CARD ++ chmap "FL,FR" + } + hooks.0 { + type ctl_elems +@@ -73,6 +74,7 @@ EMU10K1.pcm.rear.0 { + slave.pcm { + type hw + card $CARD ++ chmap "RL,RR" + } + hooks.0 { + type ctl_elems +@@ -111,6 +113,7 @@ EMU10K1.pcm.center_lfe.0 { + slave.pcm { + type hw + card $CARD ++ chmap "FC,LFE" + } + hooks.0 { + type ctl_elems diff --git a/0020-PCM-Use-compounds-for-overriding-enhancing-chmaps.patch b/0020-PCM-Use-compounds-for-overriding-enhancing-chmaps.patch new file mode 100644 index 0000000..cdf1051 --- /dev/null +++ b/0020-PCM-Use-compounds-for-overriding-enhancing-chmaps.patch @@ -0,0 +1,438 @@ +From ec7acfc408856cfe71a122e3b5387463d7b3d919 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 17:13:22 +0200 +Subject: [PATCH 20/30] PCM: Use compounds for overriding / enhancing chmaps + +Instead of a single channel map, multiple channel maps can be provided +in a form of compound (array) to hw and null plugins. In null +get_chmap, the channel map corresponding to the current channels is +copied from the given channel maps. + +Signed-off-by: Takashi Iwai +--- + src/conf/cards/Audigy.conf | 6 +- + src/conf/cards/Audigy2.conf | 8 +-- + src/conf/cards/EMU10K1.conf | 6 +- + src/pcm/pcm.c | 109 +++++++++++++++++++++++++++++++++++++++++--- + src/pcm/pcm_hw.c | 25 ++++------ + src/pcm/pcm_local.h | 10 +++- + src/pcm/pcm_null.c | 25 ++++------ + 7 files changed, 144 insertions(+), 45 deletions(-) + +--- a/src/conf/cards/Audigy.conf ++++ b/src/conf/cards/Audigy.conf +@@ -13,7 +13,7 @@ Audigy.pcm.front.0 { + slave.pcm { + type hw + card $CARD +- chmap "FL,FR" ++ chmap [ "UNKNOWN" "FL,FR" ] + } + hooks.0 { + type ctl_elems +@@ -66,7 +66,7 @@ Audigy.pcm.rear.0 { + slave.pcm { + type hw + card $CARD +- chmap "RL,RR" ++ chmap [ "UNKNOWN" "RL,RR" ] + } + hooks.0 { + type ctl_elems +@@ -102,7 +102,7 @@ Audigy.pcm.center_lfe.0 { + slave.pcm { + type hw + card $CARD +- chmap "FC,LFE" ++ chmap [ "UNKNOWN" "FC,LFE" ] + } + hooks.0 { + type ctl_elems +--- a/src/conf/cards/Audigy2.conf ++++ b/src/conf/cards/Audigy2.conf +@@ -13,7 +13,7 @@ Audigy2.pcm.front.0 { + slave.pcm { + type hw + card $CARD +- chmap "FL,FR" ++ chmap [ "UNKNOWN" "FL,FR" ] + } + hooks.0 { + type ctl_elems +@@ -66,7 +66,7 @@ Audigy2.pcm.rear.0 { + slave.pcm { + type hw + card $CARD +- chmap "RL,RR" ++ chmap [ "UNKNOWN" "RL,RR" ] + } + hooks.0 { + type ctl_elems +@@ -102,7 +102,7 @@ Audigy2.pcm.center_lfe.0 { + slave.pcm { + type hw + card $CARD +- chmap "FC,LFE" ++ chmap [ "UNKNOWN" "FC,LFE" ] + } + hooks.0 { + type ctl_elems +@@ -154,7 +154,7 @@ Audigy2.pcm.side.0 { + slave.pcm { + type hw + card $CARD +- chmap "SL,SR" ++ chmap [ "UNKNOWN" "SL,SR" ] + } + hooks.0 { + type ctl_elems +--- a/src/conf/cards/EMU10K1.conf ++++ b/src/conf/cards/EMU10K1.conf +@@ -15,7 +15,7 @@ EMU10K1.pcm.front.0 { + slave.pcm { + type hw + card $CARD +- chmap "FL,FR" ++ chmap [ "UNKNOWN" "FL,FR" ] + } + hooks.0 { + type ctl_elems +@@ -74,7 +74,7 @@ EMU10K1.pcm.rear.0 { + slave.pcm { + type hw + card $CARD +- chmap "RL,RR" ++ chmap [ "UNKNOWN" "RL,RR" ] + } + hooks.0 { + type ctl_elems +@@ -113,7 +113,7 @@ EMU10K1.pcm.center_lfe.0 { + slave.pcm { + type hw + card $CARD +- chmap "FC,LFE" ++ chmap [ "UNKNOWN" "FC,LFE" ] + } + hooks.0 { + type ctl_elems +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7531,25 +7531,37 @@ snd_pcm_chmap_t *snd_pcm_chmap_parse_str + return map; + } + ++/* copy a single channel map with the fixed type to chmap_query pointer */ ++static int _copy_to_fixed_query_map(snd_pcm_chmap_query_t **dst, ++ const snd_pcm_chmap_t *src) ++{ ++ *dst = malloc((src->channels + 2) * sizeof(int)); ++ if (!*dst) ++ return -ENOMEM; ++ (*dst)->type = SND_CHMAP_TYPE_FIXED; ++ memcpy(&(*dst)->map, src, (src->channels + 1) * sizeof(int)); ++ return 0; ++} ++ ++#ifndef DOC_HIDDEN ++/* make a chmap_query array from a single channel map */ + snd_pcm_chmap_query_t ** +-_snd_pcm_make_single_query_chmaps(snd_pcm_chmap_t *src) ++_snd_pcm_make_single_query_chmaps(const snd_pcm_chmap_t *src) + { + snd_pcm_chmap_query_t **maps; + + maps = calloc(2, sizeof(*maps)); + if (!maps) + return NULL; +- *maps = malloc((src->channels + 2) * sizeof(int)); +- if (!*maps) { ++ if (_copy_to_fixed_query_map(maps, src)) { + free(maps); + return NULL; + } +- (*maps)->type = SND_CHMAP_TYPE_FIXED; +- memcpy(&(*maps)->map, src, (src->channels + 1) * sizeof(int)); + return maps; + } + +-snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd_pcm_chmap_t *src) ++/* make a copy of chmap */ ++snd_pcm_chmap_t *_snd_pcm_copy_chmap(const snd_pcm_chmap_t *src) + { + snd_pcm_chmap_t *map; + +@@ -7560,6 +7572,91 @@ snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd + return map; + } + ++/* make a copy of channel maps */ ++snd_pcm_chmap_query_t ** ++_snd_pcm_copy_chmap_query(snd_pcm_chmap_query_t * const *src) ++{ ++ snd_pcm_chmap_query_t * const *p; ++ snd_pcm_chmap_query_t **maps; ++ int i, nums; ++ ++ for (nums = 0, p = src; *p; p++) ++ nums++; ++ ++ maps = calloc(nums + 1, sizeof(*maps)); ++ if (!maps) ++ return NULL; ++ for (i = 0; i < nums; i++) { ++ maps[i] = malloc((src[i]->map.channels + 2) * sizeof(int)); ++ if (!maps[i]) { ++ snd_pcm_free_chmaps(maps); ++ return NULL; ++ } ++ memcpy(maps[i], src[i], (src[i]->map.channels + 2) * sizeof(int)); ++ } ++ return maps; ++} ++ ++/* select the channel map with the current PCM channels and make a copy */ ++snd_pcm_chmap_t * ++_snd_pcm_choose_fixed_chmap(snd_pcm_t *pcm, snd_pcm_chmap_query_t * const *maps) ++{ ++ snd_pcm_chmap_query_t * const *p; ++ ++ for (p = maps; *p; p++) { ++ if ((*p)->map.channels == pcm->channels) ++ return _snd_pcm_copy_chmap(&(*p)->map); ++ } ++ return NULL; ++} ++ ++/* make chmap_query array from the config tree; ++ * conf must be a compound (array) ++ */ ++snd_pcm_chmap_query_t ** ++_snd_pcm_parse_config_chmaps(snd_config_t *conf) ++{ ++ snd_pcm_chmap_t *chmap; ++ snd_pcm_chmap_query_t **maps; ++ snd_config_iterator_t i, next; ++ const char *str; ++ int nums, err; ++ ++ if (snd_config_get_type(conf) != SND_CONFIG_TYPE_COMPOUND) ++ return NULL; ++ ++ nums = 0; ++ snd_config_for_each(i, next, conf) { ++ nums++; ++ } ++ ++ maps = calloc(nums + 1, sizeof(*maps)); ++ if (!maps) ++ return NULL; ++ ++ nums = 0; ++ snd_config_for_each(i, next, conf) { ++ snd_config_t *n = snd_config_iterator_entry(i); ++ err = snd_config_get_string(n, &str); ++ if (err < 0) ++ goto error; ++ chmap = snd_pcm_chmap_parse_string(str); ++ if (!chmap) ++ goto error; ++ if (_copy_to_fixed_query_map(maps + nums, chmap)) { ++ free(chmap); ++ goto error; ++ } ++ nums++; ++ } ++ return maps; ++ ++ error: ++ snd_pcm_free_chmaps(maps); ++ return NULL; ++} ++#endif /* DOC_HIDDEN */ ++ + /* + * basic helpers + */ +--- a/src/pcm/pcm_hw.c ++++ b/src/pcm/pcm_hw.c +@@ -107,7 +107,7 @@ typedef struct { + int channels; + /* for chmap */ + unsigned int chmap_caps; +- snd_pcm_chmap_t *chmap_override; ++ snd_pcm_chmap_query_t **chmap_override; + } snd_pcm_hw_t; + + #define SNDRV_FILE_PCM_STREAM_PLAYBACK ALSA_DEVICE_DIRECTORY "pcmC%iD%ip" +@@ -1146,7 +1146,7 @@ static snd_pcm_chmap_query_t **snd_pcm_h + snd_pcm_chmap_query_t **map; + + if (hw->chmap_override) +- return _snd_pcm_make_single_query_chmaps(hw->chmap_override); ++ return _snd_pcm_copy_chmap_query(hw->chmap_override); + + if (!chmap_caps(hw, CHMAP_CTL_QUERY)) + return NULL; +@@ -1171,7 +1171,7 @@ static snd_pcm_chmap_t *snd_pcm_hw_get_c + int ret; + + if (hw->chmap_override) +- return _snd_pcm_copy_chmap(hw->chmap_override); ++ return _snd_pcm_choose_fixed_chmap(pcm, hw->chmap_override); + + if (!chmap_caps(hw, CHMAP_CTL_GET)) + return NULL; +@@ -1603,7 +1603,7 @@ pcm.name { + [format STR] # Restrict only to the given format + [channels INT] # Restrict only to the given channels + [rate INT] # Restrict only to the given rate +- [chmap MAP] # Override channel map ++ [chmap MAP] # Override channel maps; MAP is a string array + } + \endcode + +@@ -1640,7 +1640,7 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c + snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN; + snd_config_t *n; + int nonblock = 1; /* non-block per default */ +- snd_pcm_chmap_t *chmap = NULL; ++ snd_pcm_chmap_query_t **chmap = NULL; + snd_pcm_hw_t *hw; + + /* look for defaults.pcm.nonblock definition */ +@@ -1732,13 +1732,8 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c + continue; + } + if (strcmp(id, "chmap") == 0) { +- err = snd_config_get_string(n, &str); +- if (err < 0) { +- SNDERR("Invalid type for %s", id); +- return -EINVAL; +- } +- free(chmap); +- chmap = snd_pcm_chmap_parse_string(str); ++ snd_pcm_free_chmaps(chmap); ++ chmap = _snd_pcm_parse_config_chmaps(n); + if (!chmap) { + SNDERR("Invalid channel map for %s", id); + return -EINVAL; +@@ -1746,17 +1741,21 @@ int _snd_pcm_hw_open(snd_pcm_t **pcmp, c + continue; + } + SNDERR("Unknown field %s", id); ++ snd_pcm_free_chmaps(chmap); + return -EINVAL; + } + if (card < 0) { + SNDERR("card is not defined"); ++ snd_pcm_free_chmaps(chmap); + return -EINVAL; + } + err = snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, + mode | (nonblock ? SND_PCM_NONBLOCK : 0), + 0, sync_ptr_ioctl); +- if (err < 0) ++ if (err < 0) { ++ snd_pcm_free_chmaps(chmap); + return err; ++ } + if (nonblock && ! (mode & SND_PCM_NONBLOCK)) { + /* revert to blocking mode for read/write access */ + snd_pcm_hw_nonblock(*pcmp, 0); +--- a/src/pcm/pcm_local.h ++++ b/src/pcm/pcm_local.h +@@ -975,6 +975,12 @@ static inline void gettimestamp(snd_htim + } + + snd_pcm_chmap_query_t ** +-_snd_pcm_make_single_query_chmaps(snd_pcm_chmap_t *src); +-snd_pcm_chmap_t *_snd_pcm_copy_chmap(snd_pcm_chmap_t *src); ++_snd_pcm_make_single_query_chmaps(const snd_pcm_chmap_t *src); ++snd_pcm_chmap_t *_snd_pcm_copy_chmap(const snd_pcm_chmap_t *src); ++snd_pcm_chmap_query_t ** ++_snd_pcm_copy_chmap_query(snd_pcm_chmap_query_t * const *src); ++snd_pcm_chmap_query_t ** ++_snd_pcm_parse_config_chmaps(snd_config_t *conf); ++snd_pcm_chmap_t * ++_snd_pcm_choose_fixed_chmap(snd_pcm_t *pcm, snd_pcm_chmap_query_t * const *maps); + +--- a/src/pcm/pcm_null.c ++++ b/src/pcm/pcm_null.c +@@ -44,7 +44,7 @@ typedef struct { + snd_pcm_uframes_t appl_ptr; + snd_pcm_uframes_t hw_ptr; + int poll_fd; +- snd_pcm_chmap_t *chmap; ++ snd_pcm_chmap_query_t **chmap; + } snd_pcm_null_t; + #endif + +@@ -274,7 +274,7 @@ static snd_pcm_chmap_query_t **snd_pcm_n + snd_pcm_null_t *null = pcm->private_data; + + if (null->chmap) +- return _snd_pcm_make_single_query_chmaps(null->chmap); ++ return _snd_pcm_copy_chmap_query(null->chmap); + return NULL; + } + +@@ -283,7 +283,7 @@ static snd_pcm_chmap_t *snd_pcm_null_get + snd_pcm_null_t *null = pcm->private_data; + + if (null->chmap) +- return _snd_pcm_copy_chmap(null->chmap); ++ return _snd_pcm_choose_fixed_chmap(pcm, null->chmap); + return NULL; + } + +@@ -407,7 +407,7 @@ and /dev/full (capture, must be readable + \code + pcm.name { + type null # Null PCM +- [chmap MAP] ++ [chmap MAP] # Provide channel maps; MAP is a string array + } + \endcode + +@@ -439,7 +439,7 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, + { + snd_config_iterator_t i, next; + snd_pcm_null_t *null; +- snd_pcm_chmap_t *chmap = NULL; ++ snd_pcm_chmap_query_t **chmap = NULL; + int err; + + snd_config_for_each(i, next, conf) { +@@ -450,14 +450,8 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, + if (snd_pcm_conf_generic_id(id)) + continue; + if (strcmp(id, "chmap") == 0) { +- const char *str; +- err = snd_config_get_string(n, &str); +- if (err < 0) { +- SNDERR("Invalid type for %s", id); +- return -EINVAL; +- } +- free(chmap); +- chmap = snd_pcm_chmap_parse_string(str); ++ snd_pcm_free_chmaps(chmap); ++ chmap = _snd_pcm_parse_config_chmaps(n); + if (!chmap) { + SNDERR("Invalid channel map for %s", id); + return -EINVAL; +@@ -465,11 +459,14 @@ int _snd_pcm_null_open(snd_pcm_t **pcmp, + continue; + } + SNDERR("Unknown field %s", id); ++ snd_pcm_free_chmaps(chmap); + return -EINVAL; + } + err = snd_pcm_null_open(pcmp, name, stream, mode); +- if (err < 0) ++ if (err < 0) { ++ snd_pcm_free_chmaps(chmap); + return err; ++ } + + null = (*pcmp)->private_data; + null->chmap = chmap; diff --git a/0021-conf-Add-chmap-definitions-to-TRIDENT-and-SI7018-con.patch b/0021-conf-Add-chmap-definitions-to-TRIDENT-and-SI7018-con.patch new file mode 100644 index 0000000..411d874 --- /dev/null +++ b/0021-conf-Add-chmap-definitions-to-TRIDENT-and-SI7018-con.patch @@ -0,0 +1,51 @@ +From 2c61a4173ff2c3d57eb77b3f66e0d0e517c799d6 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 17:34:49 +0200 +Subject: [PATCH 21/30] conf: Add chmap definitions to TRIDENT and SI7018 + configurations + +Manually add the channel map definitions as the channel front/rear is +determined dynamically. + +Signed-off-by: Takashi Iwai +--- + src/conf/cards/SI7018.conf | 2 ++ + src/conf/cards/TRID4DWAVENX.conf | 2 ++ + 2 files changed, 4 insertions(+) + +--- a/src/conf/cards/SI7018.conf ++++ b/src/conf/cards/SI7018.conf +@@ -13,6 +13,7 @@ SI7018.pcm.front.0 { + } + type hw + card $CARD ++ chmap [ "UNKNOWN" "FL,FR" ] + } + + +@@ -26,6 +27,7 @@ SI7018.pcm.rear.0 { + slave.pcm { + type hw + card $CARD ++ chmap [ "UNKNOWN" "RL,RR" ] + } + hooks.0 { + type ctl_elems +--- a/src/conf/cards/TRID4DWAVENX.conf ++++ b/src/conf/cards/TRID4DWAVENX.conf +@@ -11,6 +11,7 @@ TRID4DWAVENX.pcm.front.0 { + } + type hw + card $CARD ++ chmap [ "UNKNOWN" "FL,FR" ] + } + + +@@ -24,6 +25,7 @@ TRID4DWAVENX.pcm.rear.0 { + slave.pcm { + type hw + card $CARD ++ chmap [ "UNKNOWN" "RL,RR" ] + } + hooks.0 { + type ctl_elems diff --git a/0022-test-chmap-Fix-wrong-malloc-size.patch b/0022-test-chmap-Fix-wrong-malloc-size.patch new file mode 100644 index 0000000..481cd50 --- /dev/null +++ b/0022-test-chmap-Fix-wrong-malloc-size.patch @@ -0,0 +1,21 @@ +From 58c45b30309a1dd255c8b9e5cb34398658867b81 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 13 Sep 2012 08:06:26 +0200 +Subject: [PATCH 22/30] test/chmap: Fix wrong malloc size + +Signed-off-by: Takashi Iwai +--- + test/chmap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/test/chmap.c ++++ b/test/chmap.c +@@ -118,7 +118,7 @@ static int set_chmap(snd_pcm_t *pcm, int + } + if (setup_pcm(pcm, format, channels, rate)) + return 1; +- map = malloc(sizeof(int) * channels + 1); ++ map = malloc(sizeof(int) * (channels + 1)); + if (!map) { + printf("cannot malloc\n"); + return 1; diff --git a/0023-PCM-Define-MONO-and-other-channel-map-positions.patch b/0023-PCM-Define-MONO-and-other-channel-map-positions.patch new file mode 100644 index 0000000..deed1ba --- /dev/null +++ b/0023-PCM-Define-MONO-and-other-channel-map-positions.patch @@ -0,0 +1,116 @@ +From f7300682dc0fe9faec94461af6fdcf098047a0d1 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 18:43:59 +0200 +Subject: [PATCH 23/30] PCM: Define MONO and other channel map positions + +Follow the new definitions in the kernel side. MONO and others have +been added, and the order of position table was changed again. + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 11 +++++++++-- + include/sound/asound.h | 13 ++++++++++--- + src/pcm/pcm.c | 14 +++++++++++--- + 3 files changed, 30 insertions(+), 8 deletions(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -489,6 +489,8 @@ enum snd_pcm_chmap_type { + /** channel positions */ + enum snd_pcm_chmap_position { + SND_CHMAP_UNKNOWN = 0, /** unspecified */ ++ SND_CHMAP_NA, /** N/A, silent */ ++ SND_CHMAP_MONO, /** mono stream */ + SND_CHMAP_FL, /** front left */ + SND_CHMAP_FR, /** front right */ + SND_CHMAP_RL, /** rear left */ +@@ -508,8 +510,13 @@ enum snd_pcm_chmap_position { + SND_CHMAP_FCH, /** front center high */ + SND_CHMAP_FRH, /** front right high */ + SND_CHMAP_TC, /** top center */ +- SND_CHMAP_NA, /** N/A, silent */ +- SND_CHMAP_LAST = SND_CHMAP_NA, /** last entry */ ++ SND_CHMAP_TFL, /** top front left */ ++ SND_CHMAP_TFR, /** top front right */ ++ SND_CHMAP_TFC, /** top front center */ ++ SND_CHMAP_TRL, /** top rear left */ ++ SND_CHMAP_TRR, /** top rear right */ ++ SND_CHMAP_TRC, /** top rear center */ ++ SND_CHMAP_LAST = SND_CHMAP_TRC, /** last entry */ + }; + + #define SND_CHMAP_POSITION_MASK 0xffff /** bitmask for channel position */ +--- a/include/sound/asound.h ++++ b/include/sound/asound.h +@@ -479,8 +479,10 @@ enum { + + /* channel positions */ + enum { +- /* this follows the alsa-lib mixer channel value + 1 */ + SNDRV_CHMAP_UNKNOWN = 0, ++ SNDRV_CHMAP_NA, /* N/A, silent */ ++ SNDRV_CHMAP_MONO, /* mono stream */ ++ /* this follows the alsa-lib mixer channel value + 3 */ + SNDRV_CHMAP_FL, /* front left */ + SNDRV_CHMAP_FR, /* front right */ + SNDRV_CHMAP_RL, /* rear left */ +@@ -501,8 +503,13 @@ enum { + SNDRV_CHMAP_FCH, /* front center high */ + SNDRV_CHMAP_FRH, /* front right high */ + SNDRV_CHMAP_TC, /* top center */ +- SNDRV_CHMAP_NA, /* N/A, silent */ +- SNDRV_CHMAP_LAST = SNDRV_CHMAP_NA, ++ SNDRV_CHMAP_TFL, /* top front left */ ++ SNDRV_CHMAP_TFR, /* top front right */ ++ SNDRV_CHMAP_TFC, /* top front center */ ++ SNDRV_CHMAP_TRL, /* top rear left */ ++ SNDRV_CHMAP_TRR, /* top rear right */ ++ SNDRV_CHMAP_TRC, /* top rear center */ ++ SNDRV_CHMAP_LAST = SNDRV_CHMAP_TRC, + }; + + #define SNDRV_CHMAP_POSITION_MASK 0xffff +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7375,7 +7375,7 @@ const char *snd_pcm_chmap_type_name(enum + + #define _NAME(n) [SND_CHMAP_##n] = #n + static const char *chmap_names[SND_CHMAP_LAST + 1] = { +- _NAME(UNKNOWN), ++ _NAME(UNKNOWN), _NAME(NA), _NAME(MONO), + _NAME(FL), _NAME(FR), + _NAME(RL), _NAME(RR), + _NAME(FC), _NAME(LFE), +@@ -7383,7 +7383,8 @@ static const char *chmap_names[SND_CHMAP + _NAME(RC), _NAME(FLC), _NAME(FRC), _NAME(RLC), _NAME(RRC), + _NAME(FLW), _NAME(FRW), + _NAME(FLH), _NAME(FCH), _NAME(FRH), _NAME(TC), +- _NAME(NA), ++ _NAME(TFL), _NAME(TFR), _NAME(TFC), ++ _NAME(TRL), _NAME(TRR), _NAME(TRC), + }; + #undef _NAME + +@@ -7397,6 +7398,8 @@ const char *snd_pcm_chmap_name(enum snd_ + + static const char *chmap_long_names[SND_CHMAP_LAST + 1] = { + [SND_CHMAP_UNKNOWN] = "Unknown", ++ [SND_CHMAP_NA] = "Unused", ++ [SND_CHMAP_MONO] = "Mono", + [SND_CHMAP_FL] = "Front Left", + [SND_CHMAP_FR] = "Front Right", + [SND_CHMAP_RL] = "Rear Left", +@@ -7416,7 +7419,12 @@ static const char *chmap_long_names[SND_ + [SND_CHMAP_FCH] = "Front Center High", + [SND_CHMAP_FRH] = "Front Right High", + [SND_CHMAP_TC] = "Top Center", +- [SND_CHMAP_NA] = "Unused", ++ [SND_CHMAP_TFL] = "Top Front Left", ++ [SND_CHMAP_TFR] = "Top Front Right", ++ [SND_CHMAP_TFC] = "Top Front Center", ++ [SND_CHMAP_TRL] = "Top Rear Left", ++ [SND_CHMAP_TRR] = "Top Rear Right", ++ [SND_CHMAP_TRC] = "Top Rear Center", + }; + + const char *snd_pcm_chmap_long_name(enum snd_pcm_chmap_position val) diff --git a/0024-PCM-Fix-the-conversion-from-string-to-chmap-position.patch b/0024-PCM-Fix-the-conversion-from-string-to-chmap-position.patch new file mode 100644 index 0000000..6c25924 --- /dev/null +++ b/0024-PCM-Fix-the-conversion-from-string-to-chmap-position.patch @@ -0,0 +1,67 @@ +From c6db60e32758e8f4bb6f066a5c6fc7758a0f4c49 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 12 Sep 2012 18:44:42 +0200 +Subject: [PATCH 24/30] PCM: Fix the conversion from string to chmap position + +Use strncasecmp() to allow lower cases, and also evaluate the inverted +phase suffix, too. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm.c | 37 +++++++++++++++++++++++++++---------- + 1 file changed, 27 insertions(+), 10 deletions(-) + +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7471,24 +7471,41 @@ int snd_pcm_chmap_print(const snd_pcm_ch + static int str_to_chmap(const char *str, int len) + { + int val; ++ unsigned long v; ++ char *p; + + if (isdigit(*str)) { +- val = atoi(str); +- if (val < 0) ++ v = strtoul(str, &p, 0); ++ if (v == ULONG_MAX) + return -1; +- return val | SND_CHMAP_DRIVER_SPEC; +- } else if (str[0] == 'C' && str[1] == 'h') { +- val = atoi(str + 2); +- if (val < 0) ++ val = v; ++ val |= SND_CHMAP_DRIVER_SPEC; ++ str = p; ++ } else if (!strncasecmp(str, "ch", 2)) { ++ v = strtoul(str + 2, &p, 0); ++ if (v == ULONG_MAX) + return -1; +- return val; ++ val = v; ++ str = p; + } else { + for (val = 0; val <= SND_CHMAP_LAST; val++) { +- if (!strncmp(str, chmap_names[val], len)) +- return val; ++ int slen; ++ assert(chmap_names[val]); ++ slen = strlen(chmap_names[val]); ++ if (slen > len) ++ continue; ++ if (!strncasecmp(str, chmap_names[val], slen) && ++ !isalpha(str[slen])) { ++ str += slen; ++ break; ++ } + } +- return -1; ++ if (val > SND_CHMAP_LAST) ++ return -1; + } ++ if (str && !strncasecmp(str, "[INV]", 5)) ++ val |= SND_CHMAP_PHASE_INVERSE; ++ return val; + } + + unsigned int snd_pcm_chmap_from_string(const char *str) diff --git a/0025-PCM-A-few-doxygen-fixes-for-chmap-stuff.patch b/0025-PCM-A-few-doxygen-fixes-for-chmap-stuff.patch new file mode 100644 index 0000000..515e8c8 --- /dev/null +++ b/0025-PCM-A-few-doxygen-fixes-for-chmap-stuff.patch @@ -0,0 +1,158 @@ +From 63f6f4a6103e55d242440488999e648beb5e4e4d Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 13 Sep 2012 08:38:59 +0200 +Subject: [PATCH 25/30] PCM: A few doxygen fixes for chmap stuff + +Signed-off-by: Takashi Iwai +--- + include/pcm.h | 18 +++++++++++------- + src/pcm/pcm.c | 43 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 54 insertions(+), 7 deletions(-) + +--- a/include/pcm.h ++++ b/include/pcm.h +@@ -519,20 +519,24 @@ enum snd_pcm_chmap_position { + SND_CHMAP_LAST = SND_CHMAP_TRC, /** last entry */ + }; + +-#define SND_CHMAP_POSITION_MASK 0xffff /** bitmask for channel position */ +-#define SND_CHMAP_PHASE_INVERSE (0x01 << 16) /* the channel is phase inverted */ +-#define SND_CHMAP_DRIVER_SPEC (0x02 << 16) /* non-standard channel value */ ++/** bitmask for channel position */ ++#define SND_CHMAP_POSITION_MASK 0xffff ++ ++/** bit flag indicating the channel is phase inverted */ ++#define SND_CHMAP_PHASE_INVERSE (0x01 << 16) ++/** bit flag indicating the non-standard channel value */ ++#define SND_CHMAP_DRIVER_SPEC (0x02 << 16) + + /** the channel map header */ + typedef struct snd_pcm_chmap { +- unsigned int channels; +- unsigned int pos[0]; ++ unsigned int channels; /** number of channels */ ++ unsigned int pos[0]; /** channel position array */ + } snd_pcm_chmap_t; + + /** the header of array items returned from snd_pcm_query_chmaps() */ + typedef struct snd_pcm_chmap_query { +- enum snd_pcm_chmap_type type; +- snd_pcm_chmap_t map; ++ enum snd_pcm_chmap_type type; /** channel map type */ ++ snd_pcm_chmap_t map; /** available channel map */ + } snd_pcm_chmap_query_t; + + +--- a/src/pcm/pcm.c ++++ b/src/pcm/pcm.c +@@ -7310,6 +7310,9 @@ OBSOLETE1(snd_pcm_sw_params_get_silence_ + * 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. ++ * ++ * Note: the caller is requested to release the returned value via ++ * snd_pcm_free_chmaps(). + */ + snd_pcm_chmap_query_t **snd_pcm_query_chmaps(snd_pcm_t *pcm) + { +@@ -7336,6 +7339,8 @@ void snd_pcm_free_chmaps(snd_pcm_chmap_q + * \!brief Get the current channel map + * \param pcm PCM instance + * \return the current channel map, or NULL if error ++ * ++ * Note: the caller is requested to release the returned value via free() + */ + snd_pcm_chmap_t *snd_pcm_get_chmap(snd_pcm_t *pcm) + { +@@ -7359,12 +7364,19 @@ int snd_pcm_set_chmap(snd_pcm_t *pcm, co + + /* + */ ++#ifndef DOC_HIDDEN + #define _NAME(n) [SND_CHMAP_TYPE_##n] = #n + static const char *chmap_type_names[SND_CHMAP_TYPE_LAST + 1] = { + _NAME(NONE), _NAME(FIXED), _NAME(VAR), _NAME(PAIRED), + }; + #undef _NAME ++#endif + ++/** ++ * \!brief Get a name string for a channel map type as query results ++ * \param val Channel position ++ * \return The string corresponding to the given type, or NULL ++ */ + const char *snd_pcm_chmap_type_name(enum snd_pcm_chmap_type val) + { + if (val <= SND_CHMAP_TYPE_LAST) +@@ -7373,6 +7385,7 @@ const char *snd_pcm_chmap_type_name(enum + return NULL; + } + ++#ifndef DOC_HIDDEN + #define _NAME(n) [SND_CHMAP_##n] = #n + static const char *chmap_names[SND_CHMAP_LAST + 1] = { + _NAME(UNKNOWN), _NAME(NA), _NAME(MONO), +@@ -7387,7 +7400,13 @@ static const char *chmap_names[SND_CHMAP + _NAME(TRL), _NAME(TRR), _NAME(TRC), + }; + #undef _NAME ++#endif + ++/** ++ * \!brief Get a name string for a standard channel map position ++ * \param val Channel position ++ * \return The string corresponding to the given position, or NULL ++ */ + const char *snd_pcm_chmap_name(enum snd_pcm_chmap_position val) + { + if (val <= SND_CHMAP_LAST) +@@ -7427,6 +7446,11 @@ static const char *chmap_long_names[SND_ + [SND_CHMAP_TRC] = "Top Rear Center", + }; + ++/** ++ * \!brief Get a longer name string for a standard channel map position ++ * \param val Channel position ++ * \return The string corresponding to the given position, or NULL ++ */ + const char *snd_pcm_chmap_long_name(enum snd_pcm_chmap_position val) + { + if (val <= SND_CHMAP_LAST) +@@ -7435,6 +7459,13 @@ const char *snd_pcm_chmap_long_name(enum + return NULL; + } + ++/** ++ * \!brief Print the channels in chmap on the buffer ++ * \param map The channel map to print ++ * \param maxlen The maximal length to write (including NUL letter) ++ * \param buf The buffer to write ++ * \return The actual string length or a negative error code ++ */ + int snd_pcm_chmap_print(const snd_pcm_chmap_t *map, size_t maxlen, char *buf) + { + unsigned int i, len = 0; +@@ -7508,11 +7539,23 @@ static int str_to_chmap(const char *str, + return val; + } + ++/** ++ * \!brief Convert from string to channel position ++ * \param str The string to parse ++ * \return The channel position value or -1 as an error ++ */ + unsigned int snd_pcm_chmap_from_string(const char *str) + { + return str_to_chmap(str, strlen(str)); + } + ++/** ++ * \!brief Convert from string to channel map ++ * \param str The string to parse ++ * \return The channel map ++ * ++ * Note: the caller is requested to release the returned value via free() ++ */ + snd_pcm_chmap_t *snd_pcm_chmap_parse_string(const char *str) + { + int i, ch = 0; diff --git a/0026-PCM-Fill-SND_CHMAP_NA-to-silent-channels-in-route-pl.patch b/0026-PCM-Fill-SND_CHMAP_NA-to-silent-channels-in-route-pl.patch new file mode 100644 index 0000000..95993f1 --- /dev/null +++ b/0026-PCM-Fill-SND_CHMAP_NA-to-silent-channels-in-route-pl.patch @@ -0,0 +1,30 @@ +From 5a2daef19225478df7e3c1f8c6c9cb847e574b44 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Thu, 20 Sep 2012 13:43:12 +0200 +Subject: [PATCH 26/30] PCM: Fill SND_CHMAP_NA to silent channels in route + plugin + +Instead of SND_CHMAP_UNKNOWN, fill SND_CHMAP_NA to the silent channels. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_route.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +--- a/src/pcm/pcm_route.c ++++ b/src/pcm/pcm_route.c +@@ -720,11 +720,13 @@ static snd_pcm_chmap_t *snd_pcm_route_ge + return NULL; + } + map->channels = nsrcs; ++ for (src = 0; src < nsrcs; src++) ++ map->pos[src] = SND_CHMAP_NA; + for (dst = 0; dst < route->params.ndsts; dst++) { + snd_pcm_route_ttable_dst_t *d = &route->params.dsts[dst]; + for (src = 0; src < d->nsrcs; src++) { + int c = d->srcs[src].channel; +- if (c < nsrcs && !map->pos[c]) ++ if (c < nsrcs && map->pos[c] == SND_CHMAP_NA) + map->pos[c] = slave_map->pos[dst]; + } + } diff --git a/0027-PCM-Fix-infinite-loop-in-htimestamp-of-dmix-dsnoop-a.patch b/0027-PCM-Fix-infinite-loop-in-htimestamp-of-dmix-dsnoop-a.patch new file mode 100644 index 0000000..8ff5394 --- /dev/null +++ b/0027-PCM-Fix-infinite-loop-in-htimestamp-of-dmix-dsnoop-a.patch @@ -0,0 +1,43 @@ +From 5a6ce315201f9e2abee51f4f890c1f5c6592c998 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Fri, 21 Sep 2012 17:59:42 +0200 +Subject: [PATCH 27/30] PCM: Fix infinite loop in htimestamp of dmix, dsnoop + and dshare plugins + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_dmix.c | 1 + + src/pcm/pcm_dshare.c | 1 + + src/pcm/pcm_dsnoop.c | 1 + + 3 files changed, 3 insertions(+) + +--- a/src/pcm/pcm_dmix.c ++++ b/src/pcm/pcm_dmix.c +@@ -853,6 +853,7 @@ static int snd_pcm_dmix_htimestamp(snd_p + break; + *avail = avail1; + *tstamp = snd_pcm_hw_fast_tstamp(dmix->spcm); ++ ok = 1; + } + return 0; + } +--- a/src/pcm/pcm_dshare.c ++++ b/src/pcm/pcm_dshare.c +@@ -543,6 +543,7 @@ static int snd_pcm_dshare_htimestamp(snd + break; + *avail = avail1; + *tstamp = snd_pcm_hw_fast_tstamp(dshare->spcm); ++ ok = 1; + } + return 0; + } +--- a/src/pcm/pcm_dsnoop.c ++++ b/src/pcm/pcm_dsnoop.c +@@ -458,6 +458,7 @@ static int snd_pcm_dsnoop_htimestamp(snd + break; + *avail = avail1; + *tstamp = snd_pcm_hw_fast_tstamp(dsnoop->spcm); ++ ok = 1; + } + return 0; + } diff --git a/0028-test-add-audio_time.patch b/0028-test-add-audio_time.patch new file mode 100644 index 0000000..5d97a9f --- /dev/null +++ b/0028-test-add-audio_time.patch @@ -0,0 +1,287 @@ +From 6429a450a315e00d9e00af46eb42784a4cc5dce1 Mon Sep 17 00:00:00 2001 +From: Pierre-Louis Bossart +Date: Fri, 21 Sep 2012 20:05:18 -0500 +Subject: [PATCH 28/30] test: add audio_time + +Simple test to create playback and capture streams, and +check elapsed time vs. sample counts reported by driver. +This should be helpful for driver developers and anyone +interested in system/audio time drift. + +tested only on HDAudio + +[added Makefile.am change by tiwai] + +TODO: +- make period configurable +- better output messages +- support for wall clock when it's in the mainline + +Signed-off-by: Pierre-Louis Bossart +Signed-off-by: Takashi Iwai +--- + test/Makefile.am | 4 + test/audio_time.c | 237 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 240 insertions(+), 1 deletion(-) + create mode 100644 test/audio_time.c + +--- a/test/Makefile.am ++++ b/test/Makefile.am +@@ -2,7 +2,8 @@ SUBDIRS=. lsb + + check_PROGRAMS=control pcm pcm_min latency seq \ + playmidi1 timer rawmidi midiloop \ +- oldapi queue_timer namehint client_event_filter chmap ++ oldapi queue_timer namehint client_event_filter \ ++ chmap audio_time + + control_LDADD=../src/libasound.la + pcm_LDADD=../src/libasound.la +@@ -19,6 +20,7 @@ namehint_LDADD=../src/libasound.la + client_event_filter_LDADD=../src/libasound.la + code_CFLAGS=-Wall -pipe -g -O2 + chmap_LDADD=../src/libasound.la ++audio_time_LDADD=../src/libasound.la + + INCLUDES=-I$(top_srcdir)/include + AM_CFLAGS=-Wall -pipe -g +--- /dev/null ++++ b/test/audio_time.c +@@ -0,0 +1,237 @@ ++/* ++ * This program only tracks the difference between system time ++ * and audio time, as reported in snd_pcm_status(). It should be ++ * helpful to verify the information reported by drivers. ++ */ ++ ++#include "../include/asoundlib.h" ++#include ++ ++static char *device = "hw:0,0"; ++ ++snd_output_t *output = NULL; ++ ++long long timestamp2ns(snd_htimestamp_t t) ++{ ++ long long nsec; ++ ++ nsec = t.tv_sec * 1000000000; ++ nsec += t.tv_nsec; ++ ++ return nsec; ++} ++ ++long long timediff(snd_htimestamp_t t1, snd_htimestamp_t t2) ++{ ++ long long nsec1, nsec2; ++ ++ nsec1 = timestamp2ns(t1); ++ nsec2 = timestamp2ns(t2); ++ ++ return nsec1 - nsec2; ++} ++ ++void gettimestamp(snd_pcm_t *handle, snd_htimestamp_t *timestamp, ++ snd_htimestamp_t *trigger_timestamp, ++ snd_pcm_uframes_t *avail, snd_pcm_sframes_t *delay) ++{ ++ int err; ++ snd_pcm_status_t *status; ++ ++ snd_pcm_status_alloca(&status); ++ if ((err = snd_pcm_status(handle, status)) < 0) { ++ printf("Stream status error: %s\n", snd_strerror(err)); ++ exit(0); ++ } ++ snd_pcm_status_get_trigger_htstamp(status, trigger_timestamp); ++ snd_pcm_status_get_htstamp(status, timestamp); ++ *avail = snd_pcm_status_get_avail(status); ++ *delay = snd_pcm_status_get_delay(status); ++} ++ ++#define PERIOD 6000 ++#define PCM_LINK /* sync start for playback and capture */ ++#define TRACK_CAPTURE /* dump capture timing info */ ++#define TRACK_PLAYBACK /* dump playback timing info */ ++#define PLAYBACK_BUFFERS 4 ++ ++ ++int main(void) ++{ ++ int err; ++ unsigned int i; ++ snd_pcm_t *handle_p = NULL; ++ snd_pcm_t *handle_c = NULL; ++ snd_pcm_sframes_t frames; ++ snd_htimestamp_t tstamp_c, tstamp_p; ++ snd_htimestamp_t trigger_tstamp_c, trigger_tstamp_p; ++ unsigned char buffer_p[PERIOD*4*4]; ++ unsigned char buffer_c[PERIOD*4*4]; ++ ++ snd_pcm_sw_params_t *swparams_p; ++ snd_pcm_sw_params_t *swparams_c; ++ ++ snd_pcm_uframes_t curr_count_c; ++ snd_pcm_uframes_t frame_count_c = 0; ++ snd_pcm_uframes_t curr_count_p; ++ snd_pcm_uframes_t frame_count_p = 0; ++ ++ snd_pcm_sframes_t delay_p, delay_c; ++ snd_pcm_uframes_t avail_p, avail_c; ++ ++ if ((err = snd_pcm_open(&handle_p, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { ++ printf("Playback open error: %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ if ((err = snd_pcm_set_params(handle_p, ++ SND_PCM_FORMAT_S16, ++ SND_PCM_ACCESS_RW_INTERLEAVED, ++ 2, ++ 48000, ++ 0, ++ 500000)) < 0) { /* 0.5sec */ ++ printf("Playback open error: %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++ snd_pcm_sw_params_alloca(&swparams_p); ++ /* get the current swparams */ ++ err = snd_pcm_sw_params_current(handle_p, swparams_p); ++ if (err < 0) { ++ printf("Unable to determine current swparams_p: %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++ /* enable tstamp */ ++ err = snd_pcm_sw_params_set_tstamp_mode(handle_p, swparams_p, SND_PCM_TSTAMP_ENABLE); ++ if (err < 0) { ++ printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++ /* write the sw parameters */ ++ err = snd_pcm_sw_params(handle_p, swparams_p); ++ if (err < 0) { ++ printf("Unable to set swparams_p : %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++ if ((err = snd_pcm_open(&handle_c, device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) { ++ printf("Capture open error: %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ if ((err = snd_pcm_set_params(handle_c, ++ SND_PCM_FORMAT_S16, ++ SND_PCM_ACCESS_RW_INTERLEAVED, ++ 2, ++ 48000, ++ 0, ++ 500000)) < 0) { /* 0.5sec */ ++ printf("Capture open error: %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++ snd_pcm_sw_params_alloca(&swparams_c); ++ /* get the current swparams */ ++ err = snd_pcm_sw_params_current(handle_c, swparams_c); ++ if (err < 0) { ++ printf("Unable to determine current swparams_c: %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++ /* enable tstamp */ ++ err = snd_pcm_sw_params_set_tstamp_mode(handle_c, swparams_c, SND_PCM_TSTAMP_ENABLE); ++ if (err < 0) { ++ printf("Unable to set tstamp mode : %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++ /* write the sw parameters */ ++ err = snd_pcm_sw_params(handle_c, swparams_c); ++ if (err < 0) { ++ printf("Unable to set swparams_c : %s\n", snd_strerror(err)); ++ goto _exit; ++ } ++ ++#ifdef PCM_LINK ++ if ((err = snd_pcm_link(handle_c, handle_p)) < 0) { ++ printf("Streams link error: %s\n", snd_strerror(err)); ++ exit(0); ++ } ++#endif ++ ++ i = PLAYBACK_BUFFERS; ++ while (i--) { ++ frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); ++ if (frames < 0) { ++ printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); ++ goto _exit; ++ } ++ frame_count_p += frames; ++ } ++ ++ if (PLAYBACK_BUFFERS != 4) ++ snd_pcm_start(handle_p); ++ ++#ifndef PCM_LINK ++ /* need to start capture explicitly */ ++ snd_pcm_start(handle_c); ++#endif ++ ++ while (1) { ++ ++ frames = snd_pcm_wait(handle_c, -1); ++ if (frames < 0) { ++ printf("snd_pcm_wait failed: %s\n", snd_strerror(frames)); ++ goto _exit; ++ } ++ ++ frames = snd_pcm_readi(handle_c, buffer_c, PERIOD); ++ if (frames < 0) { ++ printf("snd_pcm_readi failed: %s\n", snd_strerror(frames)); ++ goto _exit; ++ } ++ frame_count_c += frames; ++ ++ frames = snd_pcm_writei(handle_p, buffer_p, PERIOD); ++ if (frames < 0) { ++ printf("snd_pcm_writei failed: %s\n", snd_strerror(frames)); ++ goto _exit; ++ } ++ ++ frame_count_p += frames; ++ ++#if defined(TRACK_PLAYBACK) ++ gettimestamp(handle_p, &tstamp_p, &trigger_tstamp_p, &avail_p, &delay_p); ++ ++ curr_count_p = frame_count_p - delay_p; /* written minus queued */ ++ ++ printf("playback: systime: %lli nsec, sample time %lli nsec \tsystime delta %lli \n", ++ timediff(tstamp_p,trigger_tstamp_p), ++ (long long)round(((float)curr_count_p * 1000000000.0 / 48000.0)), ++ timediff(tstamp_p, trigger_tstamp_p) - (long long)round((double)curr_count_p * 1000000000.0 / 48000.0) ++ ); ++#endif ++ ++#if defined(TRACK_CAPTURE) ++ gettimestamp(handle_c, &tstamp_c, &trigger_tstamp_c, &avail_c, &delay_c); ++ ++ curr_count_c = frame_count_c + delay_c; /* read plus queued */ ++ ++ printf("\t capture: systime: %lli nsec, sample time %lli nsec \tsystime delta %lli \n", ++ timediff(tstamp_c,trigger_tstamp_c), ++ (long long)round(((float)curr_count_c * 1000000000.0 / 48000.0)), ++ timediff(tstamp_c, trigger_tstamp_c) - (long long)round((double)curr_count_c * 1000000000.0 / 48000.0) ++ ); ++#endif ++ ++ } ++ ++_exit: ++ if (handle_p) ++ snd_pcm_close(handle_p); ++ if (handle_c) ++ snd_pcm_close(handle_c); ++ ++ return 0; ++} diff --git a/0030-PCM-Fix-the-invalid-snd_pcm_close-calls-in-rate-plug.patch b/0030-PCM-Fix-the-invalid-snd_pcm_close-calls-in-rate-plug.patch new file mode 100644 index 0000000..8879940 --- /dev/null +++ b/0030-PCM-Fix-the-invalid-snd_pcm_close-calls-in-rate-plug.patch @@ -0,0 +1,49 @@ +From 4bdb09126a32feb4394eaeb1d400d87e7c968770 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Wed, 10 Oct 2012 10:22:54 +0200 +Subject: [PATCH 30/30] PCM: Fix the invalid snd_pcm_close() calls in rate + plugin + +It happens in the error path, should call snd_pcm_free() instead. + +Signed-off-by: Takashi Iwai +--- + src/pcm/pcm_rate.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/src/pcm/pcm_rate.c ++++ b/src/pcm/pcm_rate.c +@@ -1394,13 +1394,13 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, + } + } else { + SNDERR("Invalid type for rate converter"); +- snd_pcm_close(pcm); ++ snd_pcm_free(pcm); + free(rate); + return -EINVAL; + } + if (err < 0) { + SNDERR("Cannot find rate converter"); +- snd_pcm_close(pcm); ++ snd_pcm_free(pcm); + free(rate); + return -ENOENT; + } +@@ -1409,7 +1409,7 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, + open_func = SND_PCM_RATE_PLUGIN_ENTRY(linear); + err = open_func(SND_PCM_RATE_PLUGIN_VERSION, &rate->obj, &rate->ops); + if (err < 0) { +- snd_pcm_close(pcm); ++ snd_pcm_free(pcm); + free(rate); + return err; + } +@@ -1418,7 +1418,7 @@ int snd_pcm_rate_open(snd_pcm_t **pcmp, + if (! rate->ops.init || ! (rate->ops.convert || rate->ops.convert_s16) || + ! rate->ops.input_frames || ! rate->ops.output_frames) { + SNDERR("Inproper rate plugin %s initialization", type); +- snd_pcm_close(pcm); ++ snd_pcm_free(pcm); + free(rate); + return err; + } diff --git a/alsa.changes b/alsa.changes index 33ba8c6..a28d301 100644 --- a/alsa.changes +++ b/alsa.changes @@ -1,3 +1,12 @@ +------------------------------------------------------------------- +Wed Oct 17 10:05:36 CEST 2012 - tiwai@suse.de + +- 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 + ------------------------------------------------------------------- Fri Sep 7 15:29:41 CEST 2012 - tiwai@suse.de diff --git a/alsa.spec b/alsa.spec index 45c3db8..04a7049 100644 --- a/alsa.spec +++ b/alsa.spec @@ -53,6 +53,36 @@ Source34: alsa-init.sh Source40: 50-alsa.conf Source41: install-snd-module # Patch: alsa-lib-git-fixes.diff +Patch1: 0001-Implement-the-channel-mapping-API.patch +Patch2: 0002-Implement-get_chmap-set_chmap-for-PCM-plug-route-and.patch +Patch3: 0003-Implement-get_chmap-set_chmap-for-PCM-extplug-ioplug.patch +Patch4: 0004-Add-test-chmap-program.patch +Patch5: 0005-Cache-the-chmap-operation-errors.patch +Patch6: 0006-Define-channel-map-position-enum-in-pcm.h.patch +Patch7: 0007-Follow-channel-position-definitions-to-mixer-channel.patch +Patch8: 0008-Add-SND_CHMAP_NA-and-bit-flag-definitions.patch +Patch9: 0009-PCM-Introduce-snd_pcm_chmap_t-and-snd_pcm_chmap_quer.patch +Patch10: 0010-PCM-Implement-snd_pcm_query_chmaps_from_hw.patch +Patch11: 0011-Fix-duplicated-channel-entry-in-test-chmap.c.patch +Patch12: 0012-PCM-Fix-prefix-for-snd_pcm_chmap_type-enum-members.patch +Patch13: 0013-PCM-Add-string-conversion-helper-functions-for-chmap.patch +Patch14: 0014-PCM-Add-SND_CHMAP_API_VERSION-definition.patch +Patch15: 0015-PCM-Add-snd_pcm_chmap_long_name.patch +Patch16: 0016-PCM-Add-query_chmaps-support-to-multi-plugin.patch +Patch17: 0017-PCM-Add-chmap-options-to-hw-and-null-plugins.patch +Patch18: 0018-PCM-Add-the-missing-query_chmaps-for-route-plugin.patch +Patch19: 0019-Add-chmap-override-definitions-for-Emu10k1-Audigy-an.patch +Patch20: 0020-PCM-Use-compounds-for-overriding-enhancing-chmaps.patch +Patch21: 0021-conf-Add-chmap-definitions-to-TRIDENT-and-SI7018-con.patch +Patch22: 0022-test-chmap-Fix-wrong-malloc-size.patch +Patch23: 0023-PCM-Define-MONO-and-other-channel-map-positions.patch +Patch24: 0024-PCM-Fix-the-conversion-from-string-to-chmap-position.patch +Patch25: 0025-PCM-A-few-doxygen-fixes-for-chmap-stuff.patch +Patch26: 0026-PCM-Fill-SND_CHMAP_NA-to-silent-channels-in-route-pl.patch +Patch27: 0027-PCM-Fix-infinite-loop-in-htimestamp-of-dmix-dsnoop-a.patch +Patch28: 0028-test-add-audio_time.patch +Patch30: 0030-PCM-Fix-the-invalid-snd_pcm_close-calls-in-rate-plug.patch +# Patch99: alsa-lib-doxygen-avoid-crash-for-11.3.diff Url: http://www.alsa-project.org/ BuildRoot: %{_tmppath}/%{name}-%{version}-build @@ -103,6 +133,35 @@ Architecture. %prep %setup -q -n alsa-lib-%{package_version} # %patch -p1 +%patch1 -p1 +%patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 +%patch6 -p1 +%patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 +%patch12 -p1 +%patch13 -p1 +%patch14 -p1 +%patch15 -p1 +%patch16 -p1 +%patch17 -p1 +%patch18 -p1 +%patch19 -p1 +%patch20 -p1 +%patch21 -p1 +%patch22 -p1 +%patch23 -p1 +%patch24 -p1 +%patch25 -p1 +%patch26 -p1 +%patch27 -p1 +%patch28 -p1 +%patch30 -p1 %if %suse_version == 1130 %patch99 -p1 %endif