diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 9 | ||||
-rw-r--r-- | src/map-file | 3 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.c | 297 | ||||
-rw-r--r-- | src/modules/alsa/alsa-util.h | 19 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-card.c | 196 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-sink.c | 16 | ||||
-rw-r--r-- | src/modules/alsa/module-alsa-source.c | 14 | ||||
-rw-r--r-- | src/pulse/def.h | 4 | ||||
-rw-r--r-- | src/pulse/introspect.c | 7 | ||||
-rw-r--r-- | src/pulse/introspect.h | 5 | ||||
-rw-r--r-- | src/pulse/proplist.c | 21 | ||||
-rw-r--r-- | src/pulse/proplist.h | 17 | ||||
-rw-r--r-- | src/pulse/volume.c | 91 | ||||
-rw-r--r-- | src/pulse/volume.h | 22 | ||||
-rw-r--r-- | src/pulsecore/card.c | 13 | ||||
-rw-r--r-- | src/pulsecore/card.h | 12 | ||||
-rw-r--r-- | src/pulsecore/cli-command.c | 1 | ||||
-rw-r--r-- | src/pulsecore/cli-text.c | 67 | ||||
-rw-r--r-- | src/pulsecore/module.c | 30 | ||||
-rw-r--r-- | src/pulsecore/module.h | 7 | ||||
-rw-r--r-- | src/pulsecore/protocol-native.c | 14 | ||||
-rw-r--r-- | src/pulsecore/strbuf.c | 6 | ||||
-rw-r--r-- | src/pulsecore/strbuf.h | 3 | ||||
-rw-r--r-- | src/tests/voltest.c | 29 | ||||
-rw-r--r-- | src/utils/pactl.c | 212 |
25 files changed, 842 insertions, 273 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 9dd44657..e8f0a820 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -925,7 +925,8 @@ if HAVE_ALSA modlibexec_LTLIBRARIES += \ libalsa-util.la \ module-alsa-sink.la \ - module-alsa-source.la + module-alsa-source.la \ + module-alsa-card.la endif if HAVE_SOLARIS @@ -1044,6 +1045,7 @@ SYMDEF_FILES = \ modules/oss/module-oss-symdef.h \ modules/alsa/module-alsa-sink-symdef.h \ modules/alsa/module-alsa-source-symdef.h \ + modules/alsa/module-alsa-card-symdef.h \ modules/module-solaris-symdef.h \ modules/module-waveout-symdef.h \ modules/module-detect-symdef.h \ @@ -1259,6 +1261,11 @@ module_alsa_source_la_LDFLAGS = $(MODULE_LDFLAGS) module_alsa_source_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la module_alsa_source_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) +module_alsa_card_la_SOURCES = modules/alsa/module-alsa-card.c +module_alsa_card_la_LDFLAGS = $(MODULE_LDFLAGS) +module_alsa_card_la_LIBADD = $(AM_LIBADD) $(ASOUNDLIB_LIBS) libalsa-util.la libpulsecore-@PA_MAJORMINORMICRO@.la libpulsecommon-@PA_MAJORMINORMICRO@.la libpulse.la +module_alsa_card_la_CFLAGS = $(AM_CFLAGS) $(ASOUNDLIB_CFLAGS) + # Solaris module_solaris_la_SOURCES = modules/module-solaris.c diff --git a/src/map-file b/src/map-file index 0247933a..9c9e1c7c 100644 --- a/src/map-file +++ b/src/map-file @@ -100,10 +100,12 @@ pa_cvolume_avg; pa_cvolume_channels_equal_to; pa_cvolume_compatible; pa_cvolume_equal; +pa_cvolume_get_balance; pa_cvolume_init; pa_cvolume_max; pa_cvolume_remap; pa_cvolume_set; +pa_cvolume_set_balance; pa_cvolume_snprint; pa_cvolume_valid; pa_ext_stream_restore_delete; @@ -159,6 +161,7 @@ pa_proplist_setf; pa_proplist_sets; pa_proplist_size; pa_proplist_to_string; +pa_proplist_to_string_sep; pa_proplist_unset; pa_proplist_unset_many; pa_proplist_update; diff --git a/src/modules/alsa/alsa-util.c b/src/modules/alsa/alsa-util.c index 04d23e0e..65221764 100644 --- a/src/modules/alsa/alsa-util.c +++ b/src/modules/alsa/alsa-util.c @@ -314,8 +314,8 @@ int pa_alsa_set_hw_params( pa_bool_t require_exact_channel_number) { int ret = -1; - snd_pcm_uframes_t _period_size = *period_size; - unsigned int _periods = *periods; + snd_pcm_uframes_t _period_size = period_size ? *period_size : 0; + unsigned int _periods = periods ? *periods : 0; snd_pcm_uframes_t buffer_size; unsigned int r = ss->rate; unsigned int c = ss->channels; @@ -327,8 +327,6 @@ int pa_alsa_set_hw_params( pa_assert(pcm_handle); pa_assert(ss); - pa_assert(periods); - pa_assert(period_size); snd_pcm_hw_params_alloca(&hwparams); @@ -361,10 +359,6 @@ int pa_alsa_set_hw_params( if ((ret = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &r, NULL)) < 0) goto finish; - /* Adjust the buffer sizes, if we didn't get the rate we were asking for */ - _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); - tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); - if (require_exact_channel_number) { if ((ret = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, c)) < 0) goto finish; @@ -373,50 +367,56 @@ int pa_alsa_set_hw_params( goto finish; } - if (_use_tsched) { - _period_size = tsched_size; - _periods = 1; + if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) + goto finish; - pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0); - pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); - } + if (_period_size && tsched_size && _periods) { + /* Adjust the buffer sizes, if we didn't get the rate we were asking for */ + _period_size = (snd_pcm_uframes_t) (((uint64_t) _period_size * r) / ss->rate); + tsched_size = (snd_pcm_uframes_t) (((uint64_t) tsched_size * r) / ss->rate); - buffer_size = _periods * _period_size; + if (_use_tsched) { + _period_size = tsched_size; + _periods = 1; - if ((ret = snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams)) < 0) - goto finish; + pa_assert_se(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_size) == 0); + pa_log_debug("Maximum hw buffer size is %u ms", (unsigned) buffer_size * 1000 / r); + } + + buffer_size = _periods * _period_size; - if (_periods > 0) { + if (_periods > 0) { - /* First we pass 0 as direction to get exactly what we asked - * for. That this is necessary is presumably a bug in ALSA */ + /* First we pass 0 as direction to get exactly what we asked + * for. That this is necessary is presumably a bug in ALSA */ - dir = 0; - if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) { - dir = 1; + dir = 0; if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) { - dir = -1; - if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) - goto finish; + dir = 1; + if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) { + dir = -1; + if ((ret = snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &_periods, &dir)) < 0) + goto finish; + } } } - } - if (_period_size > 0) - if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) - goto finish; + if (_period_size > 0) + if ((ret = snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_size)) < 0) + goto finish; + } if ((ret = snd_pcm_hw_params(pcm_handle, hwparams)) < 0) goto finish; if (ss->rate != r) - pa_log_warn("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r); + pa_log_info("Device %s doesn't support %u Hz, changed to %u Hz.", snd_pcm_name(pcm_handle), ss->rate, r); if (ss->channels != c) - pa_log_warn("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c); + pa_log_info("Device %s doesn't support %u channels, changed to %u.", snd_pcm_name(pcm_handle), ss->channels, c); if (ss->format != f) - pa_log_warn("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f)); + pa_log_info("Device %s doesn't support sample format %s, changed to %s.", snd_pcm_name(pcm_handle), pa_sample_format_to_string(ss->format), pa_sample_format_to_string(f)); if ((ret = snd_pcm_prepare(pcm_handle)) < 0) goto finish; @@ -434,8 +434,11 @@ int pa_alsa_set_hw_params( pa_assert(_periods > 0); pa_assert(_period_size > 0); - *periods = _periods; - *period_size = _period_size; + if (periods) + *periods = _periods; + + if (period_size) + *period_size = _period_size; if (use_mmap) *use_mmap = _use_mmap; @@ -488,14 +491,7 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min) { return 0; } -struct device_info { - pa_channel_map map; - const char *alsa_name; - const char *description; - const char *name; -}; - -static const struct device_info device_table[] = { +static const struct pa_alsa_profile_info device_table[] = { {{ 1, { PA_CHANNEL_POSITION_MONO }}, "hw", "Analog Mono", @@ -602,7 +598,6 @@ snd_pcm_t *pa_alsa_open_by_device_id( int i; int direction = 1; - int err; char *d; snd_pcm_t *pcm_handle; @@ -622,76 +617,47 @@ snd_pcm_t *pa_alsa_open_by_device_id( i = 0; for (;;) { - pa_sample_spec try_ss; - pa_bool_t reformat; if ((direction > 0) == channel_map_superset(&device_table[i].map, map)) { + pa_sample_spec try_ss; + pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name); d = pa_sprintf_malloc("%s:%s", device_table[i].alsa_name, dev_id); - reformat = FALSE; - for (;;) { - pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with"); - - /* We don't pass SND_PCM_NONBLOCK here, since alsa-lib <= - * 1.0.17a would then ignore the SND_PCM_NO_xxx - * flags. Instead we enable nonblock mode afterwards via - * snd_pcm_nonblock(). Also see - * http://mailman.alsa-project.org/pipermail/alsa-devel/2008-August/010258.html */ - - if ((err = snd_pcm_open(&pcm_handle, d, mode, - /* SND_PCM_NONBLOCK| */ - SND_PCM_NO_AUTO_RESAMPLE| - SND_PCM_NO_AUTO_CHANNELS| - (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) { - pa_log_info("Couldn't open PCM device %s: %s", d, snd_strerror(err)); - break; - } - - try_ss.channels = device_table[i].map.channels; - try_ss.rate = ss->rate; - try_ss.format = ss->format; - - if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) { - - if (!reformat) { - reformat = TRUE; - snd_pcm_close(pcm_handle); - continue; - } - - if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) { - char *t; - - t = pa_sprintf_malloc("plug:%s", d); - pa_xfree(d); - d = t; - - reformat = FALSE; + try_ss.channels = device_table[i].map.channels; + try_ss.rate = ss->rate; + try_ss.format = ss->format; + + pcm_handle = pa_alsa_open_by_device_string( + d, + dev, + &try_ss, + map, + mode, + nfrags, + period_size, + tsched_size, + use_mmap, + use_tsched, + TRUE); - snd_pcm_close(pcm_handle); - continue; - } + pa_xfree(d); - pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err)); - snd_pcm_close(pcm_handle); - break; - } + if (pcm_handle) { *ss = try_ss; *map = device_table[i].map; pa_assert(map->channels == ss->channels); - *dev = d; + if (config_description) *config_description = device_table[i].description; if (config_name) *config_name = device_table[i].name; + return pcm_handle; } - - pa_xfree(d); } if (direction > 0) { @@ -742,7 +708,7 @@ snd_pcm_t *pa_alsa_open_by_device_id( d = pa_sprintf_malloc("hw:%s", dev_id); pa_log_debug("Trying %s as last resort...", d); - pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched); + pcm_handle = pa_alsa_open_by_device_string(d, dev, ss, map, mode, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE); pa_xfree(d); if (pcm_handle) { @@ -763,7 +729,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( snd_pcm_uframes_t *period_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, - pa_bool_t *use_tsched) { + pa_bool_t *use_tsched, + pa_bool_t require_exact_channel_number) { int err; char *d; @@ -771,11 +738,8 @@ snd_pcm_t *pa_alsa_open_by_device_string( pa_bool_t reformat = FALSE; pa_assert(device); - pa_assert(dev); pa_assert(ss); pa_assert(map); - pa_assert(nfrags); - pa_assert(period_size); d = pa_xstrdup(device); @@ -793,12 +757,12 @@ snd_pcm_t *pa_alsa_open_by_device_string( SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| (reformat ? 0 : SND_PCM_NO_AUTO_FORMAT))) < 0) { - pa_log("Error opening PCM device %s: %s", d, snd_strerror(err)); + pa_log_info("Error opening PCM device %s: %s", d, snd_strerror(err)); pa_xfree(d); return NULL; } - if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, FALSE)) < 0) { + if ((err = pa_alsa_set_hw_params(pcm_handle, ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, require_exact_channel_number)) < 0) { if (!reformat) { reformat = TRUE; @@ -822,13 +786,14 @@ snd_pcm_t *pa_alsa_open_by_device_string( continue; } - pa_log("Failed to set hardware parameters on %s: %s", d, snd_strerror(err)); + pa_log_info("Failed to set hardware parameters on %s: %s", d, snd_strerror(err)); pa_xfree(d); snd_pcm_close(pcm_handle); return NULL; } - *dev = d; + if (dev) + *dev = d; if (ss->channels != map->channels) pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA); @@ -837,6 +802,94 @@ snd_pcm_t *pa_alsa_open_by_device_string( } } +int pa_alsa_probe_profiles( + const char *dev_id, + const pa_sample_spec *ss, + void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata), + void *userdata) { + + const pa_alsa_profile_info *i; + + pa_assert(dev_id); + pa_assert(ss); + pa_assert(cb); + + /* We try each combination of playback/capture. We also try to + * open only for capture resp. only for sink. Don't get confused + * by the trailing entry in device_table we use for this! */ + + for (i = device_table; i < device_table + PA_ELEMENTSOF(device_table); i++) { + const pa_alsa_profile_info *j; + snd_pcm_t *pcm_i = NULL; + + if (i->alsa_name) { + char *id; + pa_sample_spec try_ss; + pa_channel_map try_map; + + pa_log_debug("Checking for playback on %s (%s)", i->name, i->alsa_name); + id = pa_sprintf_malloc("%s:%s", i->alsa_name, dev_id); + + try_ss = *ss; + try_ss.channels = i->map.channels; + try_map = i->map; + + pcm_i = pa_alsa_open_by_device_string( + id, NULL, + &try_ss, &try_map, + SND_PCM_STREAM_PLAYBACK, + NULL, NULL, 0, NULL, NULL, + TRUE); + + pa_xfree(id); + + if (!pcm_i) + continue; + } + + for (j = device_table; j < device_table + PA_ELEMENTSOF(device_table); j++) { + snd_pcm_t *pcm_j = NULL; + + if (j->alsa_name) { + char *jd; + pa_sample_spec try_ss; + pa_channel_map try_map; + + pa_log_debug("Checking for capture on %s (%s)", j->name, j->alsa_name); + jd = pa_sprintf_malloc("%s:%s", j->alsa_name, dev_id); + + try_ss = *ss; + try_ss.channels = j->map.channels; + try_map = j->map; + + pcm_j = pa_alsa_open_by_device_string( + jd, NULL, + &try_ss, &try_map, + SND_PCM_STREAM_CAPTURE, + NULL, NULL, 0, NULL, NULL, + TRUE); + + pa_xfree(jd); + + if (!pcm_j) + continue; + } + + if (pcm_j) + snd_pcm_close(pcm_j); + + if (i->alsa_name || j->alsa_name) + cb(i->alsa_name ? i : NULL, + j->alsa_name ? j : NULL, userdata); + } + + if (pcm_i) + snd_pcm_close(pcm_i); + } + + return TRUE; +} + int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev) { int err; @@ -1151,7 +1204,26 @@ void pa_alsa_redirect_errors_dec(void) { snd_lib_error_set_handler(NULL); } -void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { +void pa_alsa_init_proplist_card(pa_proplist *p, int card) { + char *cn, *lcn; + + pa_assert(p); + pa_assert(card >= 0); + + pa_proplist_setf(p, "alsa.card", "%i", card); + + if (snd_card_get_name(card, &cn) >= 0) { + pa_proplist_sets(p, "alsa.card_name", cn); + free(cn); + } + + if (snd_card_get_longname(card, &lcn) >= 0) { + pa_proplist_sets(p, "alsa.long_card_name", lcn); + free(lcn); + } +} + +void pa_alsa_init_proplist_pcm(pa_proplist *p, snd_pcm_info_t *pcm_info) { static const char * const alsa_class_table[SND_PCM_CLASS_LAST+1] = { [SND_PCM_CLASS_GENERIC] = "generic", @@ -1172,8 +1244,7 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { snd_pcm_class_t class; snd_pcm_subclass_t subclass; - const char *n, *id, *sdn; - char *cn = NULL, *lcn = NULL; + const char *n, *id, *sdn, *cn; int card; pa_assert(p); @@ -1206,13 +1277,8 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { pa_proplist_setf(p, "alsa.device", "%u", snd_pcm_info_get_device(pcm_info)); if ((card = snd_pcm_info_get_card(pcm_info)) >= 0) { - pa_proplist_setf(p, "alsa.card", "%i", card); - - if (snd_card_get_name(card, &cn) >= 0) - pa_proplist_sets(p, "alsa.card_name", cn); - - if (snd_card_get_longname(card, &lcn) >= 0) - pa_proplist_sets(p, "alsa.long_card_name", lcn); + pa_alsa_init_proplist_card(p, card); + cn = pa_proplist_gets(p, "alsa.card_name"); } if (cn && n) @@ -1221,9 +1287,6 @@ void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info) { pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, cn); else if (n) pa_proplist_sets(p, PA_PROP_DEVICE_DESCRIPTION, n); - - free(lcn); - free(cn); } int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents) { diff --git a/src/modules/alsa/alsa-util.h b/src/modules/alsa/alsa-util.h index ce5f0eb6..f58ec8e0 100644 --- a/src/modules/alsa/alsa-util.h +++ b/src/modules/alsa/alsa-util.h @@ -78,7 +78,21 @@ snd_pcm_t *pa_alsa_open_by_device_string( snd_pcm_uframes_t *period_size, snd_pcm_uframes_t tsched_size, pa_bool_t *use_mmap, - pa_bool_t *use_tsched); + pa_bool_t *use_tsched, + pa_bool_t require_exact_channel_number); + +typedef struct pa_alsa_profile_info { + pa_channel_map map; + const char *alsa_name; + const char *description; + const char *name; +} pa_alsa_profile_info; + +int pa_alsa_probe_profiles( + const char *dev_id, + const pa_sample_spec *ss, + void (*cb)(const pa_alsa_profile_info *sink, const pa_alsa_profile_info *source, void *userdata), + void *userdata); int pa_alsa_calc_mixer_map(snd_mixer_elem_t *elem, const pa_channel_map *channel_map, snd_mixer_selem_channel_id_t mixer_map[], pa_bool_t playback); @@ -88,7 +102,8 @@ void pa_alsa_dump_status(snd_pcm_t *pcm); void pa_alsa_redirect_errors_inc(void); void pa_alsa_redirect_errors_dec(void); -void pa_alsa_init_proplist(pa_proplist *p, snd_pcm_info_t *pcm_info); +void pa_alsa_init_proplist_pcm(pa_proplist *p, snd_pcm_info_t *pcm_info); +void pa_alsa_init_proplist_card(pa_proplist *p, int card); int pa_alsa_recover_from_poll(snd_pcm_t *pcm, int revents); diff --git a/src/modules/alsa/module-alsa-card.c b/src/modules/alsa/module-alsa-card.c new file mode 100644 index 00000000..2cc8a151 --- /dev/null +++ b/src/modules/alsa/module-alsa-card.c @@ -0,0 +1,196 @@ +/*** + This file is part of PulseAudio. + + Copyright 2009 Lennart Poettering + + PulseAudio is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 2 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> +#include <pulsecore/core-util.h> +#include <pulsecore/modargs.h> + +#include "alsa-util.h" +#include "module-alsa-card-symdef.h" + +PA_MODULE_AUTHOR("Lennart Poettering"); +PA_MODULE_DESCRIPTION("ALSA Card"); +PA_MODULE_VERSION(PACKAGE_VERSION); +PA_MODULE_LOAD_ONCE(FALSE); +PA_MODULE_USAGE( + "name=<name for the sink/source> " + "device_id=<ALSA card index> " + "format=<sample format> " + "rate=<sample rate> " + "fragments=<number of fragments> " + "fragment_size=<fragment size> " + "mmap=<enable memory mapping?> " + "tsched=<enable system timer based scheduling mode?> " + "tsched_buffer_size=<buffer size when using timer based scheduling> " + "tsched_buffer_watermark=<lower fill watermark> " + "profile=<profile name>"); + +static const char* const valid_modargs[] = { + "sink_name", + "device", + "device_id", + "format", + "rate", + "channels", + "channel_map", + "fragments", + "fragment_size", + "mmap", + "tsched", + "tsched_buffer_size", + "tsched_buffer_watermark", + NULL +}; + +#define DEFAULT_DEVICE_ID "0" + +struct userdata { + pa_core *core; + pa_module *module; + + char *device_id; + + pa_card *card; +}; + +struct profile_data { + const pa_alsa_profile_info *sink, *source; +}; + +static void enumerate_cb( + const pa_alsa_profile_info *sink, + const pa_alsa_profile_info *source, + void *userdata) { + + pa_hashmap *profiles = (pa_hashmap *) userdata; + char *t, *n; + pa_card_profile *p; + struct profile_data *d; + + if (sink && source) { + n = pa_sprintf_malloc("%s+%s", sink->name, source->name); + t = pa_sprintf_malloc("Output %s + Input %s", sink->description, source->description); + } else if (sink) { + n = pa_xstrdup(sink->name); + t = pa_sprintf_malloc("Output %s", sink->description); + } else { + pa_assert(source); + n = pa_xstrdup(source->name); + t = pa_sprintf_malloc("Input %s", source->description); + } + + pa_log_info("Found output profile '%s'", t); + + p = pa_card_profile_new(n, t, sizeof(struct profile_data)); + + pa_xfree(t); + pa_xfree(n); + + p->n_sinks = !!sink; + p->n_sources = !!source; + + if (sink) + p->max_sink_channels = sink->map.channels; + if (source) + p->max_source_channels = source->map.channels; + + d = PA_CARD_PROFILE_DATA(p); + + d->sink = sink; + d->source = source; + + pa_hashmap_put(profiles, p->name, p); +} + +int pa__init(pa_module*m) { + pa_card_new_data data; + pa_modargs *ma; + int alsa_card_index; + struct userdata *u; + + pa_alsa_redirect_errors_inc(); + + pa_assert(m); + + if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { + pa_log("Failed to parse module arguments"); + goto fail; + } + + m->userdata = u = pa_xnew0(struct userdata, 1); + u->core = m->core; + u->module = m; + u->device_id = pa_xstrdup(pa_modargs_get_value(ma, "device_id", DEFAULT_DEVICE_ID)); + + if ((alsa_card_index = snd_card_get_index(u->device_id)) < 0) { + pa_log("Card '%s' doesn't exist: %s", u->device_id, snd_strerror(alsa_card_index)); + goto fail; + } + + pa_card_new_data_init(&data); + data.driver = __FILE__; + data.module = m; + pa_alsa_init_proplist_card(data.proplist, alsa_card_index); + pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id); + pa_card_new_data_set_name(&data, pa_modargs_get_value(ma, "name", u->device_id)); + + data.profiles = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); + if (pa_alsa_probe_profiles(u->device_id, &m->core->default_sample_spec, enumerate_cb, data.profiles) < 0) { + pa_card_new_data_done(&data); + goto fail; + } + + u->card = pa_card_new(m->core, &data); + pa_card_new_data_done(&data); + + return 0; + +fail: + + if (ma) + pa_modargs_free(ma); + + pa__done(m); + return -1; +} + +void pa__done(pa_module*m) { + struct userdata *u; + + pa_assert(m); + + if (!(u = m->userdata)) + goto finish; + + if (u->card) + pa_card_free(u->card); + + pa_xfree(u->device_id); + pa_xfree(u); + +finish: + snd_config_update_free_global(); + pa_alsa_redirect_errors_dec(); +} diff --git a/src/modules/alsa/module-alsa-sink.c b/src/modules/alsa/module-alsa-sink.c index dfa20557..62ce89cb 100644 --- a/src/modules/alsa/module-alsa-sink.c +++ b/src/modules/alsa/module-alsa-sink.c @@ -1308,10 +1308,9 @@ int pa__init(pa_module*m) { use_tsched = FALSE; } - u = pa_xnew0(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - m->userdata = u; u->use_mmap = use_mmap; u->use_tsched = use_tsched; u->first = TRUE; @@ -1351,7 +1350,7 @@ int pa__init(pa_module*m) { &ss, &map, SND_PCM_STREAM_PLAYBACK, &nfrags, &period_frames, tsched_frames, - &b, &d))) + &b, &d, FALSE))) goto fail; } @@ -1439,7 +1438,7 @@ int pa__init(pa_module*m) { pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); - pa_alsa_init_proplist(data.proplist, pcm_info); + pa_alsa_init_proplist_pcm(data.proplist, pcm_info); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); @@ -1633,10 +1632,8 @@ void pa__done(pa_module*m) { pa_assert(m); - if (!(u = m->userdata)) { - pa_alsa_redirect_errors_dec(); - return; - } + if (!(u = m->userdata)) + goto finish; if (u->sink) pa_sink_unlink(u->sink); @@ -1677,7 +1674,8 @@ void pa__done(pa_module*m) { pa_xfree(u->device_name); pa_xfree(u); - snd_config_update_free_global(); +finish: + snd_config_update_free_global(); pa_alsa_redirect_errors_dec(); } diff --git a/src/modules/alsa/module-alsa-source.c b/src/modules/alsa/module-alsa-source.c index f89b6e2e..7ca305f5 100644 --- a/src/modules/alsa/module-alsa-source.c +++ b/src/modules/alsa/module-alsa-source.c @@ -1142,10 +1142,9 @@ int pa__init(pa_module*m) { use_tsched = FALSE; } - u = pa_xnew0(struct userdata, 1); + m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; - m->userdata = u; u->use_mmap = use_mmap; u->use_tsched = use_tsched; u->rtpoll = pa_rtpoll_new(); @@ -1179,7 +1178,7 @@ int pa__init(pa_module*m) { &ss, &map, SND_PCM_STREAM_CAPTURE, &nfrags, &period_frames, tsched_frames, - &b, &d))) + &b, &d, FALSE))) goto fail; } @@ -1266,7 +1265,7 @@ int pa__init(pa_module*m) { pa_source_new_data_set_sample_spec(&data, &ss); pa_source_new_data_set_channel_map(&data, &map); - pa_alsa_init_proplist(data.proplist, pcm_info); + pa_alsa_init_proplist_pcm(data.proplist, pcm_info); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); @@ -1454,10 +1453,8 @@ void pa__done(pa_module*m) { pa_assert(m); - if (!(u = m->userdata)) { - pa_alsa_redirect_errors_dec(); - return; - } + if (!(u = m->userdata)) + goto finish; if (u->source) pa_source_unlink(u->source); @@ -1495,6 +1492,7 @@ void pa__done(pa_module*m) { pa_xfree(u->device_name); pa_xfree(u); +finish: snd_config_update_free_global(); pa_alsa_redirect_errors_dec(); } diff --git a/src/pulse/def.h b/src/pulse/def.h index 9e424d46..d43c070e 100644 --- a/src/pulse/def.h +++ b/src/pulse/def.h @@ -234,13 +234,13 @@ typedef enum pa_stream_flags { PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND = 0x8000U, /**< If set this stream won't be taken into account when we it is * checked whether the device this stream is connected to should - * auto-suspend. \ since 0.9.14 */ + * auto-suspend. \ since 0.9.15 */ PA_STREAM_START_UNMUTED = 0x10000U /**< Create in unmuted state. If neither PA_STREAM_START_UNMUTED * nor PA_STREAM_START_MUTED it is left to the server to decide * whether to create the stream in muted or in unmuted - * state. \since 0.9.14 */ + * state. \since 0.9.15 */ } pa_stream_flags_t; diff --git a/src/pulse/introspect.c b/src/pulse/introspect.c index bdc50e29..e7fa6d76 100644 --- a/src/pulse/introspect.c +++ b/src/pulse/introspect.c @@ -480,13 +480,16 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, while (!pa_tagstruct_eof(t)) { pa_module_info i; pa_bool_t auto_unload = FALSE; + memset(&i, 0, sizeof(i)); + i.proplist = pa_proplist_new(); if (pa_tagstruct_getu32(t, &i.index) < 0 || pa_tagstruct_gets(t, &i.name) < 0 || pa_tagstruct_gets(t, &i.argument) < 0 || pa_tagstruct_getu32(t, &i.n_used) < 0 || - pa_tagstruct_get_boolean(t, &auto_unload) < 0) { + (o->context->version < 15 && pa_tagstruct_get_boolean(t, &auto_unload) < 0) || + (o->context->version >= 15 && pa_tagstruct_get_proplist(t, i.proplist) < 0)) { pa_context_fail(o->context, PA_ERR_PROTOCOL); goto finish; } @@ -497,6 +500,8 @@ static void context_get_module_info_callback(pa_pdispatch *pd, uint32_t command, pa_module_info_cb_t cb = (pa_module_info_cb_t) o->callback; cb(o->context, &i, 0, o->userdata); } + + pa_proplist_free(i.proplist); } } diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index ae9bd5bc..a656d1cf 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -211,7 +211,7 @@ typedef struct pa_sink_info { pa_sink_flags_t flags; /**< Flags */ pa_proplist *proplist; /**< Property list \since 0.9.11 */ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */ - pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.14 */ + pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the output device. \since 0.9.15 */ } pa_sink_info; /** Callback prototype for pa_context_get_sink_info_by_name() and friends */ @@ -267,7 +267,7 @@ typedef struct pa_source_info { pa_source_flags_t flags; /**< Flags */ pa_proplist *proplist; /**< Property list \since 0.9.11 */ pa_usec_t configured_latency; /**< The latency this device has been configured to. \since 0.9.11 */ - pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.14 */ + pa_volume_t base_volume; /**< Some kind of "base" volume that refers to unamplified/unattenuated volume in the context of the input device. \since 0.9.15 */ } pa_source_info; /** Callback prototype for pa_context_get_source_info_by_name() and friends */ @@ -333,6 +333,7 @@ typedef struct pa_module_info { /** \cond fulldocs */ int auto_unload; /**< \deprecated Non-zero if this is an autoloaded module */ /** \endcond */ + pa_proplist *proplist; /**< Property list \since 0.9.15 */ } pa_module_info; /** Callback prototype for pa_context_get_module_info() and firends*/ diff --git a/src/pulse/proplist.c b/src/pulse/proplist.c index 60a92d44..282fe5cc 100644 --- a/src/pulse/proplist.c +++ b/src/pulse/proplist.c @@ -259,21 +259,24 @@ const char *pa_proplist_iterate(pa_proplist *p, void **state) { return prop->key; } -char *pa_proplist_to_string(pa_proplist *p) { +char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep) { const char *key; void *state = NULL; pa_strbuf *buf; pa_assert(p); + pa_assert(sep); buf = pa_strbuf_new(); while ((key = pa_proplist_iterate(p, &state))) { - const char *v; + if (!pa_strbuf_isempty(buf)) + pa_strbuf_puts(buf, sep); + if ((v = pa_proplist_gets(p, key))) - pa_strbuf_printf(buf, "%s = \"%s\"\n", key, v); + pa_strbuf_printf(buf, "%s = \"%s\"", key, v); else { const void *value; size_t nbytes; @@ -283,7 +286,7 @@ char *pa_proplist_to_string(pa_proplist *p) { c = pa_xmalloc(nbytes*2+1); pa_hexstr((const uint8_t*) value, nbytes, c, nbytes*2+1); - pa_strbuf_printf(buf, "%s = hex:%s\n", key, c); + pa_strbuf_printf(buf, "%s = hex:%s", key, c); pa_xfree(c); } } @@ -291,6 +294,16 @@ char *pa_proplist_to_string(pa_proplist *p) { return pa_strbuf_tostring_free(buf); } +char *pa_proplist_to_string(pa_proplist *p) { + char *s, *t; + + s = pa_proplist_to_string_sep(p, "\n"); + t = pa_sprintf_malloc("%s\n", s); + pa_xfree(s); + + return t; +} + /* Remove all whitepsapce from the beginning and the end of *s. *s may * be modified. (from conf-parser.c) */ #define WHITESPACE " \t\n" diff --git a/src/pulse/proplist.h b/src/pulse/proplist.h index 77f0399c..529871f8 100644 --- a/src/pulse/proplist.h +++ b/src/pulse/proplist.h @@ -128,6 +128,10 @@ PA_C_DECL_BEGIN #define PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE "device.buffering.fragment_size" #define PA_PROP_DEVICE_PROFILE_NAME "device.profile.name" #define PA_PROP_DEVICE_PROFILE_DESCRIPTION "device.profile.description" +#define PA_PROP_MODULE_AUTHOR "module.author" +#define PA_PROP_MODULE_DESCRIPTION "module.description" +#define PA_PROP_MODULE_USAGE "module.usage" +#define PA_PROP_MODULE_VERSION "module.version" /** A property list object. Basically a dictionary with UTF-8 strings * as keys and arbitrary data as values. \since 0.9.11 */ @@ -213,12 +217,19 @@ int pa_proplist_unset_many(pa_proplist *p, const char * const keys[]); * have any particular order. \since 0.9.11 */ const char *pa_proplist_iterate(pa_proplist *p, void **state); -/** Format the property list nicely as a human readable string. Call pa_xfree() on the result. \since - * 0.9.11 */ +/** Format the property list nicely as a human readable string. This + * works very much like pa_proplist_to_string_sep() and uses a newline + * as seperator and appends one final one. Call pa_xfree() on the + * result. \since 0.9.11 */ char *pa_proplist_to_string(pa_proplist *p); -/** Allocate a new property list and assign key/value from a human readable string. \since +/** Format the property list nicely as a human readable string and + * choose the seperator. Call pa_xfree() on the result. \since * 0.9.15 */ +char *pa_proplist_to_string_sep(pa_proplist *p, const char *sep); + +/** Allocate a new property list and assign key/value from a human + * readable string. \since 0.9.15 */ pa_proplist *pa_proplist_from_string(const char *str); /** Returns 1 if an entry for the specified key is existant in the diff --git a/src/pulse/volume.c b/src/pulse/volume.c index ace5c4d6..9191a074 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -402,3 +402,94 @@ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) { return v->channels == ss->channels; } + +static void get_avg_lr(const pa_channel_map *map, const pa_cvolume *v, pa_volume_t *l, pa_volume_t *r) { + int c; + pa_volume_t left = 0, right = 0; + unsigned n_left = 0, n_right = 0; + + pa_assert(v); + pa_assert(map); + pa_assert(map->channels == v->channels); + pa_assert(l); + pa_assert(r); + + for (c = 0; c < map->channels; c++) { + if (on_left(map->map[c])) { + left += v->values[c]; + n_left++; + } else if (on_right(map->map[c])) { + right += v->values[c]; + n_right++; + } + } + + *l = left / n_left; + *r = right / n_right; +} + +float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) { + pa_volume_t left, right; + + pa_assert(v); + pa_assert(map); + pa_assert(map->channels == v->channels); + + get_avg_lr(map, v, &left, &right); + + if (left == right) + return 0.0f; + + /* 1.0, 0.0 => -1.0 + 0.0, 1.0 => 1.0 + 0.0, 0.0 => 0.0 + 0.5, 0.5 => 0.0 + 1.0, 0.5 => -0.5 + 1.0, 0.25 => -0.75 + 0.75, 0.25 => -0.66 + 0.5, 0.25 => -0.5 */ + + if (left > right) + return -1.0f + ((float) right / (float) left); + else + return 1.0f - ((float) left / (float) right); +} + +pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance) { + pa_volume_t left, nleft, right, nright, m; + unsigned c; + + pa_assert(map->channels == v->channels); + pa_assert(map); + pa_assert(v); + pa_assert(new_balance >= -1.0f); + pa_assert(new_balance <= 1.0f); + + get_avg_lr(map, v, &left, &right); + + m = PA_MAX(left, right); + + if (new_balance <= 0) { + nright = (new_balance + 1.0f) * m; + nleft = m; + } else { + nleft = (1.0f - new_balance) * m; + nright = m; + } + + for (c = 0; c < map->channels; c++) { + if (on_left(map->map[c])) { + if (left == 0) + v->values[c] = 0; + else + v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nleft) / (uint64_t) left); + } else if (on_right(map->map[c])) { + if (right == 0) + v->values[c] = 0; + else + v->values[c] = (pa_volume_t) (((uint64_t) v->values[c] * (uint64_t) nright) / (uint64_t) right); + } + } + + return v; +} diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 5815c906..38da5dfc 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -154,20 +154,20 @@ char *pa_sw_cvolume_snprint_dB(char *s, size_t l, const pa_cvolume *c); * pa_volume_snprint(). Please note that this value can change with * any release without warning and without being considered API or ABI * breakage. You should not use this definition anywhere where it - * might become part of an ABI. \since 0.9.14 */ + * might become part of an ABI. \since 0.9.15 */ #define PA_VOLUME_SNPRINT_MAX 10 -/** Pretty print a volume \since 0.9.14 */ +/** Pretty print a volume \since 0.9.15 */ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v); /** Maximum length of the strings returned by * pa_volume_snprint_dB(). Please note that this value can change with * any release without warning and without being considered API or ABI * breakage. You should not use this definition anywhere where it - * might become part of an ABI. \since 0.9.14 */ + * might become part of an ABI. \since 0.9.15 */ #define PA_SW_VOLUME_SNPRINT_DB_MAX 10 -/** Pretty print a volume but show dB values. \since 0.9.14 */ +/** Pretty print a volume but show dB values. \since 0.9.15 */ char *pa_sw_volume_snprint_dB(char *s, size_t l, pa_volume_t v); /** Return the average volume of all channels */ @@ -233,6 +233,20 @@ pa_cvolume *pa_cvolume_remap(pa_cvolume *v, pa_channel_map *from, pa_channel_map * the specified sample spec. \since 0.9.13 */ int pa_cvolume_compatible(const pa_cvolume *v, const pa_sample_spec *ss) PA_GCC_PURE; +/** Calculate a 'balance' value for the specified volume with the + * specified channel map. The return value will range from -1.0f + * (left) to +1.0f (right) \since 0.9.15 */ +float pa_cvolume_get_balance(const pa_channel_map *map, const pa_cvolume *v) PA_GCC_PURE; + +/** Adjust the 'balance' value for the specified volume with the + * specified channel map. v will be modified in place and + * returned. The balance is a value between -1.0f and +1.0f. This + * operation might not be reversable! Also, after this call + * pa_cvolume_get_balance() is not guaranteed to actually return the + * requested balance (e.g. when the input volume was zero anyway for + * all channels)- \since 0.9.15 */ +pa_cvolume* pa_cvolume_set_balance(const pa_channel_map *map, pa_cvolume *v, float new_balance); + PA_C_DECL_END #endif diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c index 99c0cc55..ec4a50c5 100644 --- a/src/pulsecore/card.c +++ b/src/pulsecore/card.c @@ -36,13 +36,14 @@ #include "card.h" -pa_card_profile *pa_card_profile_new(const char *name) { +pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra) { pa_card_profile *c; pa_assert(name); - c = pa_xnew0(pa_card_profile, 1); + c = pa_xmalloc(PA_ALIGN(sizeof(pa_card_profile)) + extra); c->name = pa_xstrdup(name); + c->description = pa_xstrdup(description); return c; } @@ -51,6 +52,7 @@ void pa_card_profile_free(pa_card_profile *c) { pa_assert(c); pa_xfree(c->name); + pa_xfree(c->description); pa_xfree(c); } @@ -122,7 +124,9 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) { c->profiles = data->profiles; data->profiles = NULL; - c->active_profile = data->active_profile; + if (!(c->active_profile = data->active_profile)) + if (c->profiles) + c->active_profile = pa_hashmap_first(c->profiles); data->active_profile = NULL; c->userdata = NULL; @@ -189,6 +193,9 @@ int pa_card_set_profile(pa_card *c, const char *name) { if (!(profile = pa_hashmap_get(c->profiles, name))) return -1; + if (c->active_profile == profile) + return 0; + if (c->set_profile(c, profile) < 0) return -1; diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h index e32e8809..b4e68b04 100644 --- a/src/pulsecore/card.h +++ b/src/pulsecore/card.h @@ -31,17 +31,20 @@ typedef struct pa_card pa_card; typedef struct pa_card_profile { char *name; + char *description; - pa_bool_t optical_sink:1; - pa_bool_t optical_source:1; - + /* We probably want to have different properties later on here */ unsigned n_sinks; unsigned n_sources; unsigned max_sink_channels; unsigned max_source_channels; + + /* .. followed by some implementation specific data */ } pa_card_profile; +#define PA_CARD_PROFILE_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_card_profile)))) + struct pa_card { uint32_t index; pa_core *core; @@ -65,6 +68,7 @@ struct pa_card { typedef struct pa_card_new_data { char *name; + char *description; pa_proplist *proplist; const char *driver; @@ -76,7 +80,7 @@ typedef struct pa_card_new_data { pa_bool_t namereg_fail:1; } pa_card_new_data; -pa_card_profile *pa_card_profile_new(const char *name); +pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra); void pa_card_profile_free(pa_card_profile *c); pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data); diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index 8f5d9bdd..f810579e 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -51,6 +51,7 @@ #include <pulsecore/shared.h> #include <pulsecore/core-util.h> #include <pulsecore/core-error.h> +#include <pulsecore/modinfo.h> #include "cli-command.h" diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 0f4a273d..79620398 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -54,6 +54,8 @@ char *pa_module_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u module(s) loaded.\n", pa_idxset_size(c->modules)); for (m = pa_idxset_first(c->modules, &idx); m; m = pa_idxset_next(c->modules, &idx)) { + char *t; + pa_strbuf_printf(s, " index: %u\n" "\tname: <%s>\n" "\targument: <%s>\n" @@ -64,6 +66,10 @@ char *pa_module_list_to_string(pa_core *c) { pa_strempty(m->argument), pa_module_get_n_used(m), pa_yes_no(m->load_once)); + + t = pa_proplist_to_string_sep(m->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); + pa_xfree(t); } return pa_strbuf_tostring_free(s); @@ -91,8 +97,8 @@ char *pa_client_list_to_string(pa_core *c) { if (client->module) pa_strbuf_printf(s, "\towner module: %u\n", client->module->index); - t = pa_proplist_to_string(client->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(client->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -107,7 +113,7 @@ char *pa_card_list_to_string(pa_core *c) { s = pa_strbuf_new(); - pa_strbuf_printf(s, "%u card(s) available in.\n", pa_idxset_size(c->cards)); + pa_strbuf_printf(s, "%u card(s) available.\n", pa_idxset_size(c->cards)); for (card = pa_idxset_first(c->cards, &idx); card; card = pa_idxset_next(c->cards, &idx)) { char *t; @@ -123,9 +129,22 @@ char *pa_card_list_to_string(pa_core *c) { if (card->module) pa_strbuf_printf(s, "\towner module: %u\n", card->module->index); - t = pa_proplist_to_string(card->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(card->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); + + if (card->profiles) { + pa_card_profile *p; + void *state = NULL; + + pa_strbuf_puts( + s, + "\tprofiles:\n"); + + while ((p = pa_hashmap_iterate(card->profiles, &state, NULL))) + pa_strbuf_printf(s, "\t\t%s: %s\n", p->name, p->description); + } + } return pa_strbuf_tostring_free(s); @@ -167,6 +186,7 @@ char *pa_sink_list_to_string(pa_core *c) { "\tflags: %s%s%s%s%s%s\n" "\tstate: %s\n" "\tvolume: %s%s%s\n" + "\t balance %0.2f\n" "\tbase volume: %s%s%s\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" @@ -192,6 +212,7 @@ char *pa_sink_list_to_string(pa_core *c) { pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)), sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "", + pa_cvolume_get_balance(&sink->channel_map, pa_sink_get_volume(sink, FALSE)), pa_volume_snprint(v, sizeof(v), sink->base_volume), sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "", @@ -213,8 +234,8 @@ char *pa_sink_list_to_string(pa_core *c) { if (sink->module) pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index); - t = pa_proplist_to_string(sink->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -257,6 +278,7 @@ char *pa_source_list_to_string(pa_core *c) { "\tflags: %s%s%s%s%s%s\n" "\tstate: %s\n" "\tvolume: %s%s%s\n" + "\t balance %0.2f\n" "\tbase volume: %s%s%s\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" @@ -280,6 +302,7 @@ char *pa_source_list_to_string(pa_core *c) { pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)), source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "", + pa_cvolume_get_balance(&source->channel_map, pa_source_get_volume(source, FALSE)), pa_volume_snprint(v, sizeof(v), source->base_volume), source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "", @@ -301,8 +324,8 @@ char *pa_source_list_to_string(pa_core *c) { if (source->module) pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index); - t = pa_proplist_to_string(source->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(source->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -373,8 +396,8 @@ char *pa_source_output_list_to_string(pa_core *c) { if (o->direct_on_input) pa_strbuf_printf(s, "\tdirect on input: %u\n", o->direct_on_input->index); - t = pa_proplist_to_string(o->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(o->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -399,7 +422,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_strbuf_printf(s, "%u sink input(s) available.\n", pa_idxset_size(c->sink_inputs)); for (i = pa_idxset_first(c->sink_inputs, &idx); i; i = pa_idxset_next(c->sink_inputs, &idx)) { - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t, clt[28]; pa_usec_t cl; if ((cl = pa_sink_input_get_requested_latency(i)) == (pa_usec_t) -1) @@ -417,6 +440,8 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tstate: %s\n" "\tsink: %u <%s>\n" "\tvolume: %s\n" + "\t %s\n" + "\t balance %0.2f\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\trequested latency: %s\n" @@ -436,6 +461,8 @@ char *pa_sink_input_list_to_string(pa_core *c) { state_table[pa_sink_input_get_state(i)], i->sink->index, i->sink->name, pa_cvolume_snprint(cv, sizeof(cv), pa_sink_input_get_volume(i)), + pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_input_get_volume(i)), + pa_cvolume_get_balance(&i->channel_map, pa_sink_input_get_volume(i)), pa_yes_no(pa_sink_input_get_mute(i)), (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC, clt, @@ -448,8 +475,8 @@ char *pa_sink_input_list_to_string(pa_core *c) { if (i->client) pa_strbuf_printf(s, "\tclient: %u <%s>\n", i->client->index, pa_strnull(pa_proplist_gets(i->client->proplist, PA_PROP_APPLICATION_NAME))); - t = pa_proplist_to_string(i->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(i->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } @@ -462,7 +489,7 @@ char *pa_scache_list_to_string(pa_core *c) { s = pa_strbuf_new(); - pa_strbuf_printf(s, "%u cache entries available.\n", c->scache ? pa_idxset_size(c->scache) : 0); + pa_strbuf_printf(s, "%u cache entrie(s) available.\n", c->scache ? pa_idxset_size(c->scache) : 0); if (c->scache) { pa_scache_entry *e; @@ -470,7 +497,7 @@ char *pa_scache_list_to_string(pa_core *c) { for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { double l = 0; - char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t; + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX] = "n/a", cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX] = "n/a", *t; if (e->memchunk.memblock) { pa_sample_spec_snprint(ss, sizeof(ss), &e->sample_spec); @@ -487,6 +514,8 @@ char *pa_scache_list_to_string(pa_core *c) { "\tlength: %lu\n" "\tduration: %0.1f s\n" "\tvolume: %s\n" + "\t %s\n" + "\t balance %0.2f\n" "\tlazy: %s\n" "\tfilename: <%s>\n", e->name, @@ -496,11 +525,13 @@ char *pa_scache_list_to_string(pa_core *c) { (long unsigned)(e->memchunk.memblock ? e->memchunk.length : 0), l, pa_cvolume_snprint(cv, sizeof(cv), &e->volume), + pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &e->volume), + e->memchunk.memblock ? pa_cvolume_get_balance(&e->channel_map, &e->volume) : 0.0f, pa_yes_no(e->lazy), e->filename ? e->filename : "n/a"); - t = pa_proplist_to_string(e->proplist); - pa_strbuf_printf(s, "\tproperties:\n%s", t); + t = pa_proplist_to_string_sep(e->proplist, "\n\t\t"); + pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); } } diff --git a/src/pulsecore/module.c b/src/pulsecore/module.c index c4dcb478..d470bb0b 100644 --- a/src/pulsecore/module.c +++ b/src/pulsecore/module.c @@ -33,12 +33,14 @@ #include <pulse/timeval.h> #include <pulse/xmalloc.h> +#include <pulse/proplist.h> #include <pulsecore/core-subscribe.h> #include <pulsecore/log.h> #include <pulsecore/core-util.h> #include <pulsecore/macro.h> #include <pulsecore/ltdl-helper.h> +#include <pulsecore/modinfo.h> #include "module.h" @@ -50,6 +52,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { pa_module *m = NULL; pa_bool_t (*load_once)(void); + pa_modinfo *mi; pa_assert(c); pa_assert(name); @@ -61,6 +64,7 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { m->name = pa_xstrdup(name); m->argument = pa_xstrdup(argument); m->load_once = FALSE; + m->proplist = pa_proplist_new(); if (!(m->dl = lt_dlopenext(name))) { pa_log("Failed to open module \"%s\": %s", name, lt_dlerror()); @@ -111,11 +115,28 @@ pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index); + if ((mi = pa_modinfo_get_by_handle(m->dl, name))) { + + if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR)) + pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author); + + if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION)) + pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description); + + if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION)) + pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version); + + pa_modinfo_free(mi); + } + return m; fail: if (m) { + if (m->proplist) + pa_proplist_free(m->proplist); + pa_xfree(m->argument); pa_xfree(m->name); @@ -137,6 +158,9 @@ static void pa_module_free(pa_module *m) { if (m->done) m->done(m); + if (m->proplist) + pa_proplist_free(m->proplist); + lt_dlclose(m->dl); pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index); @@ -234,12 +258,6 @@ void pa_module_unload_request_by_index(pa_core *c, uint32_t idx, pa_bool_t force pa_module_unload_request(m, force); } -pa_modinfo *pa_module_get_info(pa_module *m) { - pa_assert(m); - - return pa_modinfo_get_by_handle(m->dl, m->name); -} - int pa_module_get_n_used(pa_module*m) { pa_assert(m); diff --git a/src/pulsecore/module.h b/src/pulsecore/module.h index 986f0d22..6ab43dcf 100644 --- a/src/pulsecore/module.h +++ b/src/pulsecore/module.h @@ -27,8 +27,9 @@ typedef struct pa_module pa_module; +#include <pulse/proplist.h> + #include <pulsecore/core.h> -#include <pulsecore/modinfo.h> struct pa_module { pa_core *core; @@ -45,6 +46,8 @@ struct pa_module { pa_bool_t load_once:1; pa_bool_t unload_requested:1; + + pa_proplist *proplist; }; pa_module* pa_module_load(pa_core *c, const char *name, const char*argument); @@ -79,6 +82,4 @@ int pa_module_get_n_used(pa_module*m); pa_bool_t pa__load_once(void) { return b; } \ struct __stupid_useless_struct_to_allow_trailing_semicolon -pa_modinfo *pa_module_get_info(pa_module *m); - #endif diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 54122679..eb555050 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -2728,10 +2728,9 @@ static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_c if (c->version >= 13) pa_tagstruct_put_proplist(t, client->proplist); - } -static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { +static void module_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_module *module) { pa_assert(t); pa_assert(module); @@ -2739,7 +2738,12 @@ static void module_fill_tagstruct(pa_tagstruct *t, pa_module *module) { pa_tagstruct_puts(t, module->name); pa_tagstruct_puts(t, module->argument); pa_tagstruct_putu32(t, (uint32_t) pa_module_get_n_used(module)); - pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */ + + if (c->version < 15) + pa_tagstruct_put_boolean(t, FALSE); /* autoload is obsolete */ + + if (c->version >= 15) + pa_tagstruct_put_proplist(t, module->proplist); } static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sink_input *s) { @@ -2891,7 +2895,7 @@ static void command_get_info(pa_pdispatch *pd, uint32_t command, uint32_t tag, p else if (client) client_fill_tagstruct(c, reply, client); else if (module) - module_fill_tagstruct(reply, module); + module_fill_tagstruct(c, reply, module); else if (si) sink_input_fill_tagstruct(c, reply, si); else if (so) @@ -2946,7 +2950,7 @@ static void command_get_info_list(pa_pdispatch *pd, uint32_t command, uint32_t t else if (command == PA_COMMAND_GET_CLIENT_INFO_LIST) client_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_MODULE_INFO_LIST) - module_fill_tagstruct(reply, p); + module_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_SINK_INPUT_INFO_LIST) sink_input_fill_tagstruct(c, reply, p); else if (command == PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST) diff --git a/src/pulsecore/strbuf.c b/src/pulsecore/strbuf.c index 540faef9..8b952788 100644 --- a/src/pulsecore/strbuf.c +++ b/src/pulsecore/strbuf.c @@ -180,3 +180,9 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) { size *= 2; } } + +pa_bool_t pa_strbuf_isempty(pa_strbuf *sb) { + pa_assert(sb); + + return sb->length <= 0; +} diff --git a/src/pulsecore/strbuf.h b/src/pulsecore/strbuf.h index ac68d7be..1d2a588f 100644 --- a/src/pulsecore/strbuf.h +++ b/src/pulsecore/strbuf.h @@ -23,6 +23,7 @@ ***/ #include <pulse/gccmacro.h> +#include <pulsecore/macro.h> typedef struct pa_strbuf pa_strbuf; @@ -35,4 +36,6 @@ size_t pa_strbuf_printf(pa_strbuf *sb, const char *format, ...) PA_GCC_PRINTF_A void pa_strbuf_puts(pa_strbuf *sb, const char *t); void pa_strbuf_putsn(pa_strbuf *sb, const char *t, size_t m); +pa_bool_t pa_strbuf_isempty(pa_strbuf *sb); + #endif diff --git a/src/tests/voltest.c b/src/tests/voltest.c index 5bfc97e0..879d86b9 100644 --- a/src/tests/voltest.c +++ b/src/tests/voltest.c @@ -6,6 +6,8 @@ int main(int argc, char *argv[]) { pa_volume_t v; pa_cvolume cv; + float b; + pa_channel_map map; for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 256) { @@ -28,5 +30,32 @@ int main(int argc, char *argv[]) { } + map.channels = cv.channels = 2; + map.map[0] = PA_CHANNEL_POSITION_LEFT; + map.map[1] = PA_CHANNEL_POSITION_RIGHT; + + for (cv.values[0] = PA_VOLUME_MUTED; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096) + for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096) { + char s[PA_CVOLUME_SNPRINT_MAX]; + + printf("Volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&map, &cv)); + } + + for (cv.values[0] = PA_VOLUME_MUTED+4096; cv.values[0] <= PA_VOLUME_NORM*2; cv.values[0] += 4096) + for (cv.values[1] = PA_VOLUME_MUTED; cv.values[1] <= PA_VOLUME_NORM*2; cv.values[1] += 4096) + for (b = -1.0f; b <= 1.0f; b += 0.2f) { + char s[PA_CVOLUME_SNPRINT_MAX]; + pa_cvolume r; + float k; + + printf("Before: volume: [%s]; balance: %2.1f\n", pa_cvolume_snprint(s, sizeof(s), &cv), pa_cvolume_get_balance(&map, &cv)); + + r = cv; + pa_cvolume_set_balance(&map, &r, b); + + k = pa_cvolume_get_balance(&map, &r); + printf("After: volume: [%s]; balance: %2.1f (intended: %2.1f) %s\n", pa_cvolume_snprint(s, sizeof(s), &r), k, b, k < b-.05 || k > b+.5 ? "MISMATCH" : ""); + } + return 0; } diff --git a/src/utils/pactl.c b/src/utils/pactl.c index 2fc17330..1ff979e9 100644 --- a/src/utils/pactl.c +++ b/src/utils/pactl.c @@ -153,7 +153,13 @@ static void get_server_info_callback(pa_context *c, const pa_server_info *i, voi } static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) { - char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char + s[PA_SAMPLE_SPEC_SNPRINT_MAX], + cv[PA_CVOLUME_SNPRINT_MAX], + cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], + v[PA_VOLUME_SNPRINT_MAX], + vdb[PA_SW_VOLUME_SNPRINT_DB_MAX], + cm[PA_CHANNEL_MAP_SNPRINT_MAX]; char *pl; if (is_last < 0) { @@ -173,24 +179,36 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_ printf("\n"); nl = 1; - printf(_("*** Sink #%u ***\n" - "Name: %s\n" - "Driver: %s\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Owner Module: %u\n" - "Volume: %s\n" - "Monitor Source: %s\n" - "Latency: %0.0f usec, configured %0.0f usec\n" - "Flags: %s%s%s%s%s%s\n" - "Properties:\n%s"), + printf(_("Sink #%u\n" + "\tName: %s\n" + "\tDescription: %s\n" + "\tDriver: %s\n" + "\tSample Specification: %s\n" + "\tChannel Map: %s\n" + "\tOwner Module: %u\n" + "\tMute: %s\n" + "\tVolume: %s%s%s\n" + "\t balance %0.2f\n" + "\tBase Volume: %s%s%s\n" + "\tMonitor Source: %s\n" + "\tLatency: %0.0f usec, configured %0.0f usec\n" + "\tFlags: %s%s%s%s%s%s\n" + "\tProperties:\n\t\t%s\n"), i->index, i->name, + pa_strnull(i->description), pa_strnull(i->driver), pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), i->owner_module, - i->mute ? _("muted") : pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + pa_yes_no(i->mute), + pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", + i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "", + pa_cvolume_get_balance(&i->channel_map, &i->volume), + pa_volume_snprint(v, sizeof(v), i->base_volume), + i->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", + i->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "", pa_strnull(i->monitor_source_name), (double) i->latency, (double) i->configured_latency, i->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", @@ -199,13 +217,19 @@ static void get_sink_info_callback(pa_context *c, const pa_sink_info *i, int is_ i->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", i->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", i->flags & PA_SINK_LATENCY ? "LATENCY " : "", - pl = pa_proplist_to_string(i->proplist)); + pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); } static void get_source_info_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) { - char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char + s[PA_SAMPLE_SPEC_SNPRINT_MAX], + cv[PA_CVOLUME_SNPRINT_MAX], + cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], + v[PA_VOLUME_SNPRINT_MAX], + vdb[PA_SW_VOLUME_SNPRINT_DB_MAX], + cm[PA_CHANNEL_MAP_SNPRINT_MAX]; char *pl; if (is_last < 0) { @@ -225,24 +249,36 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int printf("\n"); nl = 1; - printf(_("*** Source #%u ***\n" - "Name: %s\n" - "Driver: %s\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Owner Module: %u\n" - "Volume: %s\n" - "Monitor of Sink: %s\n" - "Latency: %0.0f usec, configured %0.0f usec\n" - "Flags: %s%s%s%s%s%s\n" - "Properties:\n%s"), + printf(_("Source #%u\n" + "\tName: %s\n" + "\tDescription: %s\n" + "\tDriver: %s\n" + "\tSample Specification: %s\n" + "\tChannel Map: %s\n" + "\tOwner Module: %u\n" + "\tMute: %s\n" + "\tVolume: %s%s%s\n" + "\t balance %0.2f\n" + "\tBase Volume: %s%s%s\n" + "\tMonitor of Sink: %s\n" + "\tLatency: %0.0f usec, configured %0.0f usec\n" + "\tFlags: %s%s%s%s%s%s\n" + "\tProperties:\n\t\t%s\n"), i->index, i->name, + pa_strnull(i->description), pa_strnull(i->driver), pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), i->owner_module, - i->mute ? "muted" : pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + pa_yes_no(i->mute), + pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", + i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume) : "", + pa_cvolume_get_balance(&i->channel_map, &i->volume), + pa_volume_snprint(v, sizeof(v), i->base_volume), + i->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", + i->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), i->base_volume) : "", i->monitor_of_sink_name ? i->monitor_of_sink_name : _("n/a"), (double) i->latency, (double) i->configured_latency, i->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", @@ -251,13 +287,14 @@ static void get_source_info_callback(pa_context *c, const pa_source_info *i, int i->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", i->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", i->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", - pl = pa_proplist_to_string(i->proplist)); + pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); } static void get_module_info_callback(pa_context *c, const pa_module_info *i, int is_last, void *userdata) { char t[32]; + char *pl; if (is_last < 0) { fprintf(stderr, _("Failed to get module information: %s\n"), pa_strerror(pa_context_errno(c))); @@ -278,14 +315,18 @@ static void get_module_info_callback(pa_context *c, const pa_module_info *i, int snprintf(t, sizeof(t), "%u", i->n_used); - printf(_("*** Module #%u ***\n" - "Name: %s\n" - "Argument: %s\n" - "Usage counter: %s\n"), + printf(_("Module #%u\n" + "\tName: %s\n" + "\tArgument: %s\n" + "\tUsage counter: %s\n" + "\tProperties:\n\t\t%s\n"), i->index, i->name, i->argument ? i->argument : "", - i->n_used != PA_INVALID_INDEX ? t : _("n/a")); + i->n_used != PA_INVALID_INDEX ? t : _("n/a"), + pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); + + pa_xfree(pl); } static void get_client_info_callback(pa_context *c, const pa_client_info *i, int is_last, void *userdata) { @@ -311,20 +352,20 @@ static void get_client_info_callback(pa_context *c, const pa_client_info *i, int snprintf(t, sizeof(t), "%u", i->owner_module); - printf(_("*** Client #%u ***\n" - "Driver: %s\n" - "Owner Module: %s\n" - "Properties:\n%s"), + printf(_("Client #%u\n" + "\tDriver: %s\n" + "\tOwner Module: %s\n" + "\tProperties:\n\t\t%s\n"), i->index, pa_strnull(i->driver), i->owner_module != PA_INVALID_INDEX ? t : _("n/a"), - pl = pa_proplist_to_string(i->proplist)); + pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); } static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) { - char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char t[32], k[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; char *pl; if (is_last < 0) { @@ -347,18 +388,21 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info snprintf(t, sizeof(t), "%u", i->owner_module); snprintf(k, sizeof(k), "%u", i->client); - printf(_("*** Sink Input #%u ***\n" - "Driver: %s\n" - "Owner Module: %s\n" - "Client: %s\n" - "Sink: %u\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Volume: %s\n" - "Buffer Latency: %0.0f usec\n" - "Sink Latency: %0.0f usec\n" - "Resample method: %s\n" - "Properties:\n%s"), + printf(_("Sink Input #%u\n" + "\tDriver: %s\n" + "\tOwner Module: %s\n" + "\tClient: %s\n" + "\tSink: %u\n" + "\tSample Specification: %s\n" + "\tChannel Map: %s\n" + "\tMute: %s\n" + "\tVolume: %s\n" + "\t %s\n" + "\t balance %0.2f\n" + "\tBuffer Latency: %0.0f usec\n" + "\tSink Latency: %0.0f usec\n" + "\tResample method: %s\n" + "\tProperties:\n\t\t%s\n"), i->index, pa_strnull(i->driver), i->owner_module != PA_INVALID_INDEX ? t : _("n/a"), @@ -366,11 +410,14 @@ static void get_sink_input_info_callback(pa_context *c, const pa_sink_input_info i->sink, pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map), - i->mute ? _("muted") : pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + pa_yes_no(i->mute), + pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume), + pa_cvolume_get_balance(&i->channel_map, &i->volume), (double) i->buffer_usec, (double) i->sink_usec, i->resample_method ? i->resample_method : _("n/a"), - pl = pa_proplist_to_string(i->proplist)); + pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); } @@ -400,17 +447,17 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu snprintf(t, sizeof(t), "%u", i->owner_module); snprintf(k, sizeof(k), "%u", i->client); - printf(_("*** Source Output #%u ***\n" - "Driver: %s\n" - "Owner Module: %s\n" - "Client: %s\n" - "Source: %u\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Buffer Latency: %0.0f usec\n" - "Source Latency: %0.0f usec\n" - "Resample method: %s\n" - "Properties:\n%s"), + printf(_("Source Output #%u\n" + "\tDriver: %s\n" + "\tOwner Module: %s\n" + "\tClient: %s\n" + "\tSource: %u\n" + "\tSample Specification: %s\n" + "\tChannel Map: %s\n" + "\tBuffer Latency: %0.0f usec\n" + "\tSource Latency: %0.0f usec\n" + "\tResample method: %s\n" + "\tProperties:\n\t\t%s\n"), i->index, pa_strnull(i->driver), i->owner_module != PA_INVALID_INDEX ? t : _("n/a"), @@ -421,13 +468,13 @@ static void get_source_output_info_callback(pa_context *c, const pa_source_outpu (double) i->buffer_usec, (double) i->source_usec, i->resample_method ? i->resample_method : _("n/a"), - pl = pa_proplist_to_string(i->proplist)); + pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); } static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int is_last, void *userdata) { - char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + char t[32], s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; char *pl; if (is_last < 0) { @@ -447,29 +494,32 @@ static void get_sample_info_callback(pa_context *c, const pa_sample_info *i, int printf("\n"); nl = 1; - pa_bytes_snprint(t, sizeof(t), i->bytes); - printf(_("*** Sample #%u ***\n" - "Name: %s\n" - "Volume: %s\n" - "Sample Specification: %s\n" - "Channel Map: %s\n" - "Duration: %0.1fs\n" - "Size: %s\n" - "Lazy: %s\n" - "Filename: %s\n" - "Properties:\n%s"), + printf(_("Sample #%u\n" + "\tName: %s\n" + "\tSample Specification: %s\n" + "\tChannel Map: %s\n" + "\tVolume: %s\n" + "\t %s\n" + "\t balance %0.2f\n" + "\tDuration: %0.1fs\n" + "\tSize: %s\n" + "\tLazy: %s\n" + "\tFilename: %s\n" + "\tProperties:\n\t\t%s\n"), i->index, i->name, - pa_cvolume_snprint(cv, sizeof(cv), &i->volume), pa_sample_spec_valid(&i->sample_spec) ? pa_sample_spec_snprint(s, sizeof(s), &i->sample_spec) : _("n/a"), pa_sample_spec_valid(&i->sample_spec) ? pa_channel_map_snprint(cm, sizeof(cm), &i->channel_map) : _("n/a"), - (double) i->duration/1000000, + pa_cvolume_snprint(cv, sizeof(cv), &i->volume), + pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &i->volume), + pa_cvolume_get_balance(&i->channel_map, &i->volume), + (double) i->duration/1000000.0, t, pa_yes_no(i->lazy), i->filename ? i->filename : _("n/a"), - pl = pa_proplist_to_string(i->proplist)); + pl = pa_proplist_to_string_sep(i->proplist, "\n\t\t")); pa_xfree(pl); } @@ -579,7 +629,7 @@ static void context_state_callback(pa_context *c, void *userdata) { break; case LIST: - actions = 8; + actions = 7; pa_operation_unref(pa_context_get_module_info_list(c, get_module_info_callback, NULL)); pa_operation_unref(pa_context_get_sink_info_list(c, get_sink_info_callback, NULL)); pa_operation_unref(pa_context_get_source_info_list(c, get_source_info_callback, NULL)); |