summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am9
-rw-r--r--src/map-file3
-rw-r--r--src/modules/alsa/alsa-util.c297
-rw-r--r--src/modules/alsa/alsa-util.h19
-rw-r--r--src/modules/alsa/module-alsa-card.c196
-rw-r--r--src/modules/alsa/module-alsa-sink.c16
-rw-r--r--src/modules/alsa/module-alsa-source.c14
-rw-r--r--src/pulse/def.h4
-rw-r--r--src/pulse/introspect.c7
-rw-r--r--src/pulse/introspect.h5
-rw-r--r--src/pulse/proplist.c21
-rw-r--r--src/pulse/proplist.h17
-rw-r--r--src/pulse/volume.c91
-rw-r--r--src/pulse/volume.h22
-rw-r--r--src/pulsecore/card.c13
-rw-r--r--src/pulsecore/card.h12
-rw-r--r--src/pulsecore/cli-command.c1
-rw-r--r--src/pulsecore/cli-text.c67
-rw-r--r--src/pulsecore/module.c30
-rw-r--r--src/pulsecore/module.h7
-rw-r--r--src/pulsecore/protocol-native.c14
-rw-r--r--src/pulsecore/strbuf.c6
-rw-r--r--src/pulsecore/strbuf.h3
-rw-r--r--src/tests/voltest.c29
-rw-r--r--src/utils/pactl.c212
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));