summaryrefslogtreecommitdiffstats
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
l---------src/modules/alsa/Makefile1
-rw-r--r--src/modules/alsa/alsa-sink.c (renamed from src/modules/module-alsa-sink.c)250
-rw-r--r--src/modules/alsa/alsa-sink.h36
-rw-r--r--src/modules/alsa/alsa-source.c (renamed from src/modules/module-alsa-source.c)249
-rw-r--r--src/modules/alsa/alsa-source.h36
-rw-r--r--src/modules/alsa/alsa-util.c (renamed from src/modules/alsa-util.c)526
-rw-r--r--src/modules/alsa/alsa-util.h (renamed from src/modules/alsa-util.h)42
-rw-r--r--src/modules/alsa/module-alsa-card.c360
-rw-r--r--src/modules/alsa/module-alsa-sink.c125
-rw-r--r--src/modules/alsa/module-alsa-source.c149
-rw-r--r--src/modules/bluetooth/module-bluetooth-device.c233
-rw-r--r--src/modules/bluetooth/module-bluetooth-discover.c291
-rw-r--r--src/modules/bluetooth/module-bluetooth-proximity.c4
-rw-r--r--src/modules/dbus-util.h2
-rw-r--r--src/modules/hal-util.c127
-rw-r--r--src/modules/hal-util.h30
-rw-r--r--src/modules/module-always-sink.c10
-rw-r--r--src/modules/module-card-restore.c284
-rw-r--r--src/modules/module-combine.c5
-rw-r--r--src/modules/module-console-kit.c22
-rw-r--r--src/modules/module-default-device-restore.c22
-rw-r--r--src/modules/module-defs.h.m42
-rw-r--r--src/modules/module-detect.c2
-rw-r--r--src/modules/module-device-restore.c40
-rw-r--r--src/modules/module-esound-sink.c12
-rw-r--r--src/modules/module-flat-volume.c224
-rw-r--r--src/modules/module-hal-detect.c657
-rw-r--r--src/modules/module-jack-sink.c9
-rw-r--r--src/modules/module-jack-source.c9
-rw-r--r--src/modules/module-ladspa-sink.c15
-rw-r--r--src/modules/module-lirc.c6
-rw-r--r--src/modules/module-match.c66
-rw-r--r--src/modules/module-mmkbd-evdev.c6
-rw-r--r--src/modules/module-null-sink.c9
-rw-r--r--src/modules/module-pipe-sink.c9
-rw-r--r--src/modules/module-pipe-source.c9
-rw-r--r--src/modules/module-position-event-sounds.c42
-rw-r--r--src/modules/module-raop-sink.c44
-rw-r--r--src/modules/module-remap-sink.c15
-rw-r--r--src/modules/module-rescue-streams.c16
-rw-r--r--src/modules/module-sine-source.c22
-rw-r--r--src/modules/module-sine.c4
-rw-r--r--src/modules/module-stream-restore.c228
-rw-r--r--src/modules/module-suspend-on-idle.c68
-rw-r--r--src/modules/module-tunnel.c18
-rw-r--r--src/modules/module-volume-restore.c507
-rw-r--r--src/modules/module-x11-bell.c2
-rw-r--r--src/modules/module-x11-xsmp.c19
l---------src/modules/oss/Makefile1
-rw-r--r--src/modules/oss/module-oss.c (renamed from src/modules/module-oss.c)100
-rw-r--r--src/modules/oss/oss-util.c (renamed from src/modules/oss-util.c)17
-rw-r--r--src/modules/oss/oss-util.h (renamed from src/modules/oss-util.h)0
-rw-r--r--src/modules/raop/base64.c9
-rw-r--r--src/modules/rtp/module-rtp-recv.c4
-rw-r--r--src/modules/rtp/module-rtp-send.c2
-rw-r--r--src/modules/rtp/rtp.c4
-rw-r--r--src/modules/rtp/sap.c4
57 files changed, 3244 insertions, 1761 deletions
diff --git a/src/modules/alsa/Makefile b/src/modules/alsa/Makefile
new file mode 120000
index 00000000..efe5a336
--- /dev/null
+++ b/src/modules/alsa/Makefile
@@ -0,0 +1 @@
+../../pulse/Makefile \ No newline at end of file
diff --git a/src/modules/module-alsa-sink.c b/src/modules/alsa/alsa-sink.c
index f9fb9bd5..22460bb0 100644
--- a/src/modules/module-alsa-sink.c
+++ b/src/modules/alsa/alsa-sink.c
@@ -53,43 +53,7 @@
#include <pulsecore/time-smoother.h>
#include "alsa-util.h"
-#include "module-alsa-sink-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("ALSA Sink");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE(
- "sink_name=<name for the sink> "
- "device=<ALSA device> "
- "device_id=<ALSA card index> "
- "format=<sample format> "
- "rate=<sample rate> "
- "channels=<number of channels> "
- "channel_map=<channel map> "
- "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>");
-
-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
-};
+#include "alsa-sink.h"
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
@@ -138,6 +102,8 @@ struct userdata {
snd_pcm_sframes_t hwbuf_unused_frames;
};
+static void userdata_free(struct userdata *u);
+
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
size_t min_sleep, min_wakeup;
@@ -285,9 +251,10 @@ static int mmap_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
break;
}
@@ -407,9 +374,10 @@ static int unix_write(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polle
if (PA_UNLIKELY(n <= u->hwbuf_unused_frames)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to write new data to the device, but there was actually nothing to write! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLOUT set -- however a subsequent snd_pcm_avail_update() returned 0.");
break;
}
@@ -613,7 +581,7 @@ static int update_sw_params(struct userdata *u) {
pa_usec_t sleep_usec, process_usec;
hw_sleep_time(u, &sleep_usec, &process_usec);
- avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->sink->sample_spec) / u->frame_size;
}
pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
@@ -747,6 +715,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -788,7 +757,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
}
-static int sink_get_volume_cb(pa_sink *s) {
+static void sink_get_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -851,27 +820,24 @@ static int sink_get_volume_cb(pa_sink *s) {
if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
- u->hardware_volume = s->volume = r;
+ s->virtual_volume = u->hardware_volume = r;
if (u->hw_dB_supported) {
pa_cvolume reset;
/* Hmm, so the hardware volume changed, let's reset our software volume */
-
pa_cvolume_reset(&reset, s->sample_spec.channels);
pa_sink_set_soft_volume(s, &reset);
}
}
- return 0;
+ return;
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
- return -1;
}
-static int sink_set_volume_cb(pa_sink *s) {
+static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -888,7 +854,7 @@ static int sink_set_volume_cb(pa_sink *s) {
long alsa_vol;
pa_volume_t vol;
- vol = s->volume.values[i];
+ vol = s->virtual_volume.values[i];
if (u->hw_dB_supported) {
@@ -902,6 +868,10 @@ static int sink_set_volume_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
@@ -921,7 +891,7 @@ static int sink_set_volume_cb(pa_sink *s) {
pa_volume_t vol;
long alsa_vol;
- vol = pa_cvolume_max(&s->volume);
+ vol = pa_cvolume_max(&s->virtual_volume);
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
@@ -934,7 +904,11 @@ static int sink_set_volume_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
- pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
+ pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -955,30 +929,26 @@ static int sink_set_volume_cb(pa_sink *s) {
char t[PA_CVOLUME_SNPRINT_MAX];
/* Match exactly what the user requested by software */
+ pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
- pa_sw_cvolume_divide(&r, &s->volume, &r);
- pa_sink_set_soft_volume(s, &r);
-
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
- pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
+ pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
} else
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
- s->volume = r;
+ s->virtual_volume = r;
- return 0;
+ return;
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
- return -1;
}
-static int sink_get_mute_cb(pa_sink *s) {
+static void sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err, sw;
@@ -987,15 +957,13 @@ static int sink_get_mute_cb(pa_sink *s) {
if ((err = snd_mixer_selem_get_playback_switch(u->mixer_elem, 0, &sw)) < 0) {
pa_log_error("Unable to get switch: %s", snd_strerror(err));
- return -1;
+ return;
}
s->muted = !sw;
-
- return 0;
}
-static int sink_set_mute_cb(pa_sink *s) {
+static void sink_set_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int err;
@@ -1004,10 +972,8 @@ static int sink_set_mute_cb(pa_sink *s) {
if ((err = snd_mixer_selem_set_playback_switch_all(u->mixer_elem, !s->muted)) < 0) {
pa_log_error("Unable to set switch: %s", snd_strerror(err));
- return -1;
+ return;
}
-
- return 0;
}
static void sink_update_requested_latency_cb(pa_sink *s) {
@@ -1217,7 +1183,7 @@ static void thread_func(void *userdata) {
u->since_start = 0;
}
- if (revents && u->use_tsched)
+ if (revents && u->use_tsched && pa_log_ratelimit())
pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
} else
revents = 0;
@@ -1233,11 +1199,36 @@ finish:
pa_log_debug("Thread shutting down");
}
-int pa__init(pa_module*m) {
+static void set_sink_name(pa_sink_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
+ const char *n;
+ char *t;
+
+ pa_assert(data);
+ pa_assert(ma);
+ pa_assert(device_name);
+
+ if ((n = pa_modargs_get_value(ma, "sink_name", NULL))) {
+ pa_sink_new_data_set_name(data, n);
+ data->namereg_fail = TRUE;
+ return;
+ }
+
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ data->namereg_fail = TRUE;
+ else {
+ n = device_id ? device_id : device_name;
+ data->namereg_fail = FALSE;
+ }
+
+ t = pa_sprintf_malloc("alsa_output.%s", n);
+ pa_sink_new_data_set_name(data, t);
+ pa_xfree(t);
+}
+
+pa_sink *pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
- pa_modargs *ma = NULL;
struct userdata *u = NULL;
- const char *dev_id;
+ const char *dev_id = NULL;
pa_sample_spec ss;
pa_channel_map map;
uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
@@ -1245,23 +1236,14 @@ int pa__init(pa_module*m) {
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- const char *name;
- char *name_buf = NULL;
- pa_bool_t namereg_fail;
- pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_usec_t usec;
pa_sink_new_data data;
snd_pcm_info_alloca(&pcm_info);
pa_assert(m);
-
- pa_alsa_redirect_errors_inc();
-
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("Failed to parse module arguments");
- goto fail;
- }
+ pa_assert(ma);
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
@@ -1300,6 +1282,11 @@ int pa__init(pa_module*m) {
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+ pa_log("Failed to parse ignore_dB argument.");
+ goto fail;
+ }
+
if (use_tsched && !pa_rtclock_hrtimer()) {
pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
use_tsched = FALSE;
@@ -1308,7 +1295,6 @@ int pa__init(pa_module*m) {
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;
@@ -1323,20 +1309,35 @@ int pa__init(pa_module*m) {
pa_smoother_set_time_offset(u->smoother, usec);
pa_smoother_pause(u->smoother, usec);
- snd_config_update_free_global();
-
b = use_mmap;
d = use_tsched;
- if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ if (profile) {
+
+ if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ pa_log("device_id= not set");
+ goto fail;
+ }
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_PLAYBACK,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d, profile)))
- if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ goto fail;
+
+ } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
dev_id,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_PLAYBACK,
&nfrags, &period_frames, tsched_frames,
- &b, &d)))
+ &b, &d, &profile)))
goto fail;
@@ -1348,7 +1349,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;
}
@@ -1356,6 +1357,9 @@ int pa__init(pa_module*m) {
pa_assert(u->device_name);
pa_log_info("Successfully opened device %s.", u->device_name);
+ if (profile)
+ pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
+
if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
u->use_mmap = use_mmap = FALSE;
@@ -1394,11 +1398,11 @@ int pa__init(pa_module*m) {
if (snd_pcm_info(u->pcm_handle, info) >= 0) {
char *md;
- int card;
+ int card_idx;
- if ((card = snd_pcm_info_get_card(info)) >= 0) {
+ if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
- md = pa_sprintf_malloc("hw:%i", card);
+ md = pa_sprintf_malloc("hw:%i", card_idx);
if (strcmp(u->device_name, md))
if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
@@ -1418,30 +1422,27 @@ int pa__init(pa_module*m) {
}
}
- if ((name = pa_modargs_get_value(ma, "sink_name", NULL)))
- namereg_fail = TRUE;
- else {
- name = name_buf = pa_sprintf_malloc("alsa_output.%s", u->device_name);
- namereg_fail = FALSE;
- }
-
pa_sink_new_data_init(&data);
- data.driver = __FILE__;
+ data.driver = driver;
data.module = m;
- pa_sink_new_data_set_name(&data, name);
- data.namereg_fail = namereg_fail;
+ data.card = card;
+ set_sink_name(&data, ma, dev_id, u->device_name);
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(m->core, 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));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+ if (profile) {
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
+ }
+
u->sink = pa_sink_new(m->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY);
pa_sink_new_data_done(&data);
- pa_xfree(name_buf);
if (!u->sink) {
pa_log("Failed to create sink object");
@@ -1507,8 +1508,8 @@ int pa__init(pa_module*m) {
}
if (suitable) {
- if (snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
- pa_log_info("Mixer doesn't support dB information.");
+ if (ignore_dB || snd_mixer_selem_get_playback_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+ pa_log_info("Mixer doesn't support dB information or data is ignored.");
else {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
@@ -1545,6 +1546,8 @@ int pa__init(pa_module*m) {
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SINK_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+ if (!u->hw_dB_supported)
+ u->sink->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
} else
pa_log_info("Using software volume control.");
}
@@ -1594,29 +1597,17 @@ int pa__init(pa_module*m) {
pa_sink_put(u->sink);
- pa_modargs_free(ma);
-
- return 0;
+ return u->sink;
fail:
- if (ma)
- pa_modargs_free(ma);
-
- pa__done(m);
+ userdata_free(u);
- return -1;
+ return NULL;
}
-void pa__done(pa_module*m) {
- struct userdata *u;
-
- pa_assert(m);
-
- if (!(u = m->userdata)) {
- pa_alsa_redirect_errors_dec();
- return;
- }
+static void userdata_free(struct userdata *u) {
+ pa_assert(u);
if (u->sink)
pa_sink_unlink(u->sink);
@@ -1656,8 +1647,13 @@ void pa__done(pa_module*m) {
pa_xfree(u->device_name);
pa_xfree(u);
+}
- snd_config_update_free_global();
+void pa_alsa_sink_free(pa_sink *s) {
+ struct userdata *u;
+
+ pa_sink_assert_ref(s);
+ pa_assert_se(u = s->userdata);
- pa_alsa_redirect_errors_dec();
+ userdata_free(u);
}
diff --git a/src/modules/alsa/alsa-sink.h b/src/modules/alsa/alsa-sink.h
new file mode 100644
index 00000000..47ece9e0
--- /dev/null
+++ b/src/modules/alsa/alsa-sink.h
@@ -0,0 +1,36 @@
+#ifndef fooalsasinkhfoo
+#define fooalsasinkhfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/sink.h>
+
+#include "alsa-util.h"
+
+pa_sink* pa_alsa_sink_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile);
+
+void pa_alsa_sink_free(pa_sink *s);
+
+#endif
diff --git a/src/modules/module-alsa-source.c b/src/modules/alsa/alsa-source.c
index a6e4c907..0fd9838c 100644
--- a/src/modules/module-alsa-source.c
+++ b/src/modules/alsa/alsa-source.c
@@ -54,43 +54,7 @@
#include <pulsecore/rtclock.h>
#include "alsa-util.h"
-#include "module-alsa-source-symdef.h"
-
-PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("ALSA Source");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
-PA_MODULE_USAGE(
- "source_name=<name for the source> "
- "device=<ALSA device> "
- "device_id=<ALSA card index> "
- "format=<sample format> "
- "rate=<sample rate> "
- "channels=<number of channels> "
- "channel_map=<channel map> "
- "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=<upper fill watermark>");
-
-static const char* const valid_modargs[] = {
- "source_name",
- "device",
- "device_id",
- "format",
- "rate",
- "channels",
- "channel_map",
- "fragments",
- "fragment_size",
- "mmap",
- "tsched",
- "tsched_buffer_size",
- "tsched_buffer_watermark",
- NULL
-};
+#include "alsa-source.h"
#define DEFAULT_DEVICE "default"
#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */
@@ -136,6 +100,8 @@ struct userdata {
snd_pcm_sframes_t hwbuf_unused_frames;
};
+static void userdata_free(struct userdata *u);
+
static void fix_tsched_watermark(struct userdata *u) {
size_t max_use;
size_t min_sleep, min_wakeup;
@@ -272,9 +238,10 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
if (PA_UNLIKELY(n <= 0)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio device.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
break;
}
@@ -379,9 +346,10 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled
if (PA_UNLIKELY(n <= 0)) {
- if (polled)
+ if (polled && pa_log_ratelimit())
pa_log("ALSA woke us up to read new data from the device, but there was actually nothing to read! "
- "Most likely this is an ALSA driver bug. Please report this issue to the PulseAudio developers.");
+ "Most likely this is an ALSA driver bug. Please report this issue to the ALSA developers. "
+ "We were woken up with POLLIN set -- however a subsequent snd_pcm_avail_update() returned 0.");
return work_done;
}
@@ -558,7 +526,7 @@ static int update_sw_params(struct userdata *u) {
pa_usec_t sleep_usec, process_usec;
hw_sleep_time(u, &sleep_usec, &process_usec);
- avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec);
+ avail_min += pa_usec_to_bytes(sleep_usec, &u->source->sample_spec) / u->frame_size;
}
pa_log_debug("setting avail_min=%lu", (unsigned long) avail_min);
@@ -693,6 +661,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
case PA_SOURCE_UNLINKED:
case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
;
}
@@ -734,7 +703,7 @@ static long to_alsa_volume(struct userdata *u, pa_volume_t vol) {
return PA_CLAMP_UNLIKELY(alsa_vol, u->hw_volume_min, u->hw_volume_max);
}
-static int source_get_volume_cb(pa_source *s) {
+static void source_get_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -797,27 +766,24 @@ static int source_get_volume_cb(pa_source *s) {
if (!pa_cvolume_equal(&u->hardware_volume, &r)) {
- u->hardware_volume = s->volume = r;
+ s->virtual_volume = u->hardware_volume = r;
if (u->hw_dB_supported) {
pa_cvolume reset;
/* Hmm, so the hardware volume changed, let's reset our software volume */
-
pa_cvolume_reset(&reset, s->sample_spec.channels);
pa_source_set_soft_volume(s, &reset);
}
}
- return 0;
+ return;
fail:
pa_log_error("Unable to read volume: %s", snd_strerror(err));
-
- return -1;
}
-static int source_set_volume_cb(pa_source *s) {
+static void source_set_volume_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
unsigned i;
@@ -834,7 +800,7 @@ static int source_set_volume_cb(pa_source *s) {
long alsa_vol;
pa_volume_t vol;
- vol = s->volume.values[i];
+ vol = s->virtual_volume.values[i];
if (u->hw_dB_supported) {
@@ -848,6 +814,10 @@ static int source_set_volume_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, u->mixer_map[i], &alsa_vol)) < 0)
goto fail;
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
r.values[i] = pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0);
} else {
@@ -867,7 +837,7 @@ static int source_set_volume_cb(pa_source *s) {
pa_volume_t vol;
long alsa_vol;
- vol = pa_cvolume_max(&s->volume);
+ vol = pa_cvolume_max(&s->virtual_volume);
if (u->hw_dB_supported) {
alsa_vol = (long) (pa_sw_volume_to_dB(vol) * 100);
@@ -880,7 +850,11 @@ static int source_set_volume_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_dB(u->mixer_elem, SND_MIXER_SCHN_MONO, &alsa_vol)) < 0)
goto fail;
- pa_cvolume_set(&r, s->volume.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+ VALGRIND_MAKE_MEM_DEFINED(&alsa_vol, sizeof(alsa_vol));
+#endif
+
+ pa_cvolume_set(&r, s->sample_spec.channels, pa_sw_volume_from_dB((double) (alsa_vol - u->hw_dB_max) / 100.0));
} else {
alsa_vol = to_alsa_volume(u, vol);
@@ -902,29 +876,26 @@ static int source_set_volume_cb(pa_source *s) {
/* Match exactly what the user requested by software */
- pa_sw_cvolume_divide(&r, &s->volume, &r);
- pa_source_set_soft_volume(s, &r);
+ pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume);
- pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume));
+ pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume));
pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume));
- pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &r));
+ pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume));
} else
/* We can't match exactly what the user requested, hence let's
* at least tell the user about it */
- s->volume = r;
+ s->virtual_volume = r;
- return 0;
+ return;
fail:
pa_log_error("Unable to set volume: %s", snd_strerror(err));
-
- return -1;
}
-static int source_get_mute_cb(pa_source *s) {
+static void source_get_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err, sw;
@@ -933,15 +904,13 @@ static int source_get_mute_cb(pa_source *s) {
if ((err = snd_mixer_selem_get_capture_switch(u->mixer_elem, 0, &sw)) < 0) {
pa_log_error("Unable to get switch: %s", snd_strerror(err));
- return -1;
+ return;
}
s->muted = !sw;
-
- return 0;
}
-static int source_set_mute_cb(pa_source *s) {
+static void source_set_mute_cb(pa_source *s) {
struct userdata *u = s->userdata;
int err;
@@ -950,10 +919,8 @@ static int source_set_mute_cb(pa_source *s) {
if ((err = snd_mixer_selem_set_capture_switch_all(u->mixer_elem, !s->muted)) < 0) {
pa_log_error("Unable to set switch: %s", snd_strerror(err));
- return -1;
+ return;
}
-
- return 0;
}
static void source_update_requested_latency_cb(pa_source *s) {
@@ -1052,7 +1019,7 @@ static void thread_func(void *userdata) {
snd_pcm_start(u->pcm_handle);
}
- if (revents && u->use_tsched)
+ if (revents && u->use_tsched && pa_log_ratelimit())
pa_log_debug("Wakeup from ALSA!%s%s", (revents & POLLIN) ? " INPUT" : "", (revents & POLLOUT) ? " OUTPUT" : "");
} else
revents = 0;
@@ -1068,11 +1035,36 @@ finish:
pa_log_debug("Thread shutting down");
}
-int pa__init(pa_module*m) {
+static void set_source_name(pa_source_new_data *data, pa_modargs *ma, const char *device_id, const char *device_name) {
+ const char *n;
+ char *t;
+
+ pa_assert(data);
+ pa_assert(ma);
+ pa_assert(device_name);
+
+ if ((n = pa_modargs_get_value(ma, "source_name", NULL))) {
+ pa_source_new_data_set_name(data, n);
+ data->namereg_fail = TRUE;
+ return;
+ }
+
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ data->namereg_fail = TRUE;
+ else {
+ n = device_id ? device_id : device_name;
+ data->namereg_fail = FALSE;
+ }
+
+ t = pa_sprintf_malloc("alsa_input.%s", n);
+ pa_source_new_data_set_name(data, t);
+ pa_xfree(t);
+}
+
+pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile) {
- pa_modargs *ma = NULL;
struct userdata *u = NULL;
- const char *dev_id;
+ const char *dev_id = NULL;
pa_sample_spec ss;
pa_channel_map map;
uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark;
@@ -1080,23 +1072,13 @@ int pa__init(pa_module*m) {
size_t frame_size;
snd_pcm_info_t *pcm_info = NULL;
int err;
- const char *name;
- char *name_buf = NULL;
- pa_bool_t namereg_fail;
- pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d;
+ pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE;
pa_source_new_data data;
snd_pcm_info_alloca(&pcm_info);
pa_assert(m);
- pa_alsa_redirect_errors_inc();
-
- if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
- pa_log("Failed to parse module arguments");
- goto fail;
- }
-
ss = m->core->default_sample_spec;
if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_ALSA) < 0) {
pa_log("Failed to parse sample specification");
@@ -1134,6 +1116,11 @@ int pa__init(pa_module*m) {
goto fail;
}
+ if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) {
+ pa_log("Failed to parse ignore_dB argument.");
+ goto fail;
+ }
+
if (use_tsched && !pa_rtclock_hrtimer()) {
pa_log_notice("Disabling timer-based scheduling because high-resolution timers are not available from the kernel.");
use_tsched = FALSE;
@@ -1142,7 +1129,6 @@ int pa__init(pa_module*m) {
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();
@@ -1152,20 +1138,34 @@ int pa__init(pa_module*m) {
u->smoother = pa_smoother_new(DEFAULT_TSCHED_WATERMARK_USEC, DEFAULT_TSCHED_WATERMARK_USEC, TRUE, 5);
pa_smoother_set_time_offset(u->smoother, pa_rtclock_usec());
- snd_config_update_free_global();
-
b = use_mmap;
d = use_tsched;
- if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ if (profile) {
+
+ if (!(dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
+ pa_log("device_id= not set");
+ goto fail;
+ }
+
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_profile(
+ dev_id,
+ &u->device_name,
+ &ss, &map,
+ SND_PCM_STREAM_CAPTURE,
+ &nfrags, &period_frames, tsched_frames,
+ &b, &d, profile)))
+ goto fail;
+
+ } else if ((dev_id = pa_modargs_get_value(ma, "device_id", NULL))) {
- if (!(u->pcm_handle = pa_alsa_open_by_device_id(
+ if (!(u->pcm_handle = pa_alsa_open_by_device_id_auto(
dev_id,
&u->device_name,
&ss, &map,
SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames,
- &b, &d)))
+ &b, &d, &profile)))
goto fail;
} else {
@@ -1176,13 +1176,16 @@ int pa__init(pa_module*m) {
&ss, &map,
SND_PCM_STREAM_CAPTURE,
&nfrags, &period_frames, tsched_frames,
- &b, &d)))
+ &b, &d, FALSE)))
goto fail;
}
pa_assert(u->device_name);
pa_log_info("Successfully opened device %s.", u->device_name);
+ if (profile)
+ pa_log_info("Selected configuration '%s' (%s).", profile->description, profile->name);
+
if (use_mmap && !b) {
pa_log_info("Device doesn't support mmap(), falling back to UNIX read/write mode.");
u->use_mmap = use_mmap = FALSE;
@@ -1221,11 +1224,11 @@ int pa__init(pa_module*m) {
if (snd_pcm_info(u->pcm_handle, info) >= 0) {
char *md;
- int card;
+ int card_idx;
- if ((card = snd_pcm_info_get_card(info)) >= 0) {
+ if ((card_idx = snd_pcm_info_get_card(info)) >= 0) {
- md = pa_sprintf_malloc("hw:%i", card);
+ md = pa_sprintf_malloc("hw:%i", card_idx);
if (strcmp(u->device_name, md))
if (pa_alsa_prepare_mixer(u->mixer_handle, md) >= 0)
@@ -1245,30 +1248,27 @@ int pa__init(pa_module*m) {
}
}
- if ((name = pa_modargs_get_value(ma, "source_name", NULL)))
- namereg_fail = TRUE;
- else {
- name = name_buf = pa_sprintf_malloc("alsa_input.%s", u->device_name);
- namereg_fail = FALSE;
- }
-
pa_source_new_data_init(&data);
- data.driver = __FILE__;
+ data.driver = driver;
data.module = m;
- pa_source_new_data_set_name(&data, name);
- data.namereg_fail = namereg_fail;
+ data.card = card;
+ set_source_name(&data, ma, dev_id, u->device_name);
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(m->core, 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));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial"));
+ if (profile) {
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_NAME, profile->name);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PROFILE_DESCRIPTION, profile->description);
+ }
+
u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY);
pa_source_new_data_done(&data);
- pa_xfree(name_buf);
if (!u->source) {
pa_log("Failed to create source object");
@@ -1329,8 +1329,8 @@ int pa__init(pa_module*m) {
}
if (suitable) {
- if (snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
- pa_log_info("Mixer doesn't support dB information.");
+ if (ignore_dB || snd_mixer_selem_get_capture_dB_range(u->mixer_elem, &u->hw_dB_min, &u->hw_dB_max) < 0)
+ pa_log_info("Mixer doesn't support dB information or data is ignored.");
else {
#ifdef HAVE_VALGRIND_MEMCHECK_H
VALGRIND_MAKE_MEM_DEFINED(&u->hw_dB_min, sizeof(u->hw_dB_min));
@@ -1367,6 +1367,9 @@ int pa__init(pa_module*m) {
u->source->set_volume = source_set_volume_cb;
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL | (u->hw_dB_supported ? PA_SOURCE_DECIBEL_VOLUME : 0);
pa_log_info("Using hardware volume control. Hardware dB scale %s.", u->hw_dB_supported ? "supported" : "not supported");
+
+ if (!u->hw_dB_supported)
+ u->source->n_volume_steps = u->hw_volume_max - u->hw_volume_min + 1;
} else
pa_log_info("Using software volume control.");
}
@@ -1415,29 +1418,17 @@ int pa__init(pa_module*m) {
pa_source_put(u->source);
- pa_modargs_free(ma);
-
- return 0;
+ return u->source;
fail:
- if (ma)
- pa_modargs_free(ma);
+ userdata_free(u);
- pa__done(m);
-
- return -1;
+ return NULL;
}
-void pa__done(pa_module*m) {
- struct userdata *u;
-
- pa_assert(m);
-
- if (!(u = m->userdata)) {
- pa_alsa_redirect_errors_dec();
- return;
- }
+static void userdata_free(struct userdata *u) {
+ pa_assert(u);
if (u->source)
pa_source_unlink(u->source);
@@ -1474,7 +1465,13 @@ void pa__done(pa_module*m) {
pa_xfree(u->device_name);
pa_xfree(u);
+}
- snd_config_update_free_global();
- pa_alsa_redirect_errors_dec();
+void pa_alsa_source_free(pa_source *s) {
+ struct userdata *u;
+
+ pa_source_assert_ref(s);
+ pa_assert_se(u = s->userdata);
+
+ userdata_free(u);
}
diff --git a/src/modules/alsa/alsa-source.h b/src/modules/alsa/alsa-source.h
new file mode 100644
index 00000000..5fed6cc8
--- /dev/null
+++ b/src/modules/alsa/alsa-source.h
@@ -0,0 +1,36 @@
+#ifndef fooalsasourcehfoo
+#define fooalsasourcehfoo
+
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2006 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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.
+***/
+
+#include <pulsecore/module.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/source.h>
+
+#include "alsa-util.h"
+
+pa_source* pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, pa_card *card, const pa_alsa_profile_info *profile);
+
+void pa_alsa_source_free(pa_source *s);
+
+#endif
diff --git a/src/modules/alsa-util.c b/src/modules/alsa/alsa-util.c
index ff3af19d..7e5a3503 100644
--- a/src/modules/alsa-util.c
+++ b/src/modules/alsa/alsa-util.c
@@ -39,6 +39,10 @@
#include "alsa-util.h"
+#ifdef HAVE_HAL
+#include "hal-util.h"
+#endif
+
struct pa_alsa_fdlist {
unsigned num_fds;
struct pollfd *fds;
@@ -232,6 +236,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
[PA_SAMPLE_FLOAT32BE] = SND_PCM_FORMAT_FLOAT_BE,
[PA_SAMPLE_S32LE] = SND_PCM_FORMAT_S32_LE,
[PA_SAMPLE_S32BE] = SND_PCM_FORMAT_S32_BE,
+ [PA_SAMPLE_S24LE] = SND_PCM_FORMAT_S24_3LE,
+ [PA_SAMPLE_S24BE] = SND_PCM_FORMAT_S24_3BE,
+ [PA_SAMPLE_S24_32LE] = SND_PCM_FORMAT_S24_LE,
+ [PA_SAMPLE_S24_32BE] = SND_PCM_FORMAT_S24_BE,
};
static const pa_sample_format_t try_order[] = {
@@ -239,6 +247,10 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
PA_SAMPLE_FLOAT32RE,
PA_SAMPLE_S32NE,
PA_SAMPLE_S32RE,
+ PA_SAMPLE_S24_32NE,
+ PA_SAMPLE_S24_32RE,
+ PA_SAMPLE_S24NE,
+ PA_SAMPLE_S24RE,
PA_SAMPLE_S16NE,
PA_SAMPLE_S16RE,
PA_SAMPLE_ALAW,
@@ -259,6 +271,14 @@ static int set_format(snd_pcm_t *pcm_handle, snd_pcm_hw_params_t *hwparams, pa_s
*f = PA_SAMPLE_FLOAT32LE;
else if (*f == PA_SAMPLE_FLOAT32LE)
*f = PA_SAMPLE_FLOAT32BE;
+ else if (*f == PA_SAMPLE_S24BE)
+ *f = PA_SAMPLE_S24LE;
+ else if (*f == PA_SAMPLE_S24LE)
+ *f = PA_SAMPLE_S24BE;
+ else if (*f == PA_SAMPLE_S24_32BE)
+ *f = PA_SAMPLE_S24_32LE;
+ else if (*f == PA_SAMPLE_S24_32LE)
+ *f = PA_SAMPLE_S24_32BE;
else if (*f == PA_SAMPLE_S16BE)
*f = PA_SAMPLE_S16LE;
else if (*f == PA_SAMPLE_S16LE)
@@ -298,8 +318,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;
@@ -311,8 +331,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);
@@ -345,10 +363,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;
@@ -357,50 +371,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;
@@ -418,8 +438,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;
@@ -472,57 +495,90 @@ 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 *name;
-};
+static const struct pa_alsa_profile_info device_table[] = {
+ {{ 1, { PA_CHANNEL_POSITION_MONO }},
+ "hw",
+ "Analog Mono",
+ "analog-mono",
+ 1 },
+
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+ "front",
+ "Analog Stereo",
+ "analog-stereo",
+ 10 },
+
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+ "iec958",
+ "IEC958 Digital Stereo",
+ "iec958-stereo",
+ 5 },
+
+ {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT }},
+ "hdmi",
+ "HDMI Digital Stereo",
+ "hdmi-stereo",
+ 4 },
-static const struct device_info device_table[] = {
- {{ 2, { PA_CHANNEL_POSITION_LEFT, PA_CHANNEL_POSITION_RIGHT } }, "front" },
+ {{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
+ "surround40",
+ "Analog Surround 4.0",
+ "analog-surround-40",
+ 7 },
{{ 4, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
- PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }}, "surround40" },
+ PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT }},
+ "a52",
+ "IEC958/AC3 Digital Surround 4.0",
+ "iec958-ac3-surround-40",
+ 2 },
{{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_LFE }}, "surround41" },
+ PA_CHANNEL_POSITION_LFE }},
+ "surround41",
+ "Analog Surround 4.1",
+ "analog-surround-41",
+ 7 },
{{ 5, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_CENTER }}, "surround50" },
+ PA_CHANNEL_POSITION_CENTER }},
+ "surround50",
+ "Analog Surround 5.0",
+ "analog-surround-50",
+ 7 },
{{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
- PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }}, "surround51" },
+ PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE }},
+ "surround51",
+ "Analog Surround 5.1",
+ "analog-surround-51",
+ 8 },
+
+ {{ 6, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_CENTER,
+ PA_CHANNEL_POSITION_FRONT_RIGHT, PA_CHANNEL_POSITION_REAR_LEFT,
+ PA_CHANNEL_POSITION_REAR_RIGHT, PA_CHANNEL_POSITION_LFE}},
+ "a52",
+ "IEC958/AC3 Digital Surround 5.1",
+ "iec958-ac3-surround-51",
+ 3 },
{{ 8, { PA_CHANNEL_POSITION_FRONT_LEFT, PA_CHANNEL_POSITION_FRONT_RIGHT,
PA_CHANNEL_POSITION_REAR_LEFT, PA_CHANNEL_POSITION_REAR_RIGHT,
PA_CHANNEL_POSITION_CENTER, PA_CHANNEL_POSITION_LFE,
- PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }} , "surround71" },
+ PA_CHANNEL_POSITION_SIDE_LEFT, PA_CHANNEL_POSITION_SIDE_RIGHT }},
+ "surround71",
+ "Analog Surround 7.1",
+ "analog-surround-71",
+ 7 },
- {{ 0, { 0 }}, NULL }
+ {{ 0, { 0 }}, NULL, NULL, NULL, 0 }
};
-static pa_bool_t channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) {
- pa_bool_t in_a[PA_CHANNEL_POSITION_MAX];
- unsigned i;
-
- pa_assert(a);
- pa_assert(b);
-
- memset(in_a, 0, sizeof(in_a));
-
- for (i = 0; i < a->channels; i++)
- in_a[a->map[i]] = TRUE;
-
- for (i = 0; i < b->channels; i++)
- if (!in_a[b->map[i]])
- return FALSE;
-
- return TRUE;
-}
-
-snd_pcm_t *pa_alsa_open_by_device_id(
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id,
char **dev,
pa_sample_spec *ss,
@@ -532,11 +588,11 @@ snd_pcm_t *pa_alsa_open_by_device_id(
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,
+ const pa_alsa_profile_info **profile) {
int i;
int direction = 1;
- int err;
char *d;
snd_pcm_t *pcm_handle;
@@ -554,99 +610,158 @@ snd_pcm_t *pa_alsa_open_by_device_id(
* way, we iterate backwards, and check all devices that do not
* provide a superset of the requested channel map.*/
- for (i = 0;; i += direction) {
- pa_sample_spec try_ss;
- pa_bool_t reformat;
-
- if (i < 0) {
- pa_assert(direction == -1);
+ i = 0;
+ for (;;) {
- /* OK, so we iterated backwards, and now are at the
- * beginning of our list. */
+ if ((direction > 0) == pa_channel_map_superset(&device_table[i].map, map)) {
+ pa_sample_spec try_ss;
- break;
+ pa_log_debug("Checking for %s (%s)", device_table[i].name, device_table[i].alsa_name);
- } else if (!device_table[i].name) {
- pa_assert(direction == 1);
+ d = pa_sprintf_malloc("%s:%s", device_table[i].alsa_name, dev_id);
- /* OK, so we are at the end of our list. at iterated
- * forwards. */
+ try_ss.channels = device_table[i].map.channels;
+ try_ss.rate = ss->rate;
+ try_ss.format = ss->format;
- i--;
- direction = -1;
- }
+ pcm_handle = pa_alsa_open_by_device_string(
+ d,
+ dev,
+ &try_ss,
+ map,
+ mode,
+ nfrags,
+ period_size,
+ tsched_size,
+ use_mmap,
+ use_tsched,
+ TRUE);
- if ((direction > 0) == !channel_map_superset(&device_table[i].map, map))
- continue;
+ pa_xfree(d);
- d = pa_sprintf_malloc("%s:%s", device_table[i].name, dev_id);
+ if (pcm_handle) {
- reformat = FALSE;
- for (;;) {
- pa_log_debug("Trying %s %s SND_PCM_NO_AUTO_FORMAT ...", d, reformat ? "without" : "with");
+ *ss = try_ss;
+ *map = device_table[i].map;
+ pa_assert(map->channels == ss->channels);
- /* 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 (profile)
+ *profile = &device_table[i];
- 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;
+ return pcm_handle;
}
+ }
- try_ss.channels = device_table[i].map.channels;
- try_ss.rate = ss->rate;
- try_ss.format = ss->format;
+ if (direction > 0) {
+ if (!device_table[i+1].alsa_name) {
+ /* OK, so we are at the end of our list. Let's turn
+ * back. */
+ direction = -1;
+ } else {
+ /* We are not at the end of the list, so let's simply
+ * try the next entry */
+ i++;
+ }
+ }
- if ((err = pa_alsa_set_hw_params(pcm_handle, &try_ss, nfrags, period_size, tsched_size, use_mmap, use_tsched, TRUE)) < 0) {
+ if (direction < 0) {
- if (!reformat) {
- reformat = TRUE;
- snd_pcm_close(pcm_handle);
- continue;
- }
+ if (device_table[i+1].alsa_name &&
+ device_table[i].map.channels == device_table[i+1].map.channels) {
- if (!pa_startswith(d, "plug:") && !pa_startswith(d, "plughw:")) {
- char *t;
+ /* OK, the next entry has the same number of channels,
+ * let's try it */
+ i++;
- t = pa_sprintf_malloc("plug:%s", d);
- pa_xfree(d);
- d = t;
+ } else {
+ /* Hmm, so the next entry does not have the same
+ * number of channels, so let's go backwards until we
+ * find the next entry with a differnt number of
+ * channels */
- reformat = FALSE;
+ for (i--; i >= 0; i--)
+ if (device_table[i].map.channels != device_table[i+1].map.channels)
+ break;
- snd_pcm_close(pcm_handle);
- continue;
- }
+ /* Hmm, there is no entry with a different number of
+ * entries, then we're done */
+ if (i < 0)
+ break;
- pa_log_info("PCM device %s refused our hw parameters: %s", d, snd_strerror(err));
- snd_pcm_close(pcm_handle);
- break;
+ /* OK, now lets find go back as long as we have the same number of channels */
+ for (; i > 0; i--)
+ if (device_table[i].map.channels != device_table[i-1].map.channels)
+ break;
}
-
- *ss = try_ss;
- *map = device_table[i].map;
- pa_assert(map->channels == ss->channels);
- *dev = d;
- return pcm_handle;
}
-
- pa_xfree(d);
}
/* OK, we didn't find any good device, so let's try the raw plughw: stuff */
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 && profile)
+ *profile = NULL;
+
+ return pcm_handle;
+}
+
+snd_pcm_t *pa_alsa_open_by_device_id_profile(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ const pa_alsa_profile_info *profile) {
+
+ char *d;
+ snd_pcm_t *pcm_handle;
+ pa_sample_spec try_ss;
+
+ pa_assert(dev_id);
+ pa_assert(dev);
+ pa_assert(ss);
+ pa_assert(map);
+ pa_assert(nfrags);
+ pa_assert(period_size);
+ pa_assert(profile);
+
+ d = pa_sprintf_malloc("%s:%s", profile->alsa_name, dev_id);
+
+ try_ss.channels = profile->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);
+
+ pa_xfree(d);
+
+ if (!pcm_handle)
+ return NULL;
+
+ *ss = try_ss;
+ *map = profile->map;
+ pa_assert(map->channels == ss->channels);
+
return pcm_handle;
}
@@ -660,7 +775,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;
@@ -668,11 +784,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);
@@ -690,12 +803,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;
@@ -719,13 +832,16 @@ 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;
+ else
+ pa_xfree(d);
if (ss->channels != map->channels)
pa_channel_map_init_extend(map, ss->channels, PA_CHANNEL_MAP_ALSA);
@@ -734,6 +850,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;
@@ -1048,7 +1252,30 @@ 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_core *c, 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);
+ }
+
+#ifdef HAVE_HAL
+ pa_hal_get_info(c, p, card);
+#endif
+}
+
+void pa_alsa_init_proplist_pcm(pa_core *c, 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",
@@ -1069,8 +1296,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);
@@ -1103,13 +1329,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(c, p, card);
+ cn = pa_proplist_gets(p, "alsa.card_name");
}
if (cn && n)
@@ -1118,9 +1339,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-util.h b/src/modules/alsa/alsa-util.h
index 95bb983a..f2d3278b 100644
--- a/src/modules/alsa-util.h
+++ b/src/modules/alsa/alsa-util.h
@@ -32,6 +32,7 @@
#include <pulse/proplist.h>
#include <pulsecore/rtpoll.h>
+#include <pulsecore/core.h>
typedef struct pa_alsa_fdlist pa_alsa_fdlist;
@@ -54,7 +55,16 @@ int pa_alsa_set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t avail_min);
int pa_alsa_prepare_mixer(snd_mixer_t *mixer, const char *dev);
snd_mixer_elem_t *pa_alsa_find_elem(snd_mixer_t *mixer, const char *name, const char *fallback, pa_bool_t playback);
-snd_pcm_t *pa_alsa_open_by_device_id(
+typedef struct pa_alsa_profile_info {
+ pa_channel_map map;
+ const char *alsa_name;
+ const char *description;
+ const char *name;
+ unsigned priority;
+} pa_alsa_profile_info;
+
+/* Picks a working profile based on the specified ss/map */
+snd_pcm_t *pa_alsa_open_by_device_id_auto(
const char *dev_id,
char **dev,
pa_sample_spec *ss,
@@ -64,8 +74,24 @@ snd_pcm_t *pa_alsa_open_by_device_id(
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,
+ const pa_alsa_profile_info **profile);
+/* Uses the specified profile */
+snd_pcm_t *pa_alsa_open_by_device_id_profile(
+ const char *dev_id,
+ char **dev,
+ pa_sample_spec *ss,
+ pa_channel_map* map,
+ int mode,
+ uint32_t *nfrags,
+ snd_pcm_uframes_t *period_size,
+ snd_pcm_uframes_t tsched_size,
+ pa_bool_t *use_mmap,
+ pa_bool_t *use_tsched,
+ const pa_alsa_profile_info *profile);
+
+/* Opens the explicit ALSA device */
snd_pcm_t *pa_alsa_open_by_device_string(
const char *device,
char **dev,
@@ -76,7 +102,14 @@ 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 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);
@@ -86,7 +119,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_core *c, pa_proplist *p, snd_pcm_info_t *pcm_info);
+void pa_alsa_init_proplist_card(pa_core *c, 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..e63414ec
--- /dev/null
+++ b/src/modules/alsa/module-alsa-card.c
@@ -0,0 +1,360 @@
+/***
+ 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 <pulsecore/queue.h>
+
+#include "alsa-util.h"
+#include "alsa-sink.h"
+#include "alsa-source.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 card/sink/source, to be prefixed> "
+ "card_name=<name for card> "
+ "sink_name=<name for sink> "
+ "source_name=<name for 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> "
+ "ignore_dB=<ignore dB information from the device?>");
+
+static const char* const valid_modargs[] = {
+ "name",
+ "card_name",
+ "sink_name",
+ "source_name",
+ "device_id",
+ "format",
+ "rate",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "profile",
+ "ignore_dB",
+ NULL
+};
+
+#define DEFAULT_DEVICE_ID "0"
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+
+ char *device_id;
+
+ pa_card *card;
+ pa_sink *sink;
+ pa_source *source;
+
+ pa_modargs *modargs;
+};
+
+struct profile_data {
+ const pa_alsa_profile_info *sink_profile, *source_profile;
+};
+
+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("output-%s+input-%s", sink->name, source->name);
+ t = pa_sprintf_malloc("Output %s + Input %s", sink->description, source->description);
+ } else if (sink) {
+ n = pa_sprintf_malloc("output-%s", sink->name);
+ t = pa_sprintf_malloc("Output %s", sink->description);
+ } else {
+ pa_assert(source);
+ n = pa_sprintf_malloc("input-%s", 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->priority = (sink ? sink->priority : 0)*100 + (source ? source->priority : 0);
+ 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_profile = sink;
+ d->source_profile = source;
+
+ pa_hashmap_put(profiles, p->name, p);
+}
+
+static void add_disabled_profile(pa_hashmap *profiles) {
+ pa_card_profile *p;
+ struct profile_data *d;
+
+ p = pa_card_profile_new("off", "Off", sizeof(struct profile_data));
+
+ d = PA_CARD_PROFILE_DATA(p);
+ d->sink_profile = d->source_profile = NULL;
+
+ pa_hashmap_put(profiles, p->name, p);
+}
+
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+ struct userdata *u;
+ struct profile_data *nd, *od;
+
+ pa_assert(c);
+ pa_assert(new_profile);
+ pa_assert_se(u = c->userdata);
+
+ nd = PA_CARD_PROFILE_DATA(new_profile);
+ od = PA_CARD_PROFILE_DATA(c->active_profile);
+
+ if (od->sink_profile != nd->sink_profile) {
+ pa_queue *inputs = NULL;
+
+ if (u->sink) {
+ if (nd->sink_profile)
+ inputs = pa_sink_move_all_start(u->sink);
+
+ pa_alsa_sink_free(u->sink);
+ u->sink = NULL;
+ }
+
+ if (nd->sink_profile) {
+ u->sink = pa_alsa_sink_new(c->module, u->modargs, __FILE__, c, nd->sink_profile);
+
+ if (inputs) {
+ if (u->sink)
+ pa_sink_move_all_finish(u->sink, inputs, FALSE);
+ else
+ pa_sink_move_all_fail(inputs);
+ }
+ }
+ }
+
+ if (od->source_profile != nd->source_profile) {
+ pa_queue *outputs = NULL;
+
+ if (u->source) {
+ if (nd->source_profile)
+ outputs = pa_source_move_all_start(u->source);
+
+ pa_alsa_source_free(u->source);
+ u->source = NULL;
+ }
+
+ if (nd->source_profile) {
+ u->source = pa_alsa_source_new(c->module, u->modargs, __FILE__, c, nd->source_profile);
+
+ if (outputs) {
+ if (u->source)
+ pa_source_move_all_finish(u->source, outputs, FALSE);
+ else
+ pa_source_move_all_fail(outputs);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void init_profile(struct userdata *u) {
+ struct profile_data *d;
+
+ pa_assert(u);
+
+ d = PA_CARD_PROFILE_DATA(u->card->active_profile);
+
+ if (d->sink_profile)
+ u->sink = pa_alsa_sink_new(u->module, u->modargs, __FILE__, u->card, d->sink_profile);
+
+ if (d->source_profile)
+ u->source = pa_alsa_source_new(u->module, u->modargs, __FILE__, u->card, d->source_profile);
+}
+
+static void set_card_name(pa_card_new_data *data, pa_modargs *ma, const char *device_id) {
+ char *t;
+ const char *n;
+
+ pa_assert(data);
+ pa_assert(ma);
+ pa_assert(device_id);
+
+ if ((n = pa_modargs_get_value(ma, "card_name", NULL))) {
+ pa_card_new_data_set_name(data, n);
+ data->namereg_fail = TRUE;
+ return;
+ }
+
+ if ((n = pa_modargs_get_value(ma, "name", NULL)))
+ data->namereg_fail = TRUE;
+ else {
+ n = device_id;
+ data->namereg_fail = FALSE;
+ }
+
+ t = pa_sprintf_malloc("alsa_card.%s", n);
+ pa_card_new_data_set_name(data, t);
+ pa_xfree(t);
+}
+
+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();
+ snd_config_update_free_global();
+
+ 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_xnew(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));
+ u->card = NULL;
+ u->sink = NULL;
+ u->source = NULL;
+ u->modargs = ma;
+
+ 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(m->core, data.proplist, alsa_card_index);
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_id);
+ set_card_name(&data, ma, 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;
+ }
+
+ if (pa_hashmap_isempty(data.profiles)) {
+ pa_log("Failed to find a working profile.");
+ pa_card_new_data_done(&data);
+ goto fail;
+ }
+
+ add_disabled_profile(data.profiles);
+
+ u->card = pa_card_new(m->core, &data);
+ pa_card_new_data_done(&data);
+
+ if (!u->card)
+ goto fail;
+
+ u->card->userdata = u;
+ u->card->set_profile = card_set_profile;
+
+ init_profile(u);
+
+ return 0;
+
+fail:
+
+ pa__done(m);
+ return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return
+ (u->sink ? pa_sink_linked_by(u->sink) : 0) +
+ (u->source ? pa_source_linked_by(u->source) : 0);
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ goto finish;
+
+ if (u->sink)
+ pa_alsa_sink_free(u->sink);
+
+ if (u->source)
+ pa_alsa_source_free(u->source);
+
+ if (u->card)
+ pa_card_free(u->card);
+
+ if (u->modargs)
+ pa_modargs_free(u->modargs);
+
+ 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
new file mode 100644
index 00000000..4f844e08
--- /dev/null
+++ b/src/modules/alsa/module-alsa-sink.c
@@ -0,0 +1,125 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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 <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+
+#include "alsa-util.h"
+#include "alsa-sink.h"
+#include "module-alsa-sink-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Sink");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "name=<name of the sink, to be prefixed> "
+ "sink_name=<name for the sink> "
+ "device=<ALSA device> "
+ "device_id=<ALSA card index> "
+ "format=<sample format> "
+ "rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
+ "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> "
+ "ignore_dB=<ignore dB information from the device?>");
+
+static const char* const valid_modargs[] = {
+ "name",
+ "sink_name",
+ "device",
+ "device_id",
+ "format",
+ "rate",
+ "channels",
+ "channel_map",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "ignore_dB",
+ NULL
+};
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+
+ pa_assert(m);
+
+ pa_alsa_redirect_errors_inc();
+ snd_config_update_free_global();
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(m->userdata = pa_alsa_sink_new(m, ma, __FILE__, NULL, NULL)))
+ goto fail;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+ pa_sink *sink;
+
+ pa_assert(m);
+ pa_assert_se(sink = m->userdata);
+
+ return pa_sink_linked_by(sink);
+}
+
+void pa__done(pa_module*m) {
+ pa_sink *sink;
+
+ pa_assert(m);
+
+ if ((sink = m->userdata))
+ pa_alsa_sink_free(sink);
+
+ 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
new file mode 100644
index 00000000..c35936df
--- /dev/null
+++ b/src/modules/alsa/module-alsa-source.c
@@ -0,0 +1,149 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 Lennart Poettering
+ Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
+
+ 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 <stdio.h>
+
+#include <asoundlib.h>
+
+#ifdef HAVE_VALGRIND_MEMCHECK_H
+#include <valgrind/memcheck.h>
+#endif
+
+#include <pulse/xmalloc.h>
+#include <pulse/util.h>
+#include <pulse/timeval.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/memchunk.h>
+#include <pulsecore/sink.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/sample-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/core-error.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/time-smoother.h>
+#include <pulsecore/rtclock.h>
+
+#include "alsa-util.h"
+#include "alsa-source.h"
+#include "module-alsa-source-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("ALSA Source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "name=<name for the source, to be prefixed> "
+ "source_name=<name for the source> "
+ "device=<ALSA device> "
+ "device_id=<ALSA card index> "
+ "format=<sample format> "
+ "rate=<sample rate> "
+ "channels=<number of channels> "
+ "channel_map=<channel map> "
+ "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=<upper fill watermark> "
+ "ignore_dB=<ignore dB information from the device?>");
+
+static const char* const valid_modargs[] = {
+ "name",
+ "source_name",
+ "device",
+ "device_id",
+ "format",
+ "rate",
+ "channels",
+ "channel_map",
+ "fragments",
+ "fragment_size",
+ "mmap",
+ "tsched",
+ "tsched_buffer_size",
+ "tsched_buffer_watermark",
+ "ignore_dB",
+ NULL
+};
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+
+ pa_assert(m);
+
+ pa_alsa_redirect_errors_inc();
+ snd_config_update_free_global();
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments");
+ goto fail;
+ }
+
+ if (!(m->userdata = pa_alsa_source_new(m, ma, __FILE__, NULL, NULL)))
+ goto fail;
+
+ pa_modargs_free(ma);
+
+ return 0;
+
+fail:
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+int pa__get_n_used(pa_module *m) {
+ pa_source *source;
+
+ pa_assert(m);
+ pa_assert_se(source = m->userdata);
+
+ return pa_source_linked_by(source);
+}
+
+void pa__done(pa_module*m) {
+ pa_source *source;
+
+ pa_assert(m);
+
+ if ((source = m->userdata))
+ pa_alsa_source_free(source);
+
+ snd_config_update_free_global();
+ pa_alsa_redirect_errors_dec();
+}
diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c
index cb4746a4..e2f6d019 100644
--- a/src/modules/bluetooth/module-bluetooth-device.c
+++ b/src/modules/bluetooth/module-bluetooth-device.c
@@ -65,7 +65,10 @@ PA_MODULE_LOAD_ONCE(FALSE);
PA_MODULE_USAGE(
"sink_name=<name of the device> "
"address=<address of the device> "
- "profile=<a2dp|hsp>");
+ "profile=<a2dp|hsp> "
+ "rate=<sample rate> "
+ "channels=<number of channels> "
+ "path=<device object path>");
struct bt_a2dp {
sbc_capabilities_t sbc_capabilities;
@@ -85,6 +88,7 @@ struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
+ pa_source *source;
pa_thread_mq thread_mq;
pa_rtpoll *rtpoll;
@@ -109,6 +113,8 @@ struct userdata {
pa_usec_t latency;
struct bt_a2dp a2dp;
+ char *path;
+ pa_dbus_connection *conn;
};
static const char* const valid_modargs[] = {
@@ -117,6 +123,7 @@ static const char* const valid_modargs[] = {
"profile",
"rate",
"channels",
+ "path",
NULL
};
@@ -228,9 +235,9 @@ static int bt_getcaps(struct userdata *u) {
msg.getcaps_req.h.length = sizeof(msg.getcaps_req);
strncpy(msg.getcaps_req.device, u->addr, 18);
- if (strcasecmp(u->profile, "a2dp") == 0)
+ if (pa_streq(u->profile, "a2dp"))
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_A2DP;
- else if (strcasecmp(u->profile, "hsp") == 0)
+ else if (pa_streq(u->profile, "hsp"))
msg.getcaps_req.transport = BT_CAPABILITIES_TRANSPORT_SCO;
else {
pa_log_error("Invalid profile argument: %s", u->profile);
@@ -448,8 +455,11 @@ static int bt_setconf(struct userdata *u) {
}
u->ss.format = PA_SAMPLE_S16LE;
}
- else
- u->ss.format = PA_SAMPLE_U8;
+ else {
+ u->ss.format = PA_SAMPLE_S16LE;
+ u->ss.channels = 1;
+ u->ss.rate = 8000;
+ }
memset(msg.buf, 0, BT_SUGGESTED_BUFFER_SIZE);
msg.setconf_req.h.type = BT_REQUEST;
@@ -597,7 +607,7 @@ static int sco_process_render(struct userdata *u) {
for (;;) {
ssize_t l;
- l = pa_loop_write(u->stream_fd, (uint8_t*) p, memchunk.length, NULL);
+ l = pa_loop_write(u->stream_fd, p, memchunk.length, NULL);
pa_log_debug("Memblock written to socket: %li bytes", (long) l);
pa_assert(l != 0);
@@ -786,6 +796,144 @@ finish:
pa_log_debug("IO thread shutting down");
}
+static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *userdata) {
+ DBusMessageIter arg_i;
+ DBusError err;
+ const char *value;
+ struct userdata *u;
+
+ pa_assert(bus);
+ pa_assert(msg);
+ pa_assert(userdata);
+ u = userdata;
+
+ pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
+ dbus_message_get_interface(msg),
+ dbus_message_get_path(msg),
+ dbus_message_get_member(msg));
+
+ dbus_error_init(&err);
+
+ if (dbus_message_is_signal(msg, "org.bluez.Headset", "PropertyChanged") ||
+ dbus_message_is_signal(msg, "org.bluez.AudioSink", "PropertyChanged")) {
+
+ struct device *d;
+ const char *profile;
+ DBusMessageIter variant_i;
+ dbus_uint16_t gain;
+
+ if (!dbus_message_iter_init(msg, &arg_i)) {
+ pa_log("dbus: message has no parameters");
+ goto done;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ goto done;
+ }
+
+ dbus_message_iter_get_basic(&arg_i, &value);
+
+ if (!dbus_message_iter_next(&arg_i)) {
+ pa_log("Property value missing");
+ goto done;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&arg_i, &variant_i);
+
+ if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_UINT16) {
+ dbus_message_iter_get_basic(&variant_i, &gain);
+
+ if (pa_streq(value, "SpeakerGain")) {
+ pa_log("spk gain: %d", gain);
+ pa_cvolume_set(&u->sink->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ u->sink->virtual_volume = u->sink->volume;
+ pa_subscription_post(u->sink->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, u->sink->index);
+ } else {
+ pa_log("mic gain: %d", gain);
+ if (!u->source)
+ goto done;
+
+ pa_cvolume_set(&u->source->volume, 1, (pa_volume_t) (gain * PA_VOLUME_NORM / 15));
+ pa_subscription_post(u->source->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, u->source->index);
+ }
+ }
+ }
+
+done:
+ dbus_error_free(&err);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static int sink_get_volume_cb(pa_sink *s) {
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ /* refresh? */
+
+ return 0;
+}
+
+static int source_get_volume_cb(pa_source *s) {
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ /* refresh? */
+
+ return 0;
+}
+
+static int sink_set_volume_cb(pa_sink *s) {
+ DBusError e;
+ DBusMessage *m, *r;
+ DBusMessageIter it, itvar;
+ dbus_uint16_t vol;
+ const char *spkgain = "SpeakerGain";
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ dbus_error_init(&e);
+
+ vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+ pa_log_debug("set headset volume: %d", vol);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetProperty"));
+ dbus_message_iter_init_append(m, &it);
+ dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &spkgain);
+ dbus_message_iter_open_container(&it, DBUS_TYPE_VARIANT, DBUS_TYPE_UINT16_AS_STRING, &itvar);
+ dbus_message_iter_append_basic(&itvar, DBUS_TYPE_UINT16, &vol);
+ dbus_message_iter_close_container(&it, &itvar);
+
+ r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->conn), m, -1, &e);
+
+finish:
+ if (m)
+ dbus_message_unref(m);
+ if (r)
+ dbus_message_unref(r);
+
+ dbus_error_free(&e);
+
+ return 0;
+}
+
+static int source_set_volume_cb(pa_source *s) {
+ dbus_uint16_t vol;
+ struct userdata *u = s->userdata;
+ pa_assert(u);
+
+ vol = ((float)pa_cvolume_max(&s->volume) / PA_VOLUME_NORM) * 15;
+
+ pa_log_debug("set headset mic volume: %d (not implemented yet)", vol);
+
+ return 0;
+}
+
int pa__init(pa_module* m) {
int e;
pa_modargs *ma;
@@ -793,8 +941,12 @@ int pa__init(pa_module* m) {
pa_sink_new_data data;
struct pollfd *pollfd;
struct userdata *u;
+ DBusError err;
+ char *tmp;
pa_assert(m);
+ dbus_error_init(&err);
+
m->userdata = u = pa_xnew0(struct userdata, 1);
u->module = m;
u->core = m->core;
@@ -830,6 +982,7 @@ int pa__init(pa_module* m) {
pa_log_error("Failed to get rate from module arguments");
goto fail;
}
+ u->path = pa_xstrdup(pa_modargs_get_value(ma, "path", NULL));
channels = u->ss.channels;
if (pa_modargs_get_value_u32(ma, "channels", &channels) < 0) {
@@ -907,7 +1060,50 @@ int pa__init(pa_module* m) {
goto fail;
}
pa_sink_put(u->sink);
+ if (!u->path)
+ goto end;
+
+ /* connect to the bus */
+ u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
+ if (dbus_error_is_set(&err) || (u->conn == NULL) ) {
+ pa_log("Failed to get D-Bus connection: %s", err.message);
+ goto fail;
+ }
+
+ /* monitor property changes */
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
+ pa_log_error("Failed to add filter function");
+ goto fail;
+ }
+
+ if (pa_streq(u->profile, "hsp")) {
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
+ pa_xfree(tmp);
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.Headset signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+ } else {
+
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_add_match(pa_dbus_connection_get(u->conn), tmp, &err);
+ pa_xfree(tmp);
+ if (dbus_error_is_set(&err)) {
+ pa_log_error("Unable to subscribe to org.bluez.AudioSink signals: %s: %s", err.name, err.message);
+ goto fail;
+ }
+ }
+
+ u->sink->get_volume = sink_get_volume_cb;
+ u->sink->set_volume = sink_set_volume_cb;
+ if (u->source) {
+ u->source->get_volume = source_get_volume_cb;
+ u->source->set_volume = source_set_volume_cb;
+ }
+
+end:
pa_modargs_free(ma);
return 0;
@@ -926,6 +1122,31 @@ void pa__done(pa_module *m) {
if (!(u = m->userdata))
return;
+ if (u->conn) {
+ DBusError error;
+ char *tmp;
+ dbus_error_init(&error);
+
+ if (pa_streq(u->profile, "hsp")) {
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
+ pa_xfree(tmp);
+ dbus_error_free(&error);
+ } else {
+ tmp = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged',path='%s'", u->path);
+ dbus_bus_remove_match(pa_dbus_connection_get(u->conn), tmp, &error);
+ pa_xfree(tmp);
+ dbus_error_free(&error);
+ }
+
+ dbus_connection_remove_filter(pa_dbus_connection_get(u->conn), filter_cb, u);
+
+ pa_dbus_connection_unref(u->conn);
+ }
+
+ if (u->path)
+ pa_xfree(u->path);
+
if (u->sink)
pa_sink_unlink(u->sink);
diff --git a/src/modules/bluetooth/module-bluetooth-discover.c b/src/modules/bluetooth/module-bluetooth-discover.c
index 2fe09370..1bc05c02 100644
--- a/src/modules/bluetooth/module-bluetooth-discover.c
+++ b/src/modules/bluetooth/module-bluetooth-discover.c
@@ -53,6 +53,13 @@ struct uuid {
PA_LLIST_FIELDS(struct uuid);
};
+struct dbus_pending {
+ char *path;
+ char *profile;
+ DBusPendingCall *pending;
+ PA_LLIST_FIELDS(struct dbus_pending);
+};
+
struct device {
char *name;
char *object_path;
@@ -70,7 +77,9 @@ struct device {
struct userdata {
pa_module *module;
pa_dbus_connection *conn;
+ dbus_int32_t dbus_data_slot;
PA_LLIST_HEAD(struct device, device_list);
+ PA_LLIST_HEAD(struct dbus_pending, dbus_pending_list);
};
static struct module *module_new(const char *profile, pa_module *pa_m) {
@@ -118,6 +127,31 @@ static void uuid_free(struct uuid *uuid) {
pa_xfree(uuid);
}
+static struct dbus_pending *dbus_pending_new(struct userdata *u, DBusPendingCall *pending, const char *path, const char *profile) {
+ struct dbus_pending *node;
+
+ pa_assert(pending);
+
+ node = pa_xnew(struct dbus_pending, 1);
+ node->pending = pending;
+ node->path = pa_xstrdup(path);
+ node->profile = pa_xstrdup(profile);
+ PA_LLIST_INIT(struct dbus_pending, node);
+ dbus_pending_call_set_data(pending, u->dbus_data_slot, node, NULL);
+
+ return node;
+}
+
+static void dbus_pending_free(struct dbus_pending *pending) {
+ pa_assert(pending);
+
+ pa_xfree(pending->path);
+ pa_xfree(pending->profile);
+ dbus_pending_call_cancel(pending->pending);
+ dbus_pending_call_unref(pending->pending);
+ pa_xfree(pending);
+}
+
static struct device *device_new(const char *object_path) {
struct device *node;
@@ -342,7 +376,7 @@ static void load_module_for_device(struct userdata *u, struct device *d, const c
pa_assert(d);
get_device_properties(u, d);
- args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\"", d->name, d->address, profile);
+ args = pa_sprintf_malloc("sink_name=\"%s\" address=\"%s\" profile=\"%s\" path=\"%s\"", d->name, d->address, profile, d->object_path);
pa_m = pa_module_load(u->module->core, "module-bluetooth-device", args);
pa_xfree(args);
@@ -468,23 +502,269 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *msg, void *
done:
dbus_error_free(&err);
- return DBUS_HANDLER_RESULT_HANDLED;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+
+static void get_properties_reply(DBusPendingCall *pending, void *user_data) {
+ struct userdata *u;
+ DBusMessage *r;
+ dbus_bool_t connected;
+ DBusMessageIter arg_i, element_i;
+ DBusMessageIter variant_i;
+ struct device *d;
+ struct dbus_pending *p;
+
+ pa_assert(u = user_data);
+
+ r = dbus_pending_call_steal_reply(pending);
+ if (!r)
+ goto end;
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_iter_init(r, &arg_i)) {
+ pa_log("%s GetProperties reply has no arguments", p->profile);
+ goto end;
+ }
+
+ if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
+ pa_log("%s GetProperties argument is not an array", p->profile);
+ goto end;
+ }
+
+ connected = FALSE;
+ dbus_message_iter_recurse(&arg_i, &element_i);
+ while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
+
+ if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter dict_i;
+ const char *key;
+
+ dbus_message_iter_recurse(&element_i, &dict_i);
+
+ if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_STRING) {
+ pa_log("Property name not a string.");
+ goto end;
+ }
+
+ dbus_message_iter_get_basic(&dict_i, &key);
+
+ if (!dbus_message_iter_next(&dict_i)) {
+ pa_log("Property value missing");
+ goto end;
+ }
+
+ if (dbus_message_iter_get_arg_type(&dict_i) != DBUS_TYPE_VARIANT) {
+ pa_log("Property value not a variant.");
+ goto end;
+ }
+
+ dbus_message_iter_recurse(&dict_i, &variant_i);
+
+ switch (dbus_message_iter_get_arg_type(&variant_i)) {
+
+ case DBUS_TYPE_BOOLEAN: {
+
+ dbus_bool_t value;
+ dbus_message_iter_get_basic(&variant_i, &value);
+
+ if (pa_streq(key, "Connected")) {
+ connected = value;
+ goto endloop;
+ }
+
+ break;
+ }
+ }
+ }
+
+ if (!dbus_message_iter_next(&element_i))
+ break;
+ }
+
+endloop:
+ if (connected) {
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ pa_log_debug("%s: %s connected", p->path, p->profile);
+ d = device_find(u, p->path);
+
+ if (!d) {
+ d = device_new(p->path);
+ PA_LLIST_PREPEND(struct device, u->device_list, d);
+ }
+
+ load_module_for_device(u, d, p->profile);
+ }
+
+ dbus_message_unref(r);
+
+end:
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+}
+
+static void list_devices_reply(DBusPendingCall *pending, void *user_data) {
+ DBusMessage *r, *m;
+ DBusPendingCall *call;
+ DBusError e;
+ char **paths = NULL;
+ int i, num = -1;
+ struct dbus_pending *p;
+ struct userdata *u;
+
+ pa_assert(u = user_data);
+ dbus_error_init(&e);
+
+ r = dbus_pending_call_steal_reply(pending);
+ if (!r) {
+ pa_log("Failed to get ListDevices reply");
+ goto end;
+ }
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
+ dbus_error_free(&e);
+ } else {
+ for (i = 0; i < num; ++i) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Headset", "GetProperties"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, paths[i], "hsp");
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
+ } else {
+ pa_log("Failed to send GetProperties");
+ }
+
+ dbus_message_unref(m);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.AudioSink", "GetProperties"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, paths[i], "a2dp");
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, get_properties_reply, u, NULL);
+ } else {
+ pa_log("Failed to send GetProperties");
+ }
+
+ dbus_message_unref(m);
+ }
+ }
+
+ if (paths)
+ dbus_free_string_array (paths);
+ dbus_message_unref(r);
+
+end:
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+}
+
+static void list_adapters_reply(DBusPendingCall *pending, void *user_data) {
+ DBusMessage *r, *m;
+ DBusPendingCall *call;
+ DBusError e;
+ char **paths = NULL;
+ int i, num = -1;
+ struct dbus_pending *p;
+ struct userdata *u;
+
+ pa_assert(u = user_data);
+ dbus_error_init(&e);
+
+ r = dbus_pending_call_steal_reply(pending);
+ if (!r) {
+ pa_log("Failed to get ListAdapters reply");
+ goto end;
+ }
+
+ if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
+ pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
+ goto end;
+ }
+
+ if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
+ pa_log("org.bluez.Manager.ListAdapters returned an error: '%s'\n", e.message);
+ dbus_error_free(&e);
+ } else {
+ for (i = 0; i < num; ++i) {
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", paths[i], "org.bluez.Adapter", "ListDevices"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, NULL, NULL);
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, list_devices_reply, u, NULL);
+ } else {
+ pa_log("Failed to send ListDevices");
+ }
+
+ dbus_message_unref(m);
+ }
+ }
+
+ if (paths)
+ dbus_free_string_array (paths);
+ dbus_message_unref(r);
+
+end:
+ p = dbus_pending_call_get_data(pending, u->dbus_data_slot);
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+}
+
+static void lookup_devices(struct userdata *u) {
+ DBusMessage *m;
+ DBusPendingCall *call;
+ struct dbus_pending *p;
+
+ pa_assert(u);
+
+ pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
+ if (dbus_connection_send_with_reply(pa_dbus_connection_get(u->conn), m, &call, -1)) {
+ p = dbus_pending_new(u, call, NULL, NULL);
+ PA_LLIST_PREPEND(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_call_set_notify(call, list_adapters_reply, u, NULL);
+ } else {
+ pa_log("Failed to send ListAdapters");
+ }
+
+ dbus_message_unref(m);
}
void pa__done(pa_module* m) {
struct userdata *u;
struct device *i;
+ struct dbus_pending *p;
pa_assert(m);
if (!(u = m->userdata))
return;
+ while ((p = u->dbus_pending_list)) {
+ PA_LLIST_REMOVE(struct dbus_pending, u->dbus_pending_list, p);
+ dbus_pending_free(p);
+ }
+
while ((i = u->device_list)) {
PA_LLIST_REMOVE(struct device, u->device_list, i);
device_free(i);
}
+ if (u->dbus_data_slot != -1) {
+ dbus_pending_call_free_data_slot(&u->dbus_data_slot);
+ }
+
if (u->conn) {
DBusError error;
dbus_error_init(&error);
@@ -514,8 +794,10 @@ int pa__init(pa_module* m) {
dbus_error_init(&err);
m->userdata = u = pa_xnew(struct userdata, 1);
+ u->dbus_data_slot = -1;
u->module = m;
PA_LLIST_HEAD_INIT(struct device, u->device_list);
+ PA_LLIST_HEAD_INIT(DBusPendingCall, u->dbus_pending_list);
/* connect to the bus */
u->conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &err);
@@ -524,6 +806,9 @@ int pa__init(pa_module* m) {
goto fail;
}
+ if (!dbus_pending_call_allocate_data_slot(&u->dbus_data_slot))
+ goto fail;
+
/* dynamic detection of bluetooth audio devices */
if (!dbus_connection_add_filter(pa_dbus_connection_get(u->conn), filter_cb, u, NULL)) {
pa_log_error("Failed to add filter function");
@@ -548,6 +833,8 @@ int pa__init(pa_module* m) {
goto fail;
}
+ lookup_devices(u);
+
return 0;
fail:
diff --git a/src/modules/bluetooth/module-bluetooth-proximity.c b/src/modules/bluetooth/module-bluetooth-proximity.c
index 4cfaaf5b..f30d39fe 100644
--- a/src/modules/bluetooth/module-bluetooth-proximity.c
+++ b/src/modules/bluetooth/module-bluetooth-proximity.c
@@ -103,7 +103,7 @@ static void update_volume(struct userdata *u) {
u->muted = FALSE;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
return;
}
@@ -116,7 +116,7 @@ static void update_volume(struct userdata *u) {
u->muted = TRUE;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
return;
}
diff --git a/src/modules/dbus-util.h b/src/modules/dbus-util.h
index 2b24ac63..c4794dac 100644
--- a/src/modules/dbus-util.h
+++ b/src/modules/dbus-util.h
@@ -24,6 +24,8 @@
#include <dbus/dbus.h>
+#include <pulsecore/core.h>
+
typedef struct pa_dbus_connection pa_dbus_connection;
/* return the DBusConnection of the specified type for the given core,
diff --git a/src/modules/hal-util.c b/src/modules/hal-util.c
new file mode 100644
index 00000000..82bbc57e
--- /dev/null
+++ b/src/modules/hal-util.c
@@ -0,0 +1,127 @@
+/***
+ 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 <pulsecore/log.h>
+
+#include <hal/libhal.h>
+
+#include "dbus-util.h"
+#include "hal-util.h"
+
+int pa_hal_get_info(pa_core *core, pa_proplist *p, int card) {
+ pa_dbus_connection *c = NULL;
+ LibHalContext *hal = NULL;
+ DBusError error;
+ int r = -1;
+ char **udis = NULL, *t;
+ int n, i;
+
+ pa_assert(core);
+ pa_assert(p);
+ pa_assert(card >= 0);
+
+ dbus_error_init(&error);
+
+ if (!(c = pa_dbus_bus_get(core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
+ pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+
+ if (!(hal = libhal_ctx_new())) {
+ pa_log_error("libhal_ctx_new() finished");
+ goto finish;
+ }
+
+ if (!libhal_ctx_set_dbus_connection(hal, pa_dbus_connection_get(c))) {
+ pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ if (!libhal_ctx_init(hal, &error)) {
+ pa_log_error("Couldn't connect to hald: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ if (!(udis = libhal_find_device_by_capability(hal, "sound", &n, &error)) < 0) {
+ pa_log_error("Couldn't find devices: %s: %s", error.name, error.message);
+ goto finish;
+ }
+
+ for (i = 0; i < n; i++) {
+ dbus_int32_t this_card;
+
+ this_card = libhal_device_get_property_int(hal, udis[i], "sound.card", &error);
+ if (dbus_error_is_set(&error)) {
+ dbus_error_free(&error);
+ continue;
+ }
+
+ if (this_card == card)
+ break;
+
+ }
+
+ if (i >= n)
+ goto finish;
+
+ pa_proplist_sets(p, "hal.udi", udis[i]);
+
+ t = libhal_device_get_property_string(hal, udis[i], "info.product", &error);
+ if (dbus_error_is_set(&error))
+ dbus_error_free(&error);
+ if (t) {
+ pa_proplist_sets(p, "hal.product", t);
+ libhal_free_string(t);
+ }
+
+ t = libhal_device_get_property_string(hal, udis[i], "sound.card_id", &error);
+ if (dbus_error_is_set(&error))
+ dbus_error_free(&error);
+ if (t) {
+ pa_proplist_sets(p, "hal.card_id", t);
+ libhal_free_string(t);
+ }
+
+ r = 0;
+
+finish:
+
+ if (udis)
+ libhal_free_string_array(udis);
+
+ dbus_error_free(&error);
+
+ if (hal) {
+ libhal_ctx_shutdown(hal, &error);
+ libhal_ctx_free(hal);
+ dbus_error_free(&error);
+ }
+
+ if (c)
+ pa_dbus_connection_unref(c);
+
+ return r;
+}
diff --git a/src/modules/hal-util.h b/src/modules/hal-util.h
new file mode 100644
index 00000000..3c0e0943
--- /dev/null
+++ b/src/modules/hal-util.h
@@ -0,0 +1,30 @@
+#ifndef foohalutilhfoo
+#define foohalutilhfoo
+
+/***
+ 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.
+***/
+
+
+#include <pulsecore/core.h>
+
+int pa_hal_get_info(pa_core *core, pa_proplist *p, int card);
+
+#endif
diff --git a/src/modules/module-always-sink.c b/src/modules/module-always-sink.c
index cd3f3112..591695fb 100644
--- a/src/modules/module-always-sink.c
+++ b/src/modules/module-always-sink.c
@@ -100,6 +100,10 @@ static pa_hook_result_t put_hook_callback(pa_core *c, pa_sink *sink, void* userd
if (u->ignore)
return PA_HOOK_OK;
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
/* Auto-loaded null-sink not active, so ignoring newly detected sink. */
if (u->null_module == PA_INVALID_INDEX)
return PA_HOOK_OK;
@@ -130,6 +134,10 @@ static pa_hook_result_t unlink_hook_callback(pa_core *c, pa_sink *sink, void* us
return PA_HOOK_OK;
}
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
load_null_sink_if_needed(c, sink, u);
return PA_HOOK_OK;
@@ -172,7 +180,7 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->put_slot);
if (u->unlink_slot)
pa_hook_slot_free(u->unlink_slot);
- if (u->null_module != PA_INVALID_INDEX)
+ if (u->null_module != PA_INVALID_INDEX && m->core->state != PA_CORE_SHUTDOWN)
pa_module_unload_request_by_index(m->core, u->null_module, TRUE);
pa_xfree(u->sink_name);
diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
new file mode 100644
index 00000000..02e973c4
--- /dev/null
+++ b/src/modules/module-card-restore.c
@@ -0,0 +1,284 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2006-2008 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 <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <gdbm.h>
+
+#include <pulse/xmalloc.h>
+#include <pulse/volume.h>
+#include <pulse/timeval.h>
+#include <pulse/util.h>
+
+#include <pulsecore/core-error.h>
+#include <pulsecore/module.h>
+#include <pulsecore/core-util.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/log.h>
+#include <pulsecore/core-subscribe.h>
+#include <pulsecore/card.h>
+#include <pulsecore/namereg.h>
+
+#include "module-card-restore-symdef.h"
+
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("Automatically restore profile of cards");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(TRUE);
+
+#define SAVE_INTERVAL 10
+
+static const char* const valid_modargs[] = {
+ NULL
+};
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_subscription *subscription;
+ pa_hook_slot *card_new_hook_slot;
+ pa_time_event *save_time_event;
+ GDBM_FILE gdbm_file;
+};
+
+struct entry {
+ char profile[PA_NAME_MAX];
+};
+
+static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(a);
+ pa_assert(e);
+ pa_assert(tv);
+ pa_assert(u);
+
+ pa_assert(e == u->save_time_event);
+ u->core->mainloop->time_free(u->save_time_event);
+ u->save_time_event = NULL;
+
+ gdbm_sync(u->gdbm_file);
+ pa_log_info("Synced.");
+}
+
+static struct entry* read_entry(struct userdata *u, const char *name) {
+ datum key, data;
+ struct entry *e;
+
+ pa_assert(u);
+ pa_assert(name);
+
+ key.dptr = (char*) name;
+ key.dsize = (int) strlen(name);
+
+ data = gdbm_fetch(u->gdbm_file, key);
+
+ if (!data.dptr)
+ goto fail;
+
+ if (data.dsize != sizeof(struct entry)) {
+ pa_log_warn("Database contains entry for card %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ goto fail;
+ }
+
+ e = (struct entry*) data.dptr;
+
+ if (!memchr(e->profile, 0, sizeof(e->profile))) {
+ pa_log_warn("Database contains entry for card %s with missing NUL byte in profile name", name);
+ goto fail;
+ }
+
+ return e;
+
+fail:
+
+ pa_xfree(data.dptr);
+ return NULL;
+}
+
+static void trigger_save(struct userdata *u) {
+ struct timeval tv;
+
+ if (u->save_time_event)
+ return;
+
+ pa_gettimeofday(&tv);
+ tv.tv_sec += SAVE_INTERVAL;
+ u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
+}
+
+static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
+ struct userdata *u = userdata;
+ struct entry entry, *old;
+ datum key, data;
+ pa_card *card;
+
+ pa_assert(c);
+ pa_assert(u);
+
+ if (t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW) &&
+ t != (PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE))
+ return;
+
+ memset(&entry, 0, sizeof(entry));
+
+ if (!(card = pa_idxset_get_by_index(c->cards, idx)))
+ return;
+
+ pa_strlcpy(entry.profile, card->active_profile ? card->active_profile->name : "", sizeof(entry.profile));
+
+ if ((old = read_entry(u, card->name))) {
+
+ if (strncmp(old->profile, entry.profile, sizeof(entry.profile)) == 0) {
+ pa_xfree(old);
+ return;
+ }
+
+ pa_xfree(old);
+ }
+
+ key.dptr = card->name;
+ key.dsize = (int) strlen(card->name);
+
+ data.dptr = (void*) &entry;
+ data.dsize = sizeof(entry);
+
+ pa_log_info("Storing profile for card %s.", card->name);
+
+ gdbm_store(u->gdbm_file, key, data, GDBM_REPLACE);
+
+ trigger_save(u);
+}
+
+static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new_data, struct userdata *u) {
+ struct entry *e;
+
+ pa_assert(new_data);
+
+ if ((e = read_entry(u, new_data->name)) && e->profile) {
+
+ if (!new_data->active_profile) {
+ pa_card_new_data_set_profile(new_data, e->profile);
+ pa_log_info("Restoring profile for card %s.", new_data->name);
+ } else
+ pa_log_debug("Not restoring profile for card %s, because already set.", new_data->name);
+
+ pa_xfree(e);
+ }
+
+ return PA_HOOK_OK;
+}
+
+int pa__init(pa_module*m) {
+ pa_modargs *ma = NULL;
+ struct userdata *u;
+ char *fname, *fn;
+ pa_card *card;
+ uint32_t idx;
+ int gdbm_cache_size;
+
+ 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_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->save_time_event = NULL;
+ u->gdbm_file = NULL;
+
+ u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_CARD, subscribe_callback, u);
+
+ u->card_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
+
+ /* We include the host identifier in the file name because gdbm
+ * files are CPU dependant, and we don't want things to go wrong
+ * if we are on a multiarch system. */
+
+ fn = pa_sprintf_malloc("card-database."CANONICAL_HOST".gdbm");
+ fname = pa_state_path(fn, TRUE);
+ pa_xfree(fn);
+
+ if (!fname)
+ goto fail;
+
+ if (!(u->gdbm_file = gdbm_open(fname, 0, GDBM_WRCREAT|GDBM_NOLOCK, 0600, NULL))) {
+ pa_log("Failed to open volume database '%s': %s", fname, gdbm_strerror(gdbm_errno));
+ pa_xfree(fname);
+ goto fail;
+ }
+
+ /* By default the cache of gdbm is rather large, let's reduce it a bit to save memory */
+ gdbm_cache_size = 10;
+ gdbm_setopt(u->gdbm_file, GDBM_CACHESIZE, &gdbm_cache_size, sizeof(gdbm_cache_size));
+
+ pa_log_info("Sucessfully opened database file '%s'.", fname);
+ pa_xfree(fname);
+
+ for (card = pa_idxset_first(m->core->cards, &idx); card; card = pa_idxset_next(m->core->cards, &idx))
+ subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_NEW, card->index, u);
+
+ pa_modargs_free(ma);
+ return 0;
+
+fail:
+ pa__done(m);
+
+ if (ma)
+ pa_modargs_free(ma);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata* u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->subscription)
+ pa_subscription_free(u->subscription);
+
+ if (u->card_new_hook_slot)
+ pa_hook_slot_free(u->card_new_hook_slot);
+
+ if (u->save_time_event)
+ u->core->mainloop->time_free(u->save_time_event);
+
+ if (u->gdbm_file)
+ gdbm_close(u->gdbm_file);
+
+ pa_xfree(u);
+}
diff --git a/src/modules/module-combine.c b/src/modules/module-combine.c
index d61d127a..82c88711 100644
--- a/src/modules/module-combine.c
+++ b/src/modules/module-combine.c
@@ -504,7 +504,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
* we are heard right-away. */
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT)
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
/* Called from thread context */
@@ -627,6 +627,7 @@ static int sink_set_state(pa_sink *sink, pa_sink_state_t state) {
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -1103,7 +1104,7 @@ int pa__init(pa_module*m) {
while ((n = pa_split(slaves, ",", &split_state))) {
pa_sink *slave_sink;
- if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK, TRUE)) || slave_sink == u->sink) {
+ if (!(slave_sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK)) || slave_sink == u->sink) {
pa_log("Invalid slave sink '%s'", n);
pa_xfree(n);
goto fail;
diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c
index e1933c28..4f3ed8dd 100644
--- a/src/modules/module-console-kit.c
+++ b/src/modules/module-console-kit.c
@@ -63,6 +63,7 @@ struct session {
};
struct userdata {
+ pa_module *module;
pa_core *core;
pa_dbus_connection *connection;
pa_hashmap *sessions;
@@ -73,7 +74,7 @@ static void add_session(struct userdata *u, const char *id) {
DBusMessage *m = NULL, *reply = NULL;
uint32_t uid;
struct session *session;
- char *t;
+ pa_client_new_data data;
dbus_error_init (&error);
@@ -109,11 +110,19 @@ static void add_session(struct userdata *u, const char *id) {
session = pa_xnew(struct session, 1);
session->id = pa_xstrdup(id);
- t = pa_sprintf_malloc("ConsoleKit Session %s", id);
- session->client = pa_client_new(u->core, __FILE__, t);
- pa_xfree(t);
-
- pa_proplist_sets(session->client->proplist, "console-kit.session", id);
+ pa_client_new_data_init(&data);
+ data.module = u->module;
+ data.driver = __FILE__;
+ pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "ConsoleKit Session %s", id);
+ pa_proplist_sets(data.proplist, "console-kit.session", id);
+ session->client = pa_client_new(u->core, &data);
+ pa_client_new_data_done(&data);
+
+ if (!session->client) {
+ pa_xfree(session->id);
+ pa_xfree(session);
+ goto fail;
+ }
pa_hashmap_put(u->sessions, session->id, session);
@@ -295,6 +304,7 @@ int pa__init(pa_module*m) {
m->userdata = u = pa_xnew(struct userdata, 1);
u->core = m->core;
+ u->module = m;
u->connection = connection;
u->sessions = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
diff --git a/src/modules/module-default-device-restore.c b/src/modules/module-default-device-restore.c
index d2cc24f3..d299f40b 100644
--- a/src/modules/module-default-device-restore.c
+++ b/src/modules/module-default-device-restore.c
@@ -57,10 +57,11 @@ static void load(struct userdata *u) {
/* We never overwrite manually configured settings */
- if (u->core->default_sink_name)
+ if (u->core->default_sink)
pa_log_info("Manually configured default sink, not overwriting.");
else if ((f = fopen(u->sink_filename, "r"))) {
char ln[256] = "";
+ pa_sink *s;
fgets(ln, sizeof(ln)-1, f);
pa_strip_nl(ln);
@@ -68,8 +69,8 @@ static void load(struct userdata *u) {
if (!ln[0])
pa_log_info("No previous default sink setting, ignoring.");
- else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SINK, TRUE)) {
- pa_namereg_set_default(u->core, ln, PA_NAMEREG_SINK);
+ else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SINK))) {
+ pa_namereg_set_default_sink(u->core, s);
pa_log_info("Restored default sink '%s'.", ln);
} else
pa_log_info("Saved default sink '%s' not existant, not restoring default sink setting.", ln);
@@ -77,10 +78,11 @@ static void load(struct userdata *u) {
} else if (errno != ENOENT)
pa_log("Failed to load default sink: %s", pa_cstrerror(errno));
- if (u->core->default_source_name)
+ if (u->core->default_source)
pa_log_info("Manually configured default source, not overwriting.");
else if ((f = fopen(u->source_filename, "r"))) {
char ln[256] = "";
+ pa_source *s;
fgets(ln, sizeof(ln)-1, f);
pa_strip_nl(ln);
@@ -88,8 +90,8 @@ static void load(struct userdata *u) {
if (!ln[0])
pa_log_info("No previous default source setting, ignoring.");
- else if (pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE, TRUE)) {
- pa_namereg_set_default(u->core, ln, PA_NAMEREG_SOURCE);
+ else if ((s = pa_namereg_get(u->core, ln, PA_NAMEREG_SOURCE))) {
+ pa_namereg_set_default_source(u->core, s);
pa_log_info("Restored default source '%s'.", ln);
} else
pa_log_info("Saved default source '%s' not existant, not restoring default source setting.", ln);
@@ -106,8 +108,8 @@ static void save(struct userdata *u) {
if (u->sink_filename) {
if ((f = fopen(u->sink_filename, "w"))) {
- const char *n = pa_namereg_get_default_sink_name(u->core);
- fprintf(f, "%s\n", pa_strempty(n));
+ pa_sink *s = pa_namereg_get_default_sink(u->core);
+ fprintf(f, "%s\n", s ? s->name : "");
fclose(f);
} else
pa_log("Failed to save default sink: %s", pa_cstrerror(errno));
@@ -115,8 +117,8 @@ static void save(struct userdata *u) {
if (u->source_filename) {
if ((f = fopen(u->source_filename, "w"))) {
- const char *n = pa_namereg_get_default_source_name(u->core);
- fprintf(f, "%s\n", pa_strempty(n));
+ pa_source *s = pa_namereg_get_default_source(u->core);
+ fprintf(f, "%s\n", s ? s->name : "");
fclose(f);
} else
pa_log("Failed to save default source: %s", pa_cstrerror(errno));
diff --git a/src/modules/module-defs.h.m4 b/src/modules/module-defs.h.m4
index 64ce1928..f9924cfa 100644
--- a/src/modules/module-defs.h.m4
+++ b/src/modules/module-defs.h.m4
@@ -18,9 +18,11 @@ gen_symbol(pa__get_description)
gen_symbol(pa__get_usage)
gen_symbol(pa__get_version)
gen_symbol(pa__load_once)
+gen_symbol(pa__get_n_used)
int pa__init(pa_module*m);
void pa__done(pa_module*m);
+int pa__get_n_used(pa_module*m);
const char* pa__get_author(void);
const char* pa__get_description(void);
diff --git a/src/modules/module-detect.c b/src/modules/module-detect.c
index 1616d47c..9ed262db 100644
--- a/src/modules/module-detect.c
+++ b/src/modules/module-detect.c
@@ -239,7 +239,7 @@ int pa__init(pa_module*m) {
#ifdef HAVE_ALSA
if ((n = detect_alsa(m->core, just_one)) <= 0)
#endif
-#if HAVE_OSS
+#ifdef HAVE_OSS
if ((n = detect_oss(m->core, just_one)) <= 0)
#endif
#ifdef HAVE_SOLARIS
diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c
index c0cb0dc5..8e0cf92b 100644
--- a/src/modules/module-device-restore.c
+++ b/src/modules/module-device-restore.c
@@ -53,6 +53,9 @@ PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Automatically restore the volume/mute state of devices");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "restore_volume=<Save/restore volumes?> "
+ "restore_muted=<Save/restore muted states?>");
#define SAVE_INTERVAL 10
@@ -98,14 +101,14 @@ static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct
pa_log_info("Synced.");
}
-static struct entry* read_entry(struct userdata *u, char *name) {
+static struct entry* read_entry(struct userdata *u, const char *name) {
datum key, data;
struct entry *e;
pa_assert(u);
pa_assert(name);
- key.dptr = name;
+ key.dptr = (char*) name;
key.dsize = (int) strlen(name);
data = gdbm_fetch(u->gdbm_file, key);
@@ -235,13 +238,22 @@ static pa_hook_result_t sink_fixate_hook_callback(pa_core *c, pa_sink_new_data *
if ((e = read_entry(u, name))) {
if (u->restore_volume) {
- pa_log_info("Restoring volume for sink %s.", new_data->name);
- pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+
+ if (!new_data->volume_is_set) {
+ pa_log_info("Restoring volume for sink %s.", new_data->name);
+ pa_sink_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+ } else
+ pa_log_debug("Not restoring volume for sink %s, because already set.", new_data->name);
+
}
if (u->restore_muted) {
- pa_log_info("Restoring mute state for sink %s.", new_data->name);
- pa_sink_new_data_set_muted(new_data, e->muted);
+
+ if (!new_data->muted_is_set) {
+ pa_log_info("Restoring mute state for sink %s.", new_data->name);
+ pa_sink_new_data_set_muted(new_data, e->muted);
+ } else
+ pa_log_debug("Not restoring mute state for sink %s, because already set.", new_data->name);
}
pa_xfree(e);
@@ -263,13 +275,21 @@ static pa_hook_result_t source_fixate_hook_callback(pa_core *c, pa_source_new_da
if ((e = read_entry(u, name))) {
if (u->restore_volume) {
- pa_log_info("Restoring volume for source %s.", new_data->name);
- pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+
+ if (!new_data->volume_is_set) {
+ pa_log_info("Restoring volume for source %s.", new_data->name);
+ pa_source_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+ } else
+ pa_log_debug("Not restoring volume for source %s, because already set.", new_data->name);
}
if (u->restore_muted) {
- pa_log_info("Restoring mute state for source %s.", new_data->name);
- pa_source_new_data_set_muted(new_data, e->muted);
+
+ if (!new_data->muted_is_set) {
+ pa_log_info("Restoring mute state for source %s.", new_data->name);
+ pa_source_new_data_set_muted(new_data, e->muted);
+ } else
+ pa_log_debug("Not restoring mute state for source %s, because already set.", new_data->name);
}
pa_xfree(e);
diff --git a/src/modules/module-esound-sink.c b/src/modules/module-esound-sink.c
index 14f1810a..552cf75e 100644
--- a/src/modules/module-esound-sink.c
+++ b/src/modules/module-esound-sink.c
@@ -156,6 +156,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -168,7 +169,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
w = pa_bytes_to_usec((uint64_t) u->offset + u->memchunk.length, &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
- break;
+ return 0;
}
case SINK_MESSAGE_PASS_SOCKET: {
@@ -621,6 +622,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
diff --git a/src/modules/module-flat-volume.c b/src/modules/module-flat-volume.c
deleted file mode 100644
index 9bc8055a..00000000
--- a/src/modules/module-flat-volume.c
+++ /dev/null
@@ -1,224 +0,0 @@
-/***
- This file is part of PulseAudio.
-
- Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
- Copyright 2004-2006, 2008 Lennart Poettering
-
- Contact: Marc-Andre Lureau <marc-andre.lureau@nokia.com>
-
- 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 <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <regex.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <pulse/xmalloc.h>
-
-#include <pulsecore/core-error.h>
-#include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/log.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/core-util.h>
-#include <pulsecore/macro.h>
-
-#include "module-flat-volume-symdef.h"
-
-PA_MODULE_AUTHOR("Marc-Andre Lureau");
-PA_MODULE_DESCRIPTION("Flat volume");
-PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("");
-
-struct userdata {
- pa_subscription *subscription;
- pa_hook_slot *sink_input_set_volume_hook_slot;
- pa_hook_slot *sink_input_fixate_hook_slot;
-};
-
-static void process_input_volume_change(
- pa_cvolume *dest_volume,
- const pa_cvolume *dest_virtual_volume,
- pa_channel_map *dest_channel_map,
- pa_sink_input *this,
- pa_sink *sink) {
-
- pa_sink_input *i;
- uint32_t idx;
- pa_cvolume max_volume, sink_volume;
-
- pa_assert(dest_volume);
- pa_assert(dest_virtual_volume);
- pa_assert(dest_channel_map);
- pa_assert(sink);
-
- if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
- return;
-
- pa_log_debug("Sink input volume changed");
-
- max_volume = *dest_virtual_volume;
- pa_cvolume_remap(&max_volume, dest_channel_map, &sink->channel_map);
-
- for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
- /* skip this sink-input if we are processing a volume change request */
- if (this && this == i)
- continue;
-
- if (pa_cvolume_max(&i->virtual_volume) > pa_cvolume_max(&max_volume)) {
- max_volume = i->virtual_volume;
- pa_cvolume_remap(&max_volume, &i->channel_map, &sink->channel_map);
- }
- }
-
- /* Set the master volume, and normalize inputs */
- if (!pa_cvolume_equal(&max_volume, &sink->volume)) {
-
- pa_sink_set_volume(sink, &max_volume);
-
- pa_log_debug("sink = %.2f (changed)", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
-
- /* Now, normalize each of the internal volume (client sink-input volume / sink master volume) */
- for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &idx))) {
- /* skip this sink-input if we are processing a volume change request */
- if (this && this == i)
- continue;
-
- sink_volume = max_volume;
- pa_cvolume_remap(&sink_volume, &sink->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(&i->volume, &i->virtual_volume, &sink_volume);
- pa_log_debug("sink input { id = %d, flat = %.2f, true = %.2f }",
- i->index,
- (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
- (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
- pa_asyncmsgq_post(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_VOLUME, pa_xnewdup(struct pa_cvolume, &i->volume, 1), 0, NULL, pa_xfree);
- }
- } else
- pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(&sink->volume)/PA_VOLUME_NORM);
-
- /* and this one */
-
- sink_volume = max_volume;
- pa_cvolume_remap(&sink_volume, &sink->channel_map, dest_channel_map);
- pa_sw_cvolume_divide(dest_volume, dest_virtual_volume, &sink_volume);
- pa_log_debug("caller sink input: { id = %d, flat = %.2f, true = %.2f }",
- this ? (int)this->index : -1,
- (double)pa_cvolume_avg(dest_virtual_volume)/PA_VOLUME_NORM,
- (double)pa_cvolume_avg(dest_volume)/PA_VOLUME_NORM);
-}
-
-static pa_hook_result_t sink_input_set_volume_hook_callback(pa_core *c, pa_sink_input_set_volume_data *this, struct userdata *u) {
- pa_assert(this);
- pa_assert(this->sink_input);
-
- process_input_volume_change(&this->volume, &this->virtual_volume, &this->sink_input->channel_map,
- this->sink_input, this->sink_input->sink);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *this, struct userdata *u) {
- pa_assert(this);
- pa_assert(this->sink);
-
- process_input_volume_change(&this->volume, &this->virtual_volume, &this->channel_map,
- NULL, this->sink);
-
- return PA_HOOK_OK;
-}
-
-static void subscribe_callback(pa_core *core, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
- struct userdata *u = userdata;
- pa_sink *sink;
- pa_sink_input *i;
- uint32_t iidx;
- pa_cvolume sink_volume;
-
- pa_assert(core);
- pa_assert(u);
-
- if (t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_NEW) &&
- t != (PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
-
- if (!(sink = pa_idxset_get_by_index(core->sinks, idx)))
- return;
-
- if (!(sink->flags & PA_SINK_DECIBEL_VOLUME))
- return;
-
- pa_log_debug("Sink volume changed");
- pa_log_debug("sink = %.2f", (double)pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)) / PA_VOLUME_NORM);
-
- sink_volume = *pa_sink_get_volume(sink, FALSE);
-
- for (i = PA_SINK_INPUT(pa_idxset_first(sink->inputs, &iidx)); i; i = PA_SINK_INPUT(pa_idxset_next(sink->inputs, &iidx))) {
- pa_cvolume si_volume;
-
- si_volume = sink_volume;
- pa_cvolume_remap(&si_volume, &sink->channel_map, &i->channel_map);
- pa_sw_cvolume_multiply(&i->virtual_volume, &i->volume, &si_volume);
- pa_log_debug("sink input = { id = %d, flat = %.2f, true = %.2f }",
- i->index,
- (double)pa_cvolume_avg(&i->virtual_volume)/PA_VOLUME_NORM,
- (double)pa_cvolume_avg(&i->volume)/PA_VOLUME_NORM);
- pa_subscription_post(i->sink->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
- }
-}
-
-int pa__init(pa_module*m) {
- struct userdata *u;
-
- pa_assert(m);
-
- u = pa_xnew(struct userdata, 1);
- m->userdata = u;
-
- u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
- u->sink_input_set_volume_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], PA_HOOK_LATE, (pa_hook_cb_t) sink_input_set_volume_hook_callback, u);
-
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK, subscribe_callback, u);
-
- return 0;
-}
-
-void pa__done(pa_module*m) {
- struct userdata* u;
-
- pa_assert(m);
-
- if (!(u = m->userdata))
- return;
-
- if (u->subscription)
- pa_subscription_free(u->subscription);
-
- if (u->sink_input_set_volume_hook_slot)
- pa_hook_slot_free(u->sink_input_set_volume_hook_slot);
- if (u->sink_input_fixate_hook_slot)
- pa_hook_slot_free(u->sink_input_fixate_hook_slot);
-
- pa_xfree(u);
-}
diff --git a/src/modules/module-hal-detect.c b/src/modules/module-hal-detect.c
index 8c1ab329..d3b351a4 100644
--- a/src/modules/module-hal-detect.c
+++ b/src/modules/module-hal-detect.c
@@ -66,9 +66,9 @@ PA_MODULE_USAGE("api=<oss>");
#endif
struct device {
- uint32_t index;
- char *udi;
- char *sink_name, *source_name;
+ char *udi, *originating_udi;
+ char *card_name, *sink_name, *source_name;
+ uint32_t module;
pa_bool_t acl_race_fix;
};
@@ -76,18 +76,13 @@ struct userdata {
pa_core *core;
LibHalContext *context;
pa_dbus_connection *connection;
- pa_hashmap *devices;
+ pa_hashmap *devices; /* Every entry is indexed twice in this table: by the udi we found the device with and by the originating device's udi */
const char *capability;
#ifdef HAVE_ALSA
pa_bool_t use_tsched;
#endif
};
-struct timerdata {
- struct userdata *u;
- char *udi;
-};
-
#define CAPABILITY_ALSA "alsa"
#define CAPABILITY_OSS "oss"
@@ -99,22 +94,22 @@ static const char* const valid_modargs[] = {
NULL
};
-static void hal_device_free(struct device* d) {
+static void device_free(struct device* d) {
pa_assert(d);
pa_xfree(d->udi);
+ pa_xfree(d->originating_udi);
pa_xfree(d->sink_name);
pa_xfree(d->source_name);
+ pa_xfree(d->card_name);
pa_xfree(d);
}
-static void hal_device_free_cb(void *d, void *data) {
- hal_device_free(d);
-}
-
static const char *strip_udi(const char *udi) {
const char *slash;
+ pa_assert(udi);
+
if ((slash = strrchr(udi, '/')))
return slash+1;
@@ -123,405 +118,380 @@ static const char *strip_udi(const char *udi) {
#ifdef HAVE_ALSA
-typedef enum {
- ALSA_TYPE_SINK,
- ALSA_TYPE_SOURCE,
- ALSA_TYPE_OTHER,
- ALSA_TYPE_MAX
-} alsa_type_t;
+enum alsa_type {
+ ALSA_TYPE_PLAYBACK,
+ ALSA_TYPE_CAPTURE,
+ ALSA_TYPE_OTHER
+};
-static alsa_type_t hal_alsa_device_get_type(LibHalContext *context, const char *udi, DBusError *error) {
+static enum alsa_type hal_alsa_device_get_type(LibHalContext *context, const char *udi) {
char *type;
- alsa_type_t t;
+ enum alsa_type t = ALSA_TYPE_OTHER;
+ DBusError error;
+
+ dbus_error_init(&error);
- if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", error)))
- return ALSA_TYPE_OTHER;
+ pa_assert(context);
+ pa_assert(udi);
- if (!strcmp(type, "playback"))
- t = ALSA_TYPE_SINK;
- else if (!strcmp(type, "capture"))
- t = ALSA_TYPE_SOURCE;
- else
- t = ALSA_TYPE_OTHER;
+ if (!(type = libhal_device_get_property_string(context, udi, "alsa.type", &error)))
+ goto finish;
+
+ if (pa_streq(type, "playback"))
+ t = ALSA_TYPE_PLAYBACK;
+ else if (pa_streq(type, "capture"))
+ t = ALSA_TYPE_CAPTURE;
libhal_free_string(type);
+finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
+
return t;
}
-static int hal_alsa_device_is_modem(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_alsa_device_is_modem(LibHalContext *context, const char *udi) {
char *class;
- int r;
+ pa_bool_t r = FALSE;
+ DBusError error;
- if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", error)))
- return 0;
+ dbus_error_init(&error);
+
+ pa_assert(context);
+ pa_assert(udi);
+
+ if (!(class = libhal_device_get_property_string(context, udi, "alsa.pcm_class", &error)))
+ goto finish;
+
+ r = pa_streq(class, "modem");
+ libhal_free_string(class);
- r = strcmp(class, "modem") == 0;
- pa_xfree(class);
+finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
return r;
}
-static pa_module* hal_device_load_alsa(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
- char *args;
- alsa_type_t type;
+static int hal_device_load_alsa(struct userdata *u, const char *udi, struct device *d) {
+ enum alsa_type type;
int device, card;
- const char *module_name;
DBusError error;
pa_module *m;
+ char *args, *originating_udi = NULL, *card_name = NULL;
dbus_error_init(&error);
pa_assert(u);
- pa_assert(sink_name);
- pa_assert(source_name);
+ pa_assert(udi);
+ pa_assert(d);
- *sink_name = *source_name = NULL;
+ /* We only care for PCM devices */
+ type = hal_alsa_device_get_type(u->context, udi);
+ if (type == ALSA_TYPE_OTHER)
+ goto fail;
- type = hal_alsa_device_get_type(u->context, udi, &error);
- if (dbus_error_is_set(&error) || type == ALSA_TYPE_OTHER)
+ /* We don't care for modems */
+ if (hal_alsa_device_is_modem(u->context, udi))
goto fail;
+ /* We only care for the main device */
device = libhal_device_get_property_int(u->context, udi, "alsa.device", &error);
if (dbus_error_is_set(&error) || device != 0)
goto fail;
- card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
- if (dbus_error_is_set(&error))
+ /* We store only one entry per card, hence we look for the originating device */
+ originating_udi = libhal_device_get_property_string(u->context, udi, "alsa.originating_device", &error);
+ if (dbus_error_is_set(&error) || !originating_udi)
goto fail;
- if (hal_alsa_device_is_modem(u->context, udi, &error))
+ /* Make sure we only load one module per card */
+ if (pa_hashmap_get(u->devices, originating_udi))
goto fail;
- if (type == ALSA_TYPE_SINK) {
- *sink_name = pa_sprintf_malloc("alsa_output.%s", strip_udi(udi));
-
- module_name = "module-alsa-sink";
- args = pa_sprintf_malloc("device_id=%u sink_name=%s tsched=%i", card, *sink_name, (int) u->use_tsched);
- } else {
- *source_name = pa_sprintf_malloc("alsa_input.%s", strip_udi(udi));
-
- module_name = "module-alsa-source";
- args = pa_sprintf_malloc("device_id=%u source_name=%s tsched=%i", card, *source_name, (int) u->use_tsched);
- }
-
- pa_log_debug("Loading %s with arguments '%s'", module_name, args);
+ /* We need the identifier */
+ card = libhal_device_get_property_int(u->context, udi, "alsa.card", &error);
+ if (dbus_error_is_set(&error))
+ goto fail;
- m = pa_module_load(u->core, module_name, args);
+ card_name = pa_sprintf_malloc("alsa_card.%s", strip_udi(originating_udi));
+ args = pa_sprintf_malloc("device_id=%u name=%s card_name=%s tsched=%i", card, strip_udi(originating_udi), card_name, (int) u->use_tsched);
+ pa_log_debug("Loading module-alsa-card with arguments '%s'", args);
+ m = pa_module_load(u->core, "module-alsa-card", args);
pa_xfree(args);
- if (!m) {
- pa_xfree(*sink_name);
- pa_xfree(*source_name);
- *sink_name = *source_name = NULL;
- }
+ if (!m)
+ goto fail;
+
+ d->originating_udi = originating_udi;
+ d->module = m->index;
+ d->card_name = card_name;
- return m;
+ return 0;
fail:
if (dbus_error_is_set(&error)) {
- pa_log_error("D-Bus error while parsing ALSA data: %s: %s", error.name, error.message);
+ pa_log_error("D-Bus error while parsing HAL ALSA data: %s: %s", error.name, error.message);
dbus_error_free(&error);
}
- return NULL;
+ pa_xfree(originating_udi);
+ pa_xfree(card_name);
+
+ return -1;
}
#endif
#ifdef HAVE_OSS
-static int hal_oss_device_is_pcm(LibHalContext *context, const char *udi, DBusError *error) {
+static pa_bool_t hal_oss_device_is_pcm(LibHalContext *context, const char *udi) {
char *class = NULL, *dev = NULL, *e;
int device;
- int r = 0;
+ pa_bool_t r = FALSE;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ pa_assert(context);
+ pa_assert(udi);
- class = libhal_device_get_property_string(context, udi, "oss.type", error);
- if (dbus_error_is_set(error) || !class)
+ /* We only care for PCM devices */
+ class = libhal_device_get_property_string(context, udi, "oss.type", &error);
+ if (dbus_error_is_set(&error) || !class)
goto finish;
- if (strcmp(class, "pcm"))
+ if (!pa_streq(class, "pcm"))
goto finish;
- dev = libhal_device_get_property_string(context, udi, "oss.device_file", error);
- if (dbus_error_is_set(error) || !dev)
+ /* We don't like /dev/audio */
+ dev = libhal_device_get_property_string(context, udi, "oss.device_file", &error);
+ if (dbus_error_is_set(&error) || !dev)
goto finish;
if ((e = strrchr(dev, '/')))
if (pa_startswith(e + 1, "audio"))
goto finish;
- device = libhal_device_get_property_int(context, udi, "oss.device", error);
- if (dbus_error_is_set(error) || device != 0)
+ /* We only care for the main device */
+ device = libhal_device_get_property_int(context, udi, "oss.device", &error);
+ if (dbus_error_is_set(&error) || device != 0)
goto finish;
- r = 1;
+ r = TRUE;
finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL OSS data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
+ }
+
libhal_free_string(class);
libhal_free_string(dev);
return r;
}
-static pa_module* hal_device_load_oss(struct userdata *u, const char *udi, char **sink_name, char **source_name) {
- char* args;
- char* device;
+static int hal_device_load_oss(struct userdata *u, const char *udi, struct device *d) {
DBusError error;
pa_module *m;
+ char *args, *originating_udi = NULL, *device, *sink_name = NULL, *source_name = NULL;
dbus_error_init(&error);
pa_assert(u);
- pa_assert(sink_name);
- pa_assert(source_name);
+ pa_assert(udi);
+ pa_assert(d);
+
+ /* We only care for OSS PCM devices */
+ if (!hal_oss_device_is_pcm(u->context, udi))
+ goto fail;
- *sink_name = *source_name = NULL;
+ /* We store only one entry per card, hence we look for the originating device */
+ originating_udi = libhal_device_get_property_string(u->context, udi, "oss.originating_device", &error);
+ if (dbus_error_is_set(&error) || !originating_udi)
+ goto fail;
- if (!hal_oss_device_is_pcm(u->context, udi, &error) || dbus_error_is_set(&error))
+ /* Make sure we only load one module per card */
+ if (pa_hashmap_get(u->devices, originating_udi))
goto fail;
+ /* We need the device file */
device = libhal_device_get_property_string(u->context, udi, "oss.device_file", &error);
if (!device || dbus_error_is_set(&error))
goto fail;
- *sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
- *source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+ sink_name = pa_sprintf_malloc("oss_output.%s", strip_udi(udi));
+ source_name = pa_sprintf_malloc("oss_input.%s", strip_udi(udi));
+ args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, sink_name, source_name);
- args = pa_sprintf_malloc("device=%s sink_name=%s source_name=%s", device, *sink_name, *source_name);
libhal_free_string(device);
pa_log_debug("Loading module-oss with arguments '%s'", args);
m = pa_module_load(u->core, "module-oss", args);
pa_xfree(args);
- if (!m) {
- pa_xfree(*sink_name);
- pa_xfree(*source_name);
- *sink_name = *source_name = NULL;
- }
+ if (!m)
+ goto fail;
+
+ d->originating_udi = originating_udi;
+ d->module = m->index;
+ d->sink_name = sink_name;
+ d->source_name = source_name;
- return m;
+ return 0;
fail:
if (dbus_error_is_set(&error)) {
- pa_log_error("D-Bus error while parsing OSS data: %s: %s", error.name, error.message);
+ pa_log_error("D-Bus error while parsing OSS HAL data: %s: %s", error.name, error.message);
dbus_error_free(&error);
}
- return NULL;
+ pa_xfree(originating_udi);
+ pa_xfree(source_name);
+ pa_xfree(sink_name);
+
+ return -1;
}
#endif
static struct device* hal_device_add(struct userdata *u, const char *udi) {
- pa_module* m = NULL;
struct device *d;
- char *sink_name = NULL, *source_name = NULL;
+ int r;
pa_assert(u);
pa_assert(u->capability);
- pa_assert(!pa_hashmap_get(u->devices, udi));
+
+ d = pa_xnew(struct device, 1);
+ d->acl_race_fix = FALSE;
+ d->udi = pa_xstrdup(udi);
+ d->originating_udi = NULL;
+ d->module = PA_INVALID_INDEX;
+ d->sink_name = d->source_name = d->card_name = NULL;
#ifdef HAVE_ALSA
- if (strcmp(u->capability, CAPABILITY_ALSA) == 0)
- m = hal_device_load_alsa(u, udi, &sink_name, &source_name);
+ if (pa_streq(u->capability, CAPABILITY_ALSA))
+ r = hal_device_load_alsa(u, udi, d);
#endif
#ifdef HAVE_OSS
- if (strcmp(u->capability, CAPABILITY_OSS) == 0)
- m = hal_device_load_oss(u, udi, &sink_name, &source_name);
+ if (pa_streq(u->capability, CAPABILITY_OSS))
+ r = hal_device_load_oss(u, udi, d);
#endif
- if (!m)
+ if (r < 0) {
+ device_free(d);
return NULL;
+ }
- d = pa_xnew(struct device, 1);
- d->acl_race_fix = FALSE;
- d->udi = pa_xstrdup(udi);
- d->index = m->index;
- d->sink_name = sink_name;
- d->source_name = source_name;
pa_hashmap_put(u->devices, d->udi, d);
+ pa_hashmap_put(u->devices, d->originating_udi, d);
return d;
}
-static int hal_device_add_all(struct userdata *u, const char *capability) {
- DBusError error;
- int i, n, count = 0;
+static int hal_device_add_all(struct userdata *u) {
+ int n, count = 0;
char** udis;
-
- pa_assert(u);
+ DBusError error;
dbus_error_init(&error);
- if (u->capability && strcmp(u->capability, capability) != 0)
- return 0;
-
- pa_log_info("Trying capability %s", capability);
+ pa_assert(u);
- udis = libhal_find_device_by_capability(u->context, capability, &n, &error);
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error finding devices: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return -1;
- }
+ udis = libhal_find_device_by_capability(u->context, u->capability, &n, &error);
+ if (dbus_error_is_set(&error) || !udis)
+ goto fail;
if (n > 0) {
- u->capability = capability;
+ int i;
for (i = 0; i < n; i++) {
struct device *d;
- if (!(d = hal_device_add(u, udis[i])))
- pa_log_debug("Not loaded device %s", udis[i]);
- else {
- if (d->sink_name)
- pa_scache_play_item_by_name(u->core, "pulse-coldplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
+ if ((d = hal_device_add(u, udis[i])))
count++;
- }
+ else
+ pa_log_debug("Not loaded device %s", udis[i]);
}
}
libhal_free_string_array(udis);
- return count;
-}
-
-static dbus_bool_t device_has_capability(LibHalContext *context, const char *udi, const char* cap, DBusError *error){
- dbus_bool_t has_prop;
-
- has_prop = libhal_device_property_exists(context, udi, "info.capabilities", error);
- if (!has_prop || dbus_error_is_set(error))
- return FALSE;
- return libhal_device_query_capability(context, udi, cap, error);
-}
-
-static void device_added_time_cb(pa_mainloop_api *ea, pa_time_event *ev, const struct timeval *tv, void *userdata) {
- DBusError error;
- struct timerdata *td = userdata;
-
- dbus_error_init(&error);
-
- if (!pa_hashmap_get(td->u->devices, td->udi)) {
- dbus_bool_t b;
- struct device *d;
+ return count;
- b = libhal_device_exists(td->u->context, td->udi, &error);
-
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error adding device: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- } else if (b) {
- if (!(d = hal_device_add(td->u, td->udi)))
- pa_log_debug("Not loaded device %s", td->udi);
- else {
- if (d->sink_name)
- pa_scache_play_item_by_name(td->u->core, "pulse-hotplug", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
- }
- }
+fail:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
}
- pa_xfree(td->udi);
- pa_xfree(td);
- ea->time_free(ev);
+ return -1;
}
static void device_added_cb(LibHalContext *context, const char *udi) {
DBusError error;
- struct timeval tv;
- struct timerdata *t;
struct userdata *u;
pa_bool_t good = FALSE;
- pa_assert_se(u = libhal_ctx_get_user_data(context));
-
- if (pa_hashmap_get(u->devices, udi))
- return;
-
- pa_log_debug("HAL Device added: %s", udi);
-
dbus_error_init(&error);
- if (u->capability) {
-
- good = device_has_capability(context, udi, u->capability, &error);
-
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error getting capability: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return;
- }
-
- } else {
-
-#ifdef HAVE_ALSA
- good = device_has_capability(context, udi, CAPABILITY_ALSA, &error);
-
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error getting capability: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return;
- }
+ pa_assert(context);
+ pa_assert(udi);
- if (good)
- u->capability = CAPABILITY_ALSA;
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
- if (!good) {
-#endif
-#ifdef HAS_OSS
- good = device_has_capability(context, udi, CAPABILITY_OSS, &error);
+ pa_assert_se(u = libhal_ctx_get_user_data(context));
- if (dbus_error_is_set(&error)) {
- pa_log_error("Error getting capability: %s: %s", error.name, error.message);
- dbus_error_free(&error);
- return;
- }
+ good = libhal_device_query_capability(context, udi, u->capability, &error);
+ if (dbus_error_is_set(&error) || !good)
+ goto finish;
- if (good)
- u->capability = CAPABILITY_OSS;
+ if (!hal_device_add(u, udi))
+ pa_log_debug("Not loaded device %s", udi);
-#endif
-#if defined(HAVE_OSS) && defined(HAVE_ALSA)
- }
-#endif
+finish:
+ if (dbus_error_is_set(&error)) {
+ pa_log_error("D-Bus error while parsing HAL data: %s: %s", error.name, error.message);
+ dbus_error_free(&error);
}
-
- if (!good)
- return;
-
- /* actually add the device 1/2 second later */
- t = pa_xnew(struct timerdata, 1);
- t->u = u;
- t->udi = pa_xstrdup(udi);
-
- pa_gettimeofday(&tv);
- pa_timeval_add(&tv, 500000);
- u->core->mainloop->time_new(u->core->mainloop, &tv, device_added_time_cb, t);
}
static void device_removed_cb(LibHalContext* context, const char *udi) {
struct device *d;
struct userdata *u;
+ pa_assert(context);
+ pa_assert(udi);
+
pa_assert_se(u = libhal_ctx_get_user_data(context));
- pa_log_debug("Device removed: %s", udi);
+ if (!(d = pa_hashmap_get(u->devices, udi)))
+ return;
+
+ pa_hashmap_remove(u->devices, d->originating_udi);
+ pa_hashmap_remove(u->devices, d->udi);
- if ((d = pa_hashmap_remove(u->devices, udi))) {
- pa_module_unload_request_by_index(u->core, d->index, TRUE);
- hal_device_free(d);
- }
+ pa_log_debug("Removing HAL device: %s", d->originating_udi);
+
+ pa_module_unload_request_by_index(u->core, d->module, TRUE);
+ device_free(d);
}
static void new_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
struct userdata *u;
+ pa_assert(context);
+ pa_assert(udi);
+ pa_assert(capability);
+
pa_assert_se(u = libhal_ctx_get_user_data(context));
- if (!u->capability || strcmp(u->capability, capability) == 0)
+ if (pa_streq(u->capability, capability))
/* capability we care about, pretend it's a new device */
device_added_cb(context, udi);
}
@@ -529,20 +499,24 @@ static void new_capability_cb(LibHalContext *context, const char *udi, const cha
static void lost_capability_cb(LibHalContext *context, const char *udi, const char* capability) {
struct userdata *u;
+ pa_assert(context);
+ pa_assert(udi);
+ pa_assert(capability);
+
pa_assert_se(u = libhal_ctx_get_user_data(context));
- if (u->capability && strcmp(u->capability, capability) == 0)
+ if (pa_streq(u->capability, capability))
/* capability we care about, pretend it was removed */
device_removed_cb(context, udi);
}
static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, void *userdata) {
- struct userdata*u = userdata;
+ struct userdata*u;
DBusError error;
pa_assert(bus);
pa_assert(message);
- pa_assert(u);
+ pa_assert_se(u = userdata);
dbus_error_init(&error);
@@ -554,13 +528,14 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
if (dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLAdded") ||
dbus_message_is_signal(message, "org.freedesktop.Hal.Device.AccessControl", "ACLRemoved")) {
uint32_t uid;
- int suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
+ pa_bool_t suspend = strcmp(dbus_message_get_member(message), "ACLRemoved") == 0;
if (!dbus_message_get_args(message, &error, DBUS_TYPE_UINT32, &uid, DBUS_TYPE_INVALID) || dbus_error_is_set(&error)) {
pa_log_error("Failed to parse ACL message: %s: %s", error.name, error.message);
goto finish;
}
+ /* Check if this is about us? */
if (uid == getuid() || uid == geteuid()) {
struct device *d;
const char *udi;
@@ -569,46 +544,44 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
if ((d = pa_hashmap_get(u->devices, udi))) {
pa_bool_t send_acl_race_fix_message = FALSE;
-
d->acl_race_fix = FALSE;
if (d->sink_name) {
pa_sink *sink;
- if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
- int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
-
- if (prev_suspended && !suspend) {
- /* resume */
- if (pa_sink_suspend(sink, 0) >= 0)
- pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
- else
- d->acl_race_fix = TRUE;
-
- } else if (!prev_suspended && suspend) {
- /* suspend */
- if (pa_sink_suspend(sink, 1) >= 0)
- send_acl_race_fix_message = TRUE;
- }
+ if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK))) {
+ pa_bool_t success = pa_sink_suspend(sink, suspend) >= 0;
+
+ if (!success && !suspend)
+ d->acl_race_fix = TRUE; /* resume failed, let's try again */
+ else if (suspend)
+ send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
}
}
if (d->source_name) {
pa_source *source;
- if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
- int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+ if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE))) {
+ pa_bool_t success = pa_source_suspend(source, suspend) >= 0;
- if (prev_suspended && !suspend) {
- /* resume */
- if (pa_source_suspend(source, 0) < 0)
- d->acl_race_fix = TRUE;
+ if (!success && !suspend)
+ d->acl_race_fix = TRUE; /* resume failed, let's try again */
+ else if (suspend)
+ send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
+ }
+ }
- } else if (!prev_suspended && suspend) {
- /* suspend */
- if (pa_source_suspend(source, 0) >= 0)
- send_acl_race_fix_message = TRUE;
- }
+ if (d->card_name) {
+ pa_card *card;
+
+ if ((card = pa_namereg_get(u->core, d->card_name, PA_NAMEREG_CARD))) {
+ pa_bool_t success = pa_card_suspend(card, suspend) >= 0;
+
+ if (!success && !suspend)
+ d->acl_race_fix = TRUE; /* resume failed, let's try again */
+ else if (suspend)
+ send_acl_race_fix_message = TRUE; /* suspend finished, let's tell everyone to try again */
}
}
@@ -621,6 +594,7 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
} else if (!suspend)
device_added_cb(u->context, udi);
+
}
return DBUS_HANDLER_RESULT_HANDLED;
@@ -631,40 +605,36 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo
sever has closed the device. We can remove this as
soon as HAL learns frevoke() */
- const char *udi;
struct device *d;
+ const char *udi;
udi = dbus_message_get_path(message);
- if ((d = pa_hashmap_get(u->devices, udi)) && d->acl_race_fix) {
- pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
-
- d->acl_race_fix = FALSE;
+ if ((d = pa_hashmap_get(u->devices, udi))) {
- if (d->sink_name) {
- pa_sink *sink;
-
- if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK, 0))) {
+ if (d->acl_race_fix) {
+ d->acl_race_fix = FALSE;
+ pa_log_debug("Got dirty give up message for '%s', trying resume ...", udi);
- int prev_suspended = pa_sink_get_state(sink) == PA_SINK_SUSPENDED;
+ if (d->sink_name) {
+ pa_sink *sink;
- if (prev_suspended) {
- /* resume */
- if (pa_sink_suspend(sink, 0) >= 0)
- pa_scache_play_item_by_name(u->core, "pulse-access", d->sink_name, FALSE, PA_VOLUME_NORM, NULL, NULL);
- }
+ if ((sink = pa_namereg_get(u->core, d->sink_name, PA_NAMEREG_SINK)))
+ pa_sink_suspend(sink, FALSE);
}
- }
- if (d->source_name) {
- pa_source *source;
+ if (d->source_name) {
+ pa_source *source;
- if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE, 0))) {
+ if ((source = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_SOURCE)))
+ pa_source_suspend(source, FALSE);
+ }
- int prev_suspended = pa_source_get_state(source) == PA_SOURCE_SUSPENDED;
+ if (d->card_name) {
+ pa_card *card;
- if (prev_suspended)
- pa_source_suspend(source, 0);
+ if ((card = pa_namereg_get(u->core, d->source_name, PA_NAMEREG_CARD)))
+ pa_card_suspend(card, FALSE);
}
}
@@ -692,18 +662,20 @@ static void hal_context_free(LibHalContext* hal_context) {
dbus_error_free(&error);
}
-static LibHalContext* hal_context_new(pa_core* c, DBusConnection *conn) {
+static LibHalContext* hal_context_new(DBusConnection *connection) {
DBusError error;
LibHalContext *hal_context = NULL;
dbus_error_init(&error);
+ pa_assert(connection);
+
if (!(hal_context = libhal_ctx_new())) {
pa_log_error("libhal_ctx_new() failed");
goto fail;
}
- if (!libhal_ctx_set_dbus_connection(hal_context, conn)) {
+ if (!libhal_ctx_set_dbus_connection(hal_context, connection)) {
pa_log_error("Error establishing DBUS connection: %s: %s", error.name, error.message);
goto fail;
}
@@ -726,13 +698,10 @@ fail:
int pa__init(pa_module*m) {
DBusError error;
- pa_dbus_connection *conn;
struct userdata *u = NULL;
- LibHalContext *hal_context = NULL;
int n = 0;
pa_modargs *ma;
const char *api;
- pa_bool_t use_tsched = TRUE;
pa_assert(m);
@@ -743,90 +712,74 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) {
+ m->userdata = u = pa_xnew(struct userdata, 1);
+ u->core = m->core;
+ u->context = NULL;
+ u->connection = NULL;
+ u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
+ u->capability = NULL;
+
+#ifdef HAVE_ALSA
+ u->use_tsched = TRUE;
+
+ if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) {
pa_log("Failed to parse tsched argument.");
goto fail;
}
- if ((api = pa_modargs_get_value(ma, "api", NULL))) {
- pa_bool_t good = FALSE;
+ api = pa_modargs_get_value(ma, "api", "alsa");
-#ifdef HAVE_ALSA
- if (strcmp(api, CAPABILITY_ALSA) == 0) {
- good = TRUE;
- api = CAPABILITY_ALSA;
- }
+ if (pa_streq(api, "alsa"))
+ u->capability = CAPABILITY_ALSA;
+#else
+ api = pa_modargs_get_value(ma, "api", "oss");
#endif
+
#ifdef HAVE_OSS
- if (strcmp(api, CAPABILITY_OSS) == 0) {
- good = TRUE;
- api = CAPABILITY_OSS;
- }
+ if (pa_streq(api, "oss"))
+ u->capability = CAPABILITY_OSS;
#endif
- if (!good) {
- pa_log_error("Invalid API specification.");
- goto fail;
- }
+ if (!u->capability) {
+ pa_log_error("Invalid API specification.");
+ goto fail;
}
- if (!(conn = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
- if (conn)
- pa_dbus_connection_unref(conn);
+ if (!(u->connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &error)) || dbus_error_is_set(&error)) {
pa_log_error("Unable to contact DBUS system bus: %s: %s", error.name, error.message);
goto fail;
}
- if (!(hal_context = hal_context_new(m->core, pa_dbus_connection_get(conn)))) {
+ if (!(u->context = hal_context_new(pa_dbus_connection_get(u->connection)))) {
/* pa_hal_context_new() logs appropriate errors */
- pa_dbus_connection_unref(conn);
goto fail;
}
- u = pa_xnew(struct userdata, 1);
- u->core = m->core;
- u->context = hal_context;
- u->connection = conn;
- u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- u->capability = api;
-#ifdef HAVE_ALSA
- u->use_tsched = use_tsched;
-#endif
- m->userdata = u;
-
-#ifdef HAVE_ALSA
- n = hal_device_add_all(u, CAPABILITY_ALSA);
-#endif
-#if defined(HAVE_ALSA) && defined(HAVE_OSS)
- if (n <= 0)
-#endif
-#ifdef HAVE_OSS
- n += hal_device_add_all(u, CAPABILITY_OSS);
-#endif
+ n = hal_device_add_all(u);
- libhal_ctx_set_user_data(hal_context, u);
- libhal_ctx_set_device_added(hal_context, device_added_cb);
- libhal_ctx_set_device_removed(hal_context, device_removed_cb);
- libhal_ctx_set_device_new_capability(hal_context, new_capability_cb);
- libhal_ctx_set_device_lost_capability(hal_context, lost_capability_cb);
+ libhal_ctx_set_user_data(u->context, u);
+ libhal_ctx_set_device_added(u->context, device_added_cb);
+ libhal_ctx_set_device_removed(u->context, device_removed_cb);
+ libhal_ctx_set_device_new_capability(u->context, new_capability_cb);
+ libhal_ctx_set_device_lost_capability(u->context, lost_capability_cb);
- if (!libhal_device_property_watch_all(hal_context, &error)) {
+ if (!libhal_device_property_watch_all(u->context, &error)) {
pa_log_error("Error monitoring device list: %s: %s", error.name, error.message);
goto fail;
}
- if (!dbus_connection_add_filter(pa_dbus_connection_get(conn), filter_cb, u, NULL)) {
+ if (!dbus_connection_add_filter(pa_dbus_connection_get(u->connection), filter_cb, u, NULL)) {
pa_log_error("Failed to add filter function");
goto fail;
}
- dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
+ dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',sender='org.freedesktop.Hal', interface='org.freedesktop.Hal.Device.AccessControl'", &error);
if (dbus_error_is_set(&error)) {
pa_log_error("Unable to subscribe to HAL ACL signals: %s: %s", error.name, error.message);
goto fail;
}
- dbus_bus_add_match(pa_dbus_connection_get(conn), "type='signal',interface='org.pulseaudio.Server'", &error);
+ dbus_bus_add_match(pa_dbus_connection_get(u->connection), "type='signal',interface='org.pulseaudio.Server'", &error);
if (dbus_error_is_set(&error)) {
pa_log_error("Unable to subscribe to PulseAudio signals: %s: %s", error.name, error.message);
goto fail;
@@ -848,7 +801,6 @@ fail:
return -1;
}
-
void pa__done(pa_module *m) {
struct userdata *u;
@@ -860,8 +812,17 @@ void pa__done(pa_module *m) {
if (u->context)
hal_context_free(u->context);
- if (u->devices)
- pa_hashmap_free(u->devices, hal_device_free_cb, NULL);
+ if (u->devices) {
+ struct device *d;
+
+ while ((d = pa_hashmap_first(u->devices))) {
+ pa_hashmap_remove(u->devices, d->udi);
+ pa_hashmap_remove(u->devices, d->originating_udi);
+ device_free(d);
+ }
+
+ pa_hashmap_free(u->devices, NULL, NULL);
+ }
if (u->connection) {
DBusError error;
diff --git a/src/modules/module-jack-sink.c b/src/modules/module-jack-sink.c
index 555cb825..b448e84e 100644
--- a/src/modules/module-jack-sink.c
+++ b/src/modules/module-jack-sink.c
@@ -430,6 +430,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-jack-source.c b/src/modules/module-jack-source.c
index 9eccbbfa..0c7ee535 100644
--- a/src/modules/module-jack-source.c
+++ b/src/modules/module-jack-source.c
@@ -398,6 +398,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_source_linked_by(u->source);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c
index a27ed712..e746f342 100644
--- a/src/modules/module-ladspa-sink.c
+++ b/src/modules/module-ladspa-sink.c
@@ -144,7 +144,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_assert_se(u = s->userdata);
/* Just hand this one over to the master sink */
- pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE);
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes + pa_memblockq_get_length(u->memblockq), TRUE, FALSE, FALSE);
}
/* Called from I/O thread context */
@@ -355,7 +355,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT) {
pa_log_debug("Requesting rewind due to state change.");
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
}
@@ -396,7 +396,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+ if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
pa_log("Master sink not found");
goto fail;
}
@@ -770,6 +770,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
unsigned c;
diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c
index 97e97dc7..9a782cac 100644
--- a/src/modules/module-lirc.c
+++ b/src/modules/module-lirc.c
@@ -118,7 +118,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
else {
pa_sink *s;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK)))
pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
@@ -135,7 +135,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_NORM;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case DOWN:
@@ -146,7 +146,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MUTED;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case MUTE:
diff --git a/src/modules/module-match.c b/src/modules/module-match.c
index 769a6b59..cbf62687 100644
--- a/src/modules/module-match.c
+++ b/src/modules/module-match.c
@@ -48,7 +48,8 @@ PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Playback stream expression matching module");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE("table=<filename>");
+PA_MODULE_USAGE("table=<filename> "
+ "key=<property_key>");
#define WHITESPACE "\n\r \t"
@@ -57,17 +58,20 @@ PA_MODULE_USAGE("table=<filename>");
static const char* const valid_modargs[] = {
"table",
+ "key",
NULL,
};
struct rule {
regex_t regex;
pa_volume_t volume;
+ pa_proplist *proplist;
struct rule *next;
};
struct userdata {
struct rule *rules;
+ char *property_key;
pa_subscription *subscription;
};
@@ -95,11 +99,12 @@ static int load_rules(struct userdata *u, const char *filename) {
while (!feof(f)) {
char *d, *v;
- pa_volume_t volume;
+ pa_volume_t volume = PA_VOLUME_NORM;
uint32_t k;
regex_t regex;
char ln[256];
struct rule *rule;
+ pa_proplist *proplist = NULL;
if (!fgets(ln, sizeof(ln), f))
break;
@@ -121,14 +126,33 @@ static int load_rules(struct userdata *u, const char *filename) {
}
*d = 0;
- if (pa_atou(v, &k) < 0) {
- pa_log("[%s:%u] failed to parse volume", filename, n);
- goto finish;
+ if (pa_atou(v, &k) >= 0) {
+ volume = (pa_volume_t) k;
+ } else if (*v == '"') {
+ char *e;
+
+ e = strchr(v+1, '"');
+ if (!e) {
+ pa_log(__FILE__ ": [%s:%u] failed to parse line - missing role closing quote", filename, n);
+ goto finish;
+ }
+
+ *e = '\0';
+ e = pa_sprintf_malloc("media.role=\"%s\"", v+1);
+ proplist = pa_proplist_from_string(e);
+ pa_xfree(e);
+ } else {
+ char *e;
+
+ e = v+strspn(v, WHITESPACE);
+ if (!*e) {
+ pa_log(__FILE__ ": [%s:%u] failed to parse line - missing end of property list", filename, n);
+ goto finish;
+ }
+ *e = '\0';
+ proplist = pa_proplist_from_string(v);
}
- volume = (pa_volume_t) k;
-
-
if (regcomp(&regex, ln, REG_EXTENDED|REG_NOSUB) != 0) {
pa_log("[%s:%u] invalid regular expression", filename, n);
goto finish;
@@ -136,6 +160,7 @@ static int load_rules(struct userdata *u, const char *filename) {
rule = pa_xnew(struct rule, 1);
rule->regex = regex;
+ rule->proplist = proplist;
rule->volume = volume;
rule->next = NULL;
@@ -177,15 +202,22 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v
if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
return;
- if (!(n = pa_proplist_gets(si->proplist, PA_PROP_MEDIA_NAME)))
+ if (!(n = pa_proplist_gets(si->proplist, u->property_key)))
return;
+ pa_log_debug("Matching with %s", n);
+
for (r = u->rules; r; r = r->next) {
if (!regexec(&r->regex, n, 0, NULL, 0)) {
- pa_cvolume cv;
- pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
- pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
- pa_sink_input_set_volume(si, &cv);
+ if (r->proplist) {
+ pa_log_debug("updating proplist of sink input '%s'", n);
+ pa_proplist_update(si->proplist, PA_UPDATE_MERGE, r->proplist);
+ } else {
+ pa_cvolume cv;
+ pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);
+ pa_cvolume_set(&cv, si->sample_spec.channels, r->volume);
+ pa_sink_input_set_volume(si, &cv, TRUE);
+ }
}
}
}
@@ -201,11 +233,14 @@ int pa__init(pa_module*m) {
goto fail;
}
+
u = pa_xnew(struct userdata, 1);
u->rules = NULL;
u->subscription = NULL;
m->userdata = u;
+ u->property_key = pa_xstrdup(pa_modargs_get_value(ma, "key", PA_PROP_MEDIA_NAME));
+
if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0)
goto fail;
@@ -234,10 +269,15 @@ void pa__done(pa_module*m) {
if (u->subscription)
pa_subscription_free(u->subscription);
+ if (u->property_key)
+ pa_xfree(u->property_key);
+
for (r = u->rules; r; r = n) {
n = r->next;
regfree(&r->regex);
+ if (r->proplist)
+ pa_proplist_free(r->proplist);
pa_xfree(r);
}
diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c
index 21f176a4..a379923a 100644
--- a/src/modules/module-mmkbd-evdev.c
+++ b/src/modules/module-mmkbd-evdev.c
@@ -109,7 +109,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
if (volchange != INVALID) {
pa_sink *s;
- if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1)))
+ if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK)))
pa_log("Failed to get sink '%s'", u->sink_name);
else {
int i;
@@ -126,7 +126,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_NORM;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case DOWN:
@@ -137,7 +137,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event
cv.values[i] = PA_VOLUME_MUTED;
}
- pa_sink_set_volume(s, &cv);
+ pa_sink_set_volume(s, &cv, TRUE, TRUE);
break;
case MUTE_TOGGLE:
diff --git a/src/modules/module-null-sink.c b/src/modules/module-null-sink.c
index daf9767c..570f8be4 100644
--- a/src/modules/module-null-sink.c
+++ b/src/modules/module-null-sink.c
@@ -324,6 +324,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-pipe-sink.c b/src/modules/module-pipe-sink.c
index 2b55c823..03e27170 100644
--- a/src/modules/module-pipe-sink.c
+++ b/src/modules/module-pipe-sink.c
@@ -315,6 +315,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-pipe-source.c b/src/modules/module-pipe-source.c
index 77310cab..975090c2 100644
--- a/src/modules/module-pipe-source.c
+++ b/src/modules/module-pipe-source.c
@@ -302,6 +302,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_source_linked_by(u->source);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-position-event-sounds.c b/src/modules/module-position-event-sounds.c
index 90e693a3..8e4f4c32 100644
--- a/src/modules/module-position-event-sounds.c
+++ b/src/modules/module-position-event-sounds.c
@@ -58,30 +58,9 @@ struct userdata {
pa_hook_slot *sink_input_fixate_hook_slot;
};
-static pa_bool_t is_left(pa_channel_position_t p) {
- return
- p == PA_CHANNEL_POSITION_FRONT_LEFT ||
- p == PA_CHANNEL_POSITION_REAR_LEFT ||
- p == PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER ||
- p == PA_CHANNEL_POSITION_SIDE_LEFT ||
- p == PA_CHANNEL_POSITION_TOP_FRONT_LEFT ||
- p == PA_CHANNEL_POSITION_TOP_REAR_LEFT;
-}
-
-static pa_bool_t is_right(pa_channel_position_t p) {
- return
- p == PA_CHANNEL_POSITION_FRONT_RIGHT ||
- p == PA_CHANNEL_POSITION_REAR_RIGHT||
- p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER ||
- p == PA_CHANNEL_POSITION_SIDE_RIGHT ||
- p == PA_CHANNEL_POSITION_TOP_FRONT_RIGHT ||
- p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
-}
-
static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_input_new_data *data, struct userdata *u) {
const char *hpos;
double f;
- unsigned c;
char t[PA_CVOLUME_SNPRINT_MAX];
pa_assert(data);
@@ -101,23 +80,16 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *core, pa_sink_i
pa_log_debug("Positioning event sound '%s' at %0.2f.", pa_strnull(pa_proplist_gets(data->proplist, PA_PROP_EVENT_ID)), f);
- if (!data->volume_is_set) {
- pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- data->volume_is_set = TRUE;
+ if (!data->virtual_volume_is_set) {
+ pa_cvolume_reset(&data->virtual_volume, data->sample_spec.channels);
+ data->virtual_volume_is_set = TRUE;
+ data->virtual_volume_is_absolute = FALSE;
}
- for (c = 0; c < data->sample_spec.channels; c++) {
-
- if (is_left(data->channel_map.map[c]))
- data->volume.values[c] =
- pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * (1.0 - f)));
-
- if (is_right(data->channel_map.map[c]))
- data->volume.values[c] =
- pa_sw_volume_multiply(data->volume.values[c], (pa_volume_t) (PA_VOLUME_NORM * f));
- }
+ pa_cvolume_set_balance(&data->virtual_volume, &data->channel_map, f*2.0-1.0);
+ data->save_volume = FALSE;
- pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->volume));
+ pa_log_debug("Final volume %s.", pa_cvolume_snprint(t, sizeof(t), &data->virtual_volume));
return PA_HOOK_OK;
}
diff --git a/src/modules/module-raop-sink.c b/src/modules/module-raop-sink.c
index 62f0a73c..74ee6122 100644
--- a/src/modules/module-raop-sink.c
+++ b/src/modules/module-raop-sink.c
@@ -195,6 +195,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -207,7 +208,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
w = pa_bytes_to_usec((u->offset - u->encoding_overhead + (u->encoded_memchunk.length / u->encoding_ratio)), &u->sink->sample_spec);
*((pa_usec_t*) data) = w > r ? w - r : 0;
- break;
+ return 0;
}
case SINK_MESSAGE_PASS_SOCKET: {
@@ -254,20 +255,17 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
return pa_sink_process_msg(o, code, data, offset, chunk);
}
-static int sink_get_volume_cb(pa_sink *s) {
+static void sink_get_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int i;
pa_assert(u);
- for (i = 0; i < s->sample_spec.channels; i++) {
- s->volume.values[i] = u->volume;
- }
-
- return 0;
+ for (i = 0; i < s->sample_spec.channels; i++)
+ s->virtual_volume.values[i] = u->volume;
}
-static int sink_set_volume_cb(pa_sink *s) {
+static void sink_set_volume_cb(pa_sink *s) {
struct userdata *u = s->userdata;
int rv;
@@ -275,39 +273,34 @@ static int sink_set_volume_cb(pa_sink *s) {
/* If we're muted, we fake it */
if (u->muted)
- return 0;
+ return;
pa_assert(s->sample_spec.channels > 0);
/* Avoid pointless volume sets */
- if (u->volume == s->volume.values[0])
- return 0;
+ if (u->volume == s->virtual_volume.values[0])
+ return;
- rv = pa_raop_client_set_volume(u->raop, s->volume.values[0]);
+ rv = pa_raop_client_set_volume(u->raop, s->virtual_volume.values[0]);
if (0 == rv)
- u->volume = s->volume.values[0];
-
- return rv;
+ u->volume = s->virtual_volume.values[0];
}
-static int sink_get_mute_cb(pa_sink *s) {
+static void sink_get_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
pa_assert(u);
s->muted = u->muted;
- return 0;
}
-static int sink_set_mute_cb(pa_sink *s) {
+static void sink_set_mute_cb(pa_sink *s) {
struct userdata *u = s->userdata;
- int rv;
pa_assert(u);
- rv = pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
+ pa_raop_client_set_volume(u->raop, (s->muted ? PA_VOLUME_MUTED : u->volume));
u->muted = s->muted;
- return rv;
}
static void thread_func(void *userdata) {
@@ -627,6 +620,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
pa_assert(m);
diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c
index 976a8ce5..e17fef03 100644
--- a/src/modules/module-remap-sink.c
+++ b/src/modules/module-remap-sink.c
@@ -119,7 +119,7 @@ static void sink_request_rewind(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
- pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE);
+ pa_sink_input_request_rewind(u->sink_input, s->thread_info.rewind_nbytes, TRUE, FALSE, FALSE);
}
/* Called from I/O thread context */
@@ -270,7 +270,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT) {
pa_log_debug("Requesting rewind due to state change.");
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
}
@@ -302,7 +302,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK, 1))) {
+ if (!(master = pa_namereg_get(m->core, pa_modargs_get_value(ma, "master", NULL), PA_NAMEREG_SINK))) {
pa_log("Master sink not found");
goto fail;
}
@@ -415,6 +415,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_sink_linked_by(u->sink);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-rescue-streams.c b/src/modules/module-rescue-streams.c
index cc6717cb..e52e39c1 100644
--- a/src/modules/module-rescue-streams.c
+++ b/src/modules/module-rescue-streams.c
@@ -54,12 +54,16 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
pa_assert(c);
pa_assert(sink);
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
if (!pa_idxset_size(sink->inputs)) {
pa_log_debug("No sink inputs to move away.");
return PA_HOOK_OK;
}
- if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK, 0)) || target == sink) {
+ if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SINK)) || target == sink) {
uint32_t idx;
for (target = pa_idxset_first(c->sinks, &idx); target; target = pa_idxset_next(c->sinks, &idx))
@@ -73,7 +77,7 @@ static pa_hook_result_t sink_hook_callback(pa_core *c, pa_sink *sink, void* user
}
while ((i = pa_idxset_first(sink->inputs, NULL))) {
- if (pa_sink_input_move_to(i, target) < 0) {
+ if (pa_sink_input_move_to(i, target, FALSE) < 0) {
pa_log_warn("Failed to move sink input %u \"%s\" to %s.", i->index, pa_proplist_gets(i->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
@@ -92,12 +96,16 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
pa_assert(c);
pa_assert(source);
+ /* There's no point in doing anything if the core is shut down anyway */
+ if (c->state == PA_CORE_SHUTDOWN)
+ return PA_HOOK_OK;
+
if (!pa_idxset_size(source->outputs)) {
pa_log_debug("No source outputs to move away.");
return PA_HOOK_OK;
}
- if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE, 0)) || target == source) {
+ if (!(target = pa_namereg_get(c, NULL, PA_NAMEREG_SOURCE)) || target == source) {
uint32_t idx;
for (target = pa_idxset_first(c->sources, &idx); target; target = pa_idxset_next(c->sources, &idx))
@@ -113,7 +121,7 @@ static pa_hook_result_t source_hook_callback(pa_core *c, pa_source *source, void
pa_assert(target != source);
while ((o = pa_idxset_first(source->outputs, NULL))) {
- if (pa_source_output_move_to(o, target) < 0) {
+ if (pa_source_output_move_to(o, target, FALSE) < 0) {
pa_log_warn("Failed to move source output %u \"%s\" to %s.", o->index, pa_proplist_gets(o->proplist, PA_PROP_APPLICATION_NAME), target->name);
return PA_HOOK_OK;
}
diff --git a/src/modules/module-sine-source.c b/src/modules/module-sine-source.c
index be95cc39..5626c2ab 100644
--- a/src/modules/module-sine-source.c
+++ b/src/modules/module-sine-source.c
@@ -96,9 +96,9 @@ static int source_process_msg(
switch (code) {
- case PA_SINK_MESSAGE_SET_STATE:
+ case PA_SOURCE_MESSAGE_SET_STATE:
- if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING)
+ if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
u->timestamp = pa_rtclock_usec();
break;
@@ -201,15 +201,6 @@ finish:
pa_log_debug("Thread shutting down");
}
-static void calc_sine(float *f, size_t l, double freq) {
- size_t i;
-
- l /= sizeof(float);
-
- for (i = 0; i < l; i++)
- *(f++) = (float) 0.5f * sin((double) i*M_PI*2*freq / (double) l);
-}
-
int pa__init(pa_module*m) {
struct userdata *u;
pa_modargs *ma;
@@ -295,6 +286,15 @@ fail:
return -1;
}
+int pa__get_n_used(pa_module *m) {
+ struct userdata *u;
+
+ pa_assert(m);
+ pa_assert_se(u = m->userdata);
+
+ return pa_source_linked_by(u->source);
+}
+
void pa__done(pa_module*m) {
struct userdata *u;
diff --git a/src/modules/module-sine.c b/src/modules/module-sine.c
index 3b0dc352..b0782c3c 100644
--- a/src/modules/module-sine.c
+++ b/src/modules/module-sine.c
@@ -115,7 +115,7 @@ static void sink_input_state_change_cb(pa_sink_input *i, pa_sink_input_state_t s
* we are heard right-away. */
if (PA_SINK_INPUT_IS_LINKED(state) &&
i->thread_info.state == PA_SINK_INPUT_INIT)
- pa_sink_input_request_rewind(i, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(i, 0, FALSE, TRUE, TRUE);
}
int pa__init(pa_module*m) {
@@ -131,7 +131,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK, TRUE))) {
+ if (!(sink = pa_namereg_get(m->core, pa_modargs_get_value(ma, "sink", NULL), PA_NAMEREG_SINK))) {
pa_log("No such sink.");
goto fail;
}
diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c
index fdf69a20..0c9bd4f9 100644
--- a/src/modules/module-stream-restore.c
+++ b/src/modules/module-stream-restore.c
@@ -56,6 +56,10 @@ PA_MODULE_AUTHOR("Lennart Poettering");
PA_MODULE_DESCRIPTION("Automatically restore the volume/mute/device state of streams");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
+PA_MODULE_USAGE(
+ "restore_device=<Save/restore sinks/sources?> "
+ "restore_volume=<Save/restore volumes?> "
+ "restore_muted=<Save/restore muted states?>");
#define SAVE_INTERVAL 10
@@ -87,10 +91,12 @@ struct userdata {
};
struct entry {
- char device[PA_NAME_MAX];
pa_channel_map channel_map;
- pa_cvolume volume;
+ char device[PA_NAME_MAX];
+ pa_cvolume relative_volume;
+ pa_cvolume absolute_volume;
pa_bool_t muted:1;
+ pa_bool_t device_valid:1, relative_volume_valid:1, absolute_volume_valid:1, muted_valid:1;
};
@@ -137,14 +143,14 @@ static char *get_name(pa_proplist *p, const char *prefix) {
return pa_sprintf_malloc("%s-fallback:%s", prefix, r);
}
-static struct entry* read_entry(struct userdata *u, char *name) {
+static struct entry* read_entry(struct userdata *u, const char *name) {
datum key, data;
struct entry *e;
pa_assert(u);
pa_assert(name);
- key.dptr = name;
+ key.dptr = (char*) name;
key.dsize = (int) strlen(name);
data = gdbm_fetch(u->gdbm_file, key);
@@ -153,7 +159,9 @@ static struct entry* read_entry(struct userdata *u, char *name) {
goto fail;
if (data.dsize != sizeof(struct entry)) {
- pa_log_warn("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
+ /* This is probably just a database upgrade, hence let's not
+ * consider this more than a debug message */
+ pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu", name, (unsigned long) data.dsize, (unsigned long) sizeof(struct entry));
goto fail;
}
@@ -164,18 +172,19 @@ static struct entry* read_entry(struct userdata *u, char *name) {
goto fail;
}
- if (!(pa_cvolume_valid(&e->volume))) {
- pa_log_warn("Invalid volume stored in database for stream %s", name);
+ if (!(pa_channel_map_valid(&e->channel_map))) {
+ pa_log_warn("Invalid channel map stored in database for stream %s", name);
goto fail;
}
- if (!(pa_channel_map_valid(&e->channel_map))) {
- pa_log_warn("Invalid channel map stored in database for stream %s", name);
+ if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
+ pa_log_warn("Invalid device name stored in database for stream %s", name);
goto fail;
}
- if (e->volume.channels != e->channel_map.channels) {
- pa_log_warn("Volume and channel map don't match in database entry for stream %s", name);
+ if ((e->relative_volume_valid && (!pa_cvolume_valid(&e->relative_volume) || e->relative_volume.channels != e->channel_map.channels)) ||
+ (e->absolute_volume_valid && (!pa_cvolume_valid(&e->absolute_volume) || e->absolute_volume.channels != e->channel_map.channels))) {
+ pa_log_warn("Invalid volume stored in database for stream %s", name);
goto fail;
}
@@ -213,6 +222,33 @@ static void trigger_save(struct userdata *u) {
u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
}
+static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
+ pa_cvolume t;
+
+ pa_assert(a);
+ pa_assert(b);
+
+ if (a->device_valid != b->device_valid ||
+ (a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
+ return FALSE;
+
+ if (a->muted_valid != b->muted_valid ||
+ (a->muted && (a->muted != b->muted)))
+ return FALSE;
+
+ t = b->relative_volume;
+ if (a->relative_volume_valid != b->relative_volume_valid ||
+ (a->relative_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->relative_volume)))
+ return FALSE;
+
+ t = b->absolute_volume;
+ if (a->absolute_volume_valid != b->absolute_volume_valid ||
+ (a->absolute_volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->absolute_volume)))
+ return FALSE;
+
+ return TRUE;
+}
+
static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
struct userdata *u = userdata;
struct entry entry, *old;
@@ -240,9 +276,23 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
return;
entry.channel_map = sink_input->channel_map;
- entry.volume = *pa_sink_input_get_volume(sink_input);
+
+ if (sink_input->sink->flags & PA_SINK_FLAT_VOLUME) {
+ entry.absolute_volume = *pa_sink_input_get_volume(sink_input);
+ entry.absolute_volume_valid = sink_input->save_volume;
+
+ pa_sw_cvolume_divide(&entry.relative_volume, &entry.absolute_volume, pa_sink_get_volume(sink_input->sink, FALSE));
+ entry.relative_volume_valid = sink_input->save_volume;
+ } else {
+ entry.relative_volume = *pa_sink_input_get_volume(sink_input);
+ entry.relative_volume_valid = sink_input->save_volume;
+ }
+
entry.muted = pa_sink_input_get_mute(sink_input);
+ entry.muted_valid = sink_input->save_muted;
+
pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
+ entry.device_valid = sink_input->save_sink;
} else {
pa_source_output *source_output;
@@ -255,19 +305,15 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3
if (!(name = get_name(source_output->proplist, "source-output")))
return;
- /* The following fields are filled in to make the entry valid
- * according to read_entry(). They are otherwise useless */
entry.channel_map = source_output->channel_map;
- pa_cvolume_reset(&entry.volume, entry.channel_map.channels);
+
pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
+ entry.device_valid = source_output->save_source;
}
if ((old = read_entry(u, name))) {
- if (pa_cvolume_equal(pa_cvolume_remap(&old->volume, &old->channel_map, &entry.channel_map), &entry.volume) &&
- !old->muted == !entry.muted &&
- strcmp(old->device, entry.device) == 0) {
-
+ if (entries_equal(old, &entry)) {
pa_xfree(old);
pa_xfree(name);
return;
@@ -297,20 +343,25 @@ static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_n
pa_assert(new_data);
+ if (!u->restore_device)
+ return PA_HOOK_OK;
+
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
if ((e = read_entry(u, name))) {
pa_sink *s;
- if (u->restore_device &&
- (s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK, TRUE))) {
+ if (e->device_valid) {
- if (!new_data->sink) {
- pa_log_info("Restoring device for stream %s.", name);
- new_data->sink = s;
- } else
- pa_log_info("Not restore device for stream %s, because already set.", name);
+ if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
+ if (!new_data->sink) {
+ pa_log_info("Restoring device for stream %s.", name);
+ new_data->sink = s;
+ new_data->save_sink = TRUE;
+ } else
+ pa_log_info("Not restore device for stream %s, because already set.", name);
+ }
}
pa_xfree(e);
@@ -327,6 +378,9 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
pa_assert(new_data);
+ if (!u->restore_volume && !u->restore_muted)
+ return PA_HOOK_OK;
+
if (!(name = get_name(new_data->proplist, "sink-input")))
return PA_HOOK_OK;
@@ -334,17 +388,43 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu
if (u->restore_volume) {
- if (!new_data->volume_is_set) {
- pa_log_info("Restoring volume for sink input %s.", name);
- pa_sink_input_new_data_set_volume(new_data, pa_cvolume_remap(&e->volume, &e->channel_map, &new_data->channel_map));
+ if (!new_data->virtual_volume_is_set) {
+ pa_cvolume v;
+ pa_cvolume_init(&v);
+
+ if (new_data->sink->flags & PA_SINK_FLAT_VOLUME) {
+
+ if (e->absolute_volume_valid &&
+ e->device_valid &&
+ pa_streq(new_data->sink->name, e->device)) {
+
+ v = e->absolute_volume;
+ new_data->virtual_volume_is_absolute = TRUE;
+ } else if (e->relative_volume_valid) {
+
+ v = e->relative_volume;
+ new_data->virtual_volume_is_absolute = FALSE;
+ }
+
+ } else if (e->relative_volume_valid) {
+ v = e->relative_volume;
+ new_data->virtual_volume_is_absolute = FALSE;
+ }
+
+ if (v.channels > 0) {
+ pa_log_info("Restoring volume for sink input %s.", name);
+ pa_sink_input_new_data_set_virtual_volume(new_data, pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map));
+ new_data->save_volume = TRUE;
+ }
} else
pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
}
- if (u->restore_muted) {
+ if (u->restore_muted && e->muted_valid) {
if (!new_data->muted_is_set) {
pa_log_info("Restoring mute state for sink input %s.", name);
pa_sink_input_new_data_set_muted(new_data, e->muted);
+ new_data->save_muted = TRUE;
} else
pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
}
@@ -363,21 +443,27 @@ static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_ou
pa_assert(new_data);
+ if (!u->restore_device)
+ return PA_HOOK_OK;
+
+ if (new_data->direct_on_input)
+ return PA_HOOK_OK;
+
if (!(name = get_name(new_data->proplist, "source-output")))
return PA_HOOK_OK;
if ((e = read_entry(u, name))) {
pa_source *s;
- if (u->restore_device &&
- !new_data->direct_on_input &&
- (s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE, TRUE))) {
-
- if (!new_data->source) {
- pa_log_info("Restoring device for stream %s.", name);
- new_data->source = s;
- } else
- pa_log_info("Not restoring device for stream %s, because already set", name);
+ if (e->device_valid) {
+ if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
+ if (!new_data->source) {
+ pa_log_info("Restoring device for stream %s.", name);
+ new_data->source = s;
+ new_data->save_source = TRUE;
+ } else
+ pa_log_info("Not restoring device for stream %s, because already set", name);
+ }
}
pa_xfree(e);
@@ -431,21 +517,40 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
}
if (u->restore_volume) {
- pa_cvolume v = e->volume;
- pa_log_info("Restoring volume for sink input %s.", name);
- pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map));
+ pa_cvolume v;
+ pa_cvolume_init(&v);
+
+ if (si->sink->flags & PA_SINK_FLAT_VOLUME) {
+
+ if (e->absolute_volume_valid &&
+ e->device_valid &&
+ pa_streq(e->device, si->sink->name))
+ v = e->absolute_volume;
+ else if (e->relative_volume_valid) {
+ pa_cvolume t = si->sink->virtual_volume;
+ pa_sw_cvolume_multiply(&v, &e->relative_volume, pa_cvolume_remap(&t, &si->sink->channel_map, &e->channel_map));
+ }
+ } else if (e->relative_volume_valid)
+ v = e->relative_volume;
+
+ if (v.channels > 0) {
+ pa_log_info("Restoring volume for sink input %s.", name);
+ pa_sink_input_set_volume(si, pa_cvolume_remap(&v, &e->channel_map, &si->channel_map), TRUE);
+ }
}
- if (u->restore_muted) {
+ if (u->restore_muted &&
+ e->muted_valid) {
pa_log_info("Restoring mute state for sink input %s.", name);
- pa_sink_input_set_mute(si, e->muted);
+ pa_sink_input_set_mute(si, e->muted, TRUE);
}
if (u->restore_device &&
- (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+ e->device_valid &&
+ (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
pa_log_info("Restoring device for stream %s.", name);
- pa_sink_input_move_to(si, s);
+ pa_sink_input_move_to(si, s, TRUE);
}
}
@@ -462,10 +567,11 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
}
if (u->restore_device &&
- (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE, TRUE))) {
+ e->device_valid &&
+ (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
pa_log_info("Restoring device for stream %s.", name);
- pa_source_output_move_to(so, s);
+ pa_source_output_move_to(so, s, TRUE);
}
}
}
@@ -548,11 +654,13 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
pa_xfree(key.dptr);
if ((e = read_entry(u, name))) {
+ pa_cvolume r;
+
pa_tagstruct_puts(reply, name);
pa_tagstruct_put_channel_map(reply, &e->channel_map);
- pa_tagstruct_put_cvolume(reply, &e->volume);
- pa_tagstruct_puts(reply, e->device);
- pa_tagstruct_put_boolean(reply, e->muted);
+ pa_tagstruct_put_cvolume(reply, e->relative_volume_valid ? &e->relative_volume : pa_cvolume_init(&r));
+ pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
+ pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
pa_xfree(e);
}
@@ -592,16 +700,28 @@ static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connectio
if (pa_tagstruct_gets(t, &name) < 0 ||
pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
- pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
+ pa_tagstruct_get_cvolume(t, &entry.relative_volume) < 0 ||
pa_tagstruct_gets(t, &device) < 0 ||
pa_tagstruct_get_boolean(t, &muted) < 0)
goto fail;
- if (entry.channel_map.channels != entry.volume.channels)
+ entry.absolute_volume_valid = FALSE;
+ entry.relative_volume_valid = entry.relative_volume.channels > 0;
+
+ if (entry.relative_volume_valid &&
+ entry.channel_map.channels != entry.relative_volume.channels)
goto fail;
entry.muted = muted;
- pa_strlcpy(entry.device, device, sizeof(entry.device));
+ entry.muted_valid = TRUE;
+
+ if (device)
+ pa_strlcpy(entry.device, device, sizeof(entry.device));
+ entry.device_valid = !!entry.device[0];
+
+ if (entry.device_valid &&
+ !pa_namereg_is_valid_name(entry.device))
+ goto fail;
key.dptr = (void*) name;
key.dsize = (int) strlen(name);
diff --git a/src/modules/module-suspend-on-idle.c b/src/modules/module-suspend-on-idle.c
index 8ab84e08..5e5e53e7 100644
--- a/src/modules/module-suspend-on-idle.c
+++ b/src/modules/module-suspend-on-idle.c
@@ -62,8 +62,10 @@ struct userdata {
*source_output_new_slot,
*sink_input_unlink_slot,
*source_output_unlink_slot,
- *sink_input_move_slot,
- *source_output_move_slot,
+ *sink_input_move_start_slot,
+ *source_output_move_start_slot,
+ *sink_input_move_finish_slot,
+ *source_output_move_finish_slot,
*sink_input_state_changed_slot,
*source_output_state_changed_slot;
};
@@ -181,40 +183,60 @@ static pa_hook_result_t source_output_unlink_hook_cb(pa_core *c, pa_source_outpu
return PA_HOOK_OK;
}
-static pa_hook_result_t sink_input_move_hook_cb(pa_core *c, pa_sink_input_move_hook_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_move_start_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_assert(c);
- pa_assert(data);
+ pa_sink_input_assert_ref(s);
pa_assert(u);
- if ((d = pa_hashmap_get(u->device_infos, data->destination)))
- resume(d);
-
- if (pa_sink_check_suspend(data->sink_input->sink) <= 1)
- if ((d = pa_hashmap_get(u->device_infos, data->sink_input->sink)))
+ if (pa_sink_check_suspend(s->sink) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, s->sink)))
restart(d);
return PA_HOOK_OK;
}
-static pa_hook_result_t source_output_move_hook_cb(pa_core *c, pa_source_output_move_hook_data *data, struct userdata *u) {
+static pa_hook_result_t sink_input_move_finish_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_assert(c);
- pa_assert(data);
+ pa_sink_input_assert_ref(s);
pa_assert(u);
- if ((d = pa_hashmap_get(u->device_infos, data->destination)))
+ if ((d = pa_hashmap_get(u->device_infos, s->sink)))
resume(d);
- if (pa_source_check_suspend(data->source_output->source) <= 1)
- if ((d = pa_hashmap_get(u->device_infos, data->source_output->source)))
+ return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_output_move_start_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_source_output_assert_ref(s);
+ pa_assert(u);
+
+ if (pa_source_check_suspend(s->source) <= 1)
+ if ((d = pa_hashmap_get(u->device_infos, s->source)))
restart(d);
return PA_HOOK_OK;
}
+static pa_hook_result_t source_output_move_finish_hook_cb(pa_core *c, pa_source_output *s, struct userdata *u) {
+ struct device_info *d;
+
+ pa_assert(c);
+ pa_source_output_assert_ref(s);
+ pa_assert(u);
+
+ if ((d = pa_hashmap_get(u->device_infos, s->source)))
+ resume(d);
+
+ return PA_HOOK_OK;
+}
+
static pa_hook_result_t sink_input_state_changed_hook_cb(pa_core *c, pa_sink_input *s, struct userdata *u) {
struct device_info *d;
pa_sink_input_state_t state;
@@ -376,8 +398,10 @@ int pa__init(pa_module*m) {
u->source_output_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_fixate_hook_cb, u);
u->sink_input_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_unlink_hook_cb, u);
u->source_output_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_unlink_hook_cb, u);
- u->sink_input_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_hook_cb, u);
- u->source_output_move_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_hook_cb, u);
+ u->sink_input_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_start_hook_cb, u);
+ u->source_output_move_start_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_start_hook_cb, u);
+ u->sink_input_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_move_finish_hook_cb, u);
+ u->source_output_move_finish_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_move_finish_hook_cb, u);
u->sink_input_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) sink_input_state_changed_hook_cb, u);
u->source_output_state_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) source_output_state_changed_hook_cb, u);
@@ -421,8 +445,10 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->sink_input_new_slot);
if (u->sink_input_unlink_slot)
pa_hook_slot_free(u->sink_input_unlink_slot);
- if (u->sink_input_move_slot)
- pa_hook_slot_free(u->sink_input_move_slot);
+ if (u->sink_input_move_start_slot)
+ pa_hook_slot_free(u->sink_input_move_start_slot);
+ if (u->sink_input_move_finish_slot)
+ pa_hook_slot_free(u->sink_input_move_finish_slot);
if (u->sink_input_state_changed_slot)
pa_hook_slot_free(u->sink_input_state_changed_slot);
@@ -430,8 +456,10 @@ void pa__done(pa_module*m) {
pa_hook_slot_free(u->source_output_new_slot);
if (u->source_output_unlink_slot)
pa_hook_slot_free(u->source_output_unlink_slot);
- if (u->source_output_move_slot)
- pa_hook_slot_free(u->source_output_move_slot);
+ if (u->source_output_move_start_slot)
+ pa_hook_slot_free(u->source_output_move_start_slot);
+ if (u->source_output_move_finish_slot)
+ pa_hook_slot_free(u->source_output_move_finish_slot);
if (u->source_output_state_changed_slot)
pa_hook_slot_free(u->source_output_state_changed_slot);
diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c
index a46d6e59..5c7a6e55 100644
--- a/src/modules/module-tunnel.c
+++ b/src/modules/module-tunnel.c
@@ -494,6 +494,7 @@ static int sink_set_state(pa_sink *s, pa_sink_state_t state) {
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -581,6 +582,7 @@ static int source_set_state(pa_source *s, pa_source_state_t state) {
case PA_SOURCE_UNLINKED:
case PA_SOURCE_INIT:
+ case PA_SINK_INVALID_STATE:
;
}
@@ -1054,10 +1056,10 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag
pa_assert(u->sink);
if ((u->version < 11 || !!mute == !!u->sink->muted) &&
- pa_cvolume_equal(&volume, &u->sink->volume))
+ pa_cvolume_equal(&volume, &u->sink->virtual_volume))
return;
- memcpy(&u->sink->volume, &volume, sizeof(pa_cvolume));
+ memcpy(&u->sink->virtual_volume, &volume, sizeof(pa_cvolume));
if (u->version >= 11)
u->sink->muted = !!mute;
@@ -1619,7 +1621,7 @@ static void on_connection(pa_socket_client *sc, pa_iochannel *io, void *userdata
#ifdef TUNNEL_SINK
/* Called from main context */
-static int sink_set_volume(pa_sink *sink) {
+static void sink_set_volume(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
@@ -1632,14 +1634,12 @@ static int sink_set_volume(pa_sink *sink) {
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);
pa_tagstruct_putu32(t, tag = u->ctag++);
pa_tagstruct_putu32(t, u->device_index);
- pa_tagstruct_put_cvolume(t, &sink->volume);
+ pa_tagstruct_put_cvolume(t, &sink->virtual_volume);
pa_pstream_send_tagstruct(u->pstream, t);
-
- return 0;
}
/* Called from main context */
-static int sink_set_mute(pa_sink *sink) {
+static void sink_set_mute(pa_sink *sink) {
struct userdata *u;
pa_tagstruct *t;
uint32_t tag;
@@ -1649,7 +1649,7 @@ static int sink_set_mute(pa_sink *sink) {
pa_assert(u);
if (u->version < 11)
- return -1;
+ return;
t = pa_tagstruct_new(NULL, 0);
pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_MUTE);
@@ -1657,8 +1657,6 @@ static int sink_set_mute(pa_sink *sink) {
pa_tagstruct_putu32(t, u->device_index);
pa_tagstruct_put_boolean(t, !!sink->muted);
pa_pstream_send_tagstruct(u->pstream, t);
-
- return 0;
}
#endif
diff --git a/src/modules/module-volume-restore.c b/src/modules/module-volume-restore.c
index aac0d046..21c71491 100644
--- a/src/modules/module-volume-restore.c
+++ b/src/modules/module-volume-restore.c
@@ -23,43 +23,19 @@
#include <config.h>
#endif
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
-
#include <pulse/xmalloc.h>
-#include <pulse/volume.h>
-#include <pulse/timeval.h>
-#include <pulsecore/core-error.h>
#include <pulsecore/module.h>
-#include <pulsecore/core-util.h>
#include <pulsecore/modargs.h>
#include <pulsecore/log.h>
-#include <pulsecore/core-subscribe.h>
-#include <pulsecore/sink-input.h>
-#include <pulsecore/source-output.h>
-#include <pulsecore/namereg.h>
+#include <pulsecore/core-util.h>
#include "module-volume-restore-symdef.h"
PA_MODULE_AUTHOR("Lennart Poettering");
-PA_MODULE_DESCRIPTION("Automatically restore the volume and the devices of streams");
+PA_MODULE_DESCRIPTION("Compatibility module");
PA_MODULE_VERSION(PACKAGE_VERSION);
PA_MODULE_LOAD_ONCE(TRUE);
-PA_MODULE_USAGE(
- "table=<filename> "
- "restore_device=<Restore the device for each stream?> "
- "restore_volume=<Restore the volume for each stream?>"
-);
-
-#define WHITESPACE "\n\r \t"
-#define DEFAULT_VOLUME_TABLE_FILE "volume-restore.table"
-#define SAVE_INTERVAL 10
static const char* const valid_modargs[] = {
"table",
@@ -68,413 +44,10 @@ static const char* const valid_modargs[] = {
NULL,
};
-struct rule {
- char* name;
- pa_bool_t volume_is_set;
- pa_cvolume volume;
- char *sink, *source;
-};
-
-struct userdata {
- pa_core *core;
- pa_hashmap *hashmap;
- pa_subscription *subscription;
- pa_hook_slot
- *sink_input_new_hook_slot,
- *sink_input_fixate_hook_slot,
- *source_output_new_hook_slot;
- pa_bool_t modified;
- char *table_file;
- pa_time_event *save_time_event;
-};
-
-static pa_cvolume* parse_volume(const char *s, pa_cvolume *v) {
- char *p;
- long k;
- unsigned i;
-
- pa_assert(s);
- pa_assert(v);
-
- if (!isdigit(*s))
- return NULL;
-
- k = strtol(s, &p, 0);
- if (k <= 0 || k > (long) PA_CHANNELS_MAX)
- return NULL;
-
- v->channels = (uint8_t) k;
-
- for (i = 0; i < v->channels; i++) {
- p += strspn(p, WHITESPACE);
-
- if (!isdigit(*p))
- return NULL;
-
- k = strtol(p, &p, 0);
-
- if (k < (long) PA_VOLUME_MUTED)
- return NULL;
-
- v->values[i] = (pa_volume_t) k;
- }
-
- if (*p != 0)
- return NULL;
-
- return v;
-}
-
-static int load_rules(struct userdata *u) {
- FILE *f;
- int n = 0;
- int ret = -1;
- char buf_name[256], buf_volume[256], buf_sink[256], buf_source[256];
- char *ln = buf_name;
-
- if (!(f = fopen(u->table_file, "r"))) {
- if (errno == ENOENT) {
- pa_log_info("Starting with empty ruleset.");
- ret = 0;
- } else
- pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
-
- goto finish;
- }
-
- pa_lock_fd(fileno(f), 1);
-
- while (!feof(f)) {
- struct rule *rule;
- pa_cvolume v;
- pa_bool_t v_is_set;
-
- if (!fgets(ln, sizeof(buf_name), f))
- break;
-
- n++;
-
- pa_strip_nl(ln);
-
- if (ln[0] == '#')
- continue;
-
- if (ln == buf_name) {
- ln = buf_volume;
- continue;
- }
-
- if (ln == buf_volume) {
- ln = buf_sink;
- continue;
- }
-
- if (ln == buf_sink) {
- ln = buf_source;
- continue;
- }
-
- pa_assert(ln == buf_source);
-
- if (buf_volume[0]) {
- if (!parse_volume(buf_volume, &v)) {
- pa_log("parse failure in %s:%u, stopping parsing", u->table_file, n);
- goto finish;
- }
-
- v_is_set = TRUE;
- } else
- v_is_set = FALSE;
-
- ln = buf_name;
-
- if (pa_hashmap_get(u->hashmap, buf_name)) {
- pa_log("double entry in %s:%u, ignoring", u->table_file, n);
- continue;
- }
-
- rule = pa_xnew(struct rule, 1);
- rule->name = pa_xstrdup(buf_name);
- if ((rule->volume_is_set = v_is_set))
- rule->volume = v;
- rule->sink = buf_sink[0] ? pa_xstrdup(buf_sink) : NULL;
- rule->source = buf_source[0] ? pa_xstrdup(buf_source) : NULL;
-
- pa_hashmap_put(u->hashmap, rule->name, rule);
- }
-
- if (ln != buf_name) {
- pa_log("invalid number of lines in %s.", u->table_file);
- goto finish;
- }
-
- ret = 0;
-
-finish:
- if (f) {
- pa_lock_fd(fileno(f), 0);
- fclose(f);
- }
-
- return ret;
-}
-
-static int save_rules(struct userdata *u) {
- FILE *f;
- int ret = -1;
- void *state = NULL;
- struct rule *rule;
-
- if (!u->modified)
- return 0;
-
- pa_log_info("Saving rules...");
-
- if (!(f = fopen(u->table_file, "w"))) {
- pa_log("Failed to open file '%s': %s", u->table_file, pa_cstrerror(errno));
- goto finish;
- }
-
- pa_lock_fd(fileno(f), 1);
-
- while ((rule = pa_hashmap_iterate(u->hashmap, &state, NULL))) {
- unsigned i;
-
- fprintf(f, "%s\n", rule->name);
-
- if (rule->volume_is_set) {
- fprintf(f, "%u", rule->volume.channels);
-
- for (i = 0; i < rule->volume.channels; i++)
- fprintf(f, " %u", rule->volume.values[i]);
- }
-
- fprintf(f, "\n%s\n%s\n",
- rule->sink ? rule->sink : "",
- rule->source ? rule->source : "");
- }
-
- ret = 0;
- u->modified = FALSE;
- pa_log_debug("Successfully saved rules...");
-
-finish:
- if (f) {
- pa_lock_fd(fileno(f), 0);
- fclose(f);
- }
-
- return ret;
-}
-
-static char* client_name(pa_client *c) {
- char *t, *e;
-
- if (!pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME) || !c->driver)
- return NULL;
-
- t = pa_sprintf_malloc("%s$%s", c->driver, pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME));
- t[strcspn(t, "\n\r#")] = 0;
-
- if (!*t) {
- pa_xfree(t);
- return NULL;
- }
-
- if ((e = strrchr(t, '('))) {
- char *k = e + 1 + strspn(e + 1, "0123456789-");
-
- /* Dirty trick: truncate all trailing parens with numbers in
- * between, since they are usually used to identify multiple
- * sessions of the same application, which is something we
- * explicitly don't want. Besides other stuff this makes xmms
- * with esound work properly for us. */
-
- if (*k == ')' && *(k+1) == 0)
- *e = 0;
- }
-
- return t;
-}
-
-static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *tv, void *userdata) {
- struct userdata *u = userdata;
-
- pa_assert(a);
- pa_assert(e);
- pa_assert(tv);
- pa_assert(u);
-
- pa_assert(e == u->save_time_event);
- u->core->mainloop->time_free(u->save_time_event);
- u->save_time_event = NULL;
-
- save_rules(u);
-}
-
-static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
- struct userdata *u = userdata;
- pa_sink_input *si = NULL;
- pa_source_output *so = NULL;
- struct rule *r;
- char *name;
-
- pa_assert(c);
- pa_assert(u);
-
- if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
- t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
- t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
- t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
- return;
-
- if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
- if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx)))
- return;
-
- if (!si->client || !(name = client_name(si->client)))
- return;
- } else {
- pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
-
- if (!(so = pa_idxset_get_by_index(c->source_outputs, idx)))
- return;
-
- if (!so->client || !(name = client_name(so->client)))
- return;
- }
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
- pa_xfree(name);
-
- if (si) {
-
- if (!r->volume_is_set || !pa_cvolume_equal(pa_sink_input_get_volume(si), &r->volume)) {
- pa_log_info("Saving volume for <%s>", r->name);
- r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = TRUE;
- u->modified = TRUE;
- }
-
- if (!r->sink || strcmp(si->sink->name, r->sink) != 0) {
- pa_log_info("Saving sink for <%s>", r->name);
- pa_xfree(r->sink);
- r->sink = pa_xstrdup(si->sink->name);
- u->modified = TRUE;
- }
- } else {
- pa_assert(so);
-
- if (!r->source || strcmp(so->source->name, r->source) != 0) {
- pa_log_info("Saving source for <%s>", r->name);
- pa_xfree(r->source);
- r->source = pa_xstrdup(so->source->name);
- u->modified = TRUE;
- }
- }
-
- } else {
- pa_log_info("Creating new entry for <%s>", name);
-
- r = pa_xnew(struct rule, 1);
- r->name = name;
-
- if (si) {
- r->volume = *pa_sink_input_get_volume(si);
- r->volume_is_set = TRUE;
- r->sink = pa_xstrdup(si->sink->name);
- r->source = NULL;
- } else {
- pa_assert(so);
- r->volume_is_set = FALSE;
- r->sink = NULL;
- r->source = pa_xstrdup(so->source->name);
- }
-
- pa_hashmap_put(u->hashmap, r->name, r);
- u->modified = TRUE;
- }
-
- if (u->modified && !u->save_time_event) {
- struct timeval tv;
- pa_gettimeofday(&tv);
- tv.tv_sec += SAVE_INTERVAL;
- u->save_time_event = u->core->mainloop->time_new(u->core->mainloop, &tv, save_time_callback, u);
- }
-}
-
-static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
- struct rule *r;
- char *name;
-
- pa_assert(data);
-
- /* In the NEW hook we only adjust the device. Adjusting the volume
- * is left for the FIXATE hook */
-
- if (!data->client || !(name = client_name(data->client)))
- return PA_HOOK_OK;
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
- if (!data->sink && r->sink) {
- if ((data->sink = pa_namereg_get(c, r->sink, PA_NAMEREG_SINK, 1)))
- pa_log_info("Restoring sink for <%s>", r->name);
- }
- }
-
- pa_xfree(name);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *data, struct userdata *u) {
- struct rule *r;
- char *name;
-
- pa_assert(data);
-
- /* In the FIXATE hook we only adjust the volum. Adjusting the device
- * is left for the NEW hook */
-
- if (!data->client || !(name = client_name(data->client)))
- return PA_HOOK_OK;
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
-
- if (r->volume_is_set && data->sample_spec.channels == r->volume.channels) {
- pa_log_info("Restoring volume for <%s>", r->name);
- pa_sink_input_new_data_set_volume(data, &r->volume);
- }
- }
-
- pa_xfree(name);
-
- return PA_HOOK_OK;
-}
-
-static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *data, struct userdata *u) {
- struct rule *r;
- char *name;
-
- pa_assert(data);
-
- if (!data->client || !(name = client_name(data->client)))
- return PA_HOOK_OK;
-
- if ((r = pa_hashmap_get(u->hashmap, name))) {
- if (!data->source && r->source) {
- if ((data->source = pa_namereg_get(c, r->source, PA_NAMEREG_SOURCE, 1)))
- pa_log_info("Restoring source for <%s>", r->name);
- }
- }
-
- return PA_HOOK_OK;
-}
-
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
- struct userdata *u;
pa_bool_t restore_device = TRUE, restore_volume = TRUE;
+ char *t;
pa_assert(m);
@@ -483,90 +56,26 @@ int pa__init(pa_module*m) {
goto fail;
}
- u = pa_xnew(struct userdata, 1);
- u->core = m->core;
- u->hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
- u->modified = FALSE;
- u->subscription = NULL;
- u->sink_input_new_hook_slot = u->sink_input_fixate_hook_slot = u->source_output_new_hook_slot = NULL;
- u->save_time_event = NULL;
-
- m->userdata = u;
-
- if (!(u->table_file = pa_state_path(pa_modargs_get_value(ma, "table", DEFAULT_VOLUME_TABLE_FILE), TRUE)))
- goto fail;
-
if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0) {
pa_log("restore_volume= and restore_device= expect boolean arguments");
goto fail;
}
- if (!(restore_device || restore_volume)) {
- pa_log("Both restrong the volume and restoring the device are disabled. There's no point in using this module at all then, failing.");
- goto fail;
- }
-
- if (load_rules(u) < 0)
- goto fail;
+ pa_log_warn("module-volume-restore is obsolete. It has been replaced by module-stream-restore. We will now load the latter but please make sure to remove module-volume-restore from your configuration.");
- u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
-
- if (restore_device) {
- u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
- u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
- }
+ t = pa_sprintf_malloc("restore_volume=%s restore_device=%s", pa_yes_no(restore_volume), pa_yes_no(restore_device));
+ pa_module_load(m->core, "module-stream-restore", t);
+ pa_xfree(t);
- if (restore_volume)
- u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
+ pa_module_unload_request(m, TRUE);
pa_modargs_free(ma);
return 0;
fail:
- pa__done(m);
if (ma)
pa_modargs_free(ma);
return -1;
}
-
-static void free_func(void *p, void *userdata) {
- struct rule *r = p;
- pa_assert(r);
-
- pa_xfree(r->name);
- pa_xfree(r->sink);
- pa_xfree(r->source);
- pa_xfree(r);
-}
-
-void pa__done(pa_module*m) {
- struct userdata* u;
-
- pa_assert(m);
-
- if (!(u = m->userdata))
- return;
-
- if (u->subscription)
- pa_subscription_free(u->subscription);
-
- if (u->sink_input_new_hook_slot)
- pa_hook_slot_free(u->sink_input_new_hook_slot);
- if (u->sink_input_fixate_hook_slot)
- pa_hook_slot_free(u->sink_input_fixate_hook_slot);
- if (u->source_output_new_hook_slot)
- pa_hook_slot_free(u->source_output_new_hook_slot);
-
- if (u->hashmap) {
- save_rules(u);
- pa_hashmap_free(u->hashmap, free_func, NULL);
- }
-
- if (u->save_time_event)
- u->core->mainloop->time_free(u->save_time_event);
-
- pa_xfree(u->table_file);
- pa_xfree(u);
-}
diff --git a/src/modules/module-x11-bell.c b/src/modules/module-x11-bell.c
index e93721c1..bef02536 100644
--- a/src/modules/module-x11-bell.c
+++ b/src/modules/module-x11-bell.c
@@ -82,7 +82,7 @@ static int x11_event_cb(pa_x11_wrapper *w, XEvent *e, void *userdata) {
bne = (XkbBellNotifyEvent*) e;
- if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, TRUE, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {
+ if (pa_scache_play_item_by_name(u->core, u->scache_item, u->sink_name, ((pa_volume_t) bne->percent*PA_VOLUME_NORM)/100U, NULL, NULL) < 0) {
pa_log_info("Ringing bell failed, reverting to X11 device bell.");
XkbForceDeviceBell(pa_x11_wrapper_get_display(w), bne->device, bne->bell_class, bne->bell_id, bne->percent);
}
diff --git a/src/modules/module-x11-xsmp.c b/src/modules/module-x11-xsmp.c
index 57d182fd..5fc8047d 100644
--- a/src/modules/module-x11-xsmp.c
+++ b/src/modules/module-x11-xsmp.c
@@ -117,13 +117,14 @@ static void new_ice_connection(IceConn connection, IcePointer client_data, Bool
int pa__init(pa_module*m) {
pa_modargs *ma = NULL;
- char t[256], *vendor, *client_id, *k;
+ char t[256], *vendor, *client_id;
SmcCallbacks callbacks;
SmProp prop_program, prop_user;
SmProp *prop_list[2];
SmPropValue val_program, val_user;
struct userdata *u;
const char *e;
+ pa_client_new_data data;
pa_assert(m);
@@ -198,16 +199,22 @@ int pa__init(pa_module*m) {
SmcSetProperties(u->connection, PA_ELEMENTSOF(prop_list), prop_list);
pa_log_info("Connected to session manager '%s' as '%s'.", vendor = SmcVendor(u->connection), client_id);
- k = pa_sprintf_malloc("XSMP Session on %s as %s", vendor, client_id);
- u->client = pa_client_new(u->core, __FILE__, k);
- pa_xfree(k);
- pa_proplist_sets(u->client->proplist, "xsmp.vendor", vendor);
- pa_proplist_sets(u->client->proplist, "xsmp.client.id", client_id);
+ pa_client_new_data_init(&data);
+ data.module = m;
+ data.driver = __FILE__;
+ pa_proplist_setf(data.proplist, PA_PROP_APPLICATION_NAME, "XSMP Session on %s as %s", vendor, client_id);
+ pa_proplist_sets(data.proplist, "xsmp.vendor", vendor);
+ pa_proplist_sets(data.proplist, "xsmp.client.id", client_id);
+ u->client = pa_client_new(u->core, &data);
+ pa_client_new_data_done(&data);
free(vendor);
free(client_id);
+ if (!u->client)
+ goto fail;
+
pa_modargs_free(ma);
return 0;
diff --git a/src/modules/oss/Makefile b/src/modules/oss/Makefile
new file mode 120000
index 00000000..efe5a336
--- /dev/null
+++ b/src/modules/oss/Makefile
@@ -0,0 +1 @@
+../../pulse/Makefile \ No newline at end of file
diff --git a/src/modules/module-oss.c b/src/modules/oss/module-oss.c
index 23a32549..eac0c8e6 100644
--- a/src/modules/module-oss.c
+++ b/src/modules/oss/module-oss.c
@@ -71,6 +71,11 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#include <sys/audioio.h>
+#include <sys/syscall.h>
+#endif
+
#include "oss-util.h"
#include "module-oss-symdef.h"
@@ -121,7 +126,7 @@ struct userdata {
int mixer_fd;
int mixer_devmask;
- int nfrags, frag_size;
+ int nfrags, frag_size, orig_frag_size;
pa_bool_t use_mmap;
unsigned out_mmap_current, in_mmap_current;
@@ -399,13 +404,27 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {
if (u->use_getodelay) {
int arg;
-
+#if defined(__NetBSD__) && !defined(SNDCTL_DSP_GETODELAY)
+#if defined(AUDIO_GETBUFINFO)
+ struct audio_info info;
+ if (syscall(SYS_ioctl, u->fd, AUDIO_GETBUFINFO, &info) < 0) {
+ pa_log_info("Device doesn't support AUDIO_GETBUFINFO: %s", pa_cstrerror(errno));
+ u->use_getodelay = 0;
+ } else {
+ arg = info.play.seek + info.blocksize / 2;
+ r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
+ }
+#else
+ pa_log_info("System doesn't support AUDIO_GETBUFINFO");
+ u->use_getodelay = 0;
+#endif
+#else
if (ioctl(u->fd, SNDCTL_DSP_GETODELAY, &arg) < 0) {
pa_log_info("Device doesn't support SNDCTL_DSP_GETODELAY: %s", pa_cstrerror(errno));
u->use_getodelay = 0;
} else
r = pa_bytes_to_usec((size_t) arg, &u->sink->sample_spec);
-
+#endif
}
if (!u->use_getodelay && u->use_getospace) {
@@ -424,7 +443,6 @@ static pa_usec_t io_sink_get_latency(struct userdata *u) {
return r;
}
-
static pa_usec_t io_source_get_latency(struct userdata *u) {
pa_usec_t r = 0;
@@ -508,9 +526,6 @@ static int suspend(struct userdata *u) {
return 0;
}
-static int sink_get_volume(pa_sink *s);
-static int source_get_volume(pa_source *s);
-
static int unsuspend(struct userdata *u) {
int m;
pa_sample_spec ss, *ss_original;
@@ -536,7 +551,7 @@ static int unsuspend(struct userdata *u) {
}
if (u->nfrags >= 2 && u->frag_size >= 1)
- if (pa_oss_set_fragments(u->fd, u->nfrags, u->frag_size) < 0) {
+ if (pa_oss_set_fragments(u->fd, u->nfrags, u->orig_frag_size) < 0) {
pa_log_warn("Resume failed, couldn't set original fragment settings.");
goto fail;
}
@@ -602,9 +617,9 @@ static int unsuspend(struct userdata *u) {
build_pollfd(u);
if (u->sink)
- sink_get_volume(u->sink);
+ pa_sink_get_volume(u->sink, TRUE);
if (u->source)
- source_get_volume(u->source);
+ pa_source_get_volume(u->source, TRUE);
pa_log_info("Resumed successfully...");
@@ -681,6 +696,7 @@ static int sink_process_msg(pa_msgobject *o, int code, void *data, int64_t offse
break;
+ case PA_SINK_INVALID_STATE:
case PA_SINK_UNLINKED:
case PA_SINK_INIT:
;
@@ -762,6 +778,7 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
case PA_SOURCE_UNLINKED:
case PA_SOURCE_INIT:
+ case PA_SOURCE_INVALID_STATE:
;
}
@@ -777,84 +794,76 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off
return ret;
}
-static int sink_get_volume(pa_sink *s) {
+static void sink_get_volume(pa_sink *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_PCM)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
-static int sink_set_volume(pa_sink *s) {
+static void sink_set_volume(pa_sink *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));
if (u->mixer_devmask & SOUND_MASK_VOLUME)
- if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_PCM)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
-static int source_get_volume(pa_source *s) {
+static void source_get_volume(pa_source *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
-static int source_set_volume(pa_source *s) {
+static void source_set_volume(pa_source *s) {
struct userdata *u;
- int r;
pa_assert_se(u = s->userdata);
pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV));
if (u->mixer_devmask & SOUND_MASK_IGAIN)
- if ((r = pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
if (u->mixer_devmask & SOUND_MASK_RECLEV)
- if ((r = pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->volume)) >= 0)
- return r;
+ if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0)
+ return;
pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno));
- return -1;
}
static void thread_func(void *userdata) {
@@ -877,7 +886,7 @@ static void thread_func(void *userdata) {
/* pa_log("loop"); */
- if (PA_SINK_IS_OPENED(u->sink->thread_info.state))
+ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state))
if (u->sink->thread_info.rewind_requested)
pa_sink_process_rewind(u->sink, 0);
@@ -1144,7 +1153,7 @@ int pa__init(pa_module*m) {
struct userdata *u = NULL;
const char *dev;
int fd = -1;
- int nfrags, frag_size;
+ int nfrags, orig_frag_size, frag_size;
int mode, caps;
pa_bool_t record = TRUE, playback = TRUE, use_mmap = TRUE;
pa_sample_spec ss;
@@ -1201,12 +1210,12 @@ int pa__init(pa_module*m) {
if (use_mmap && (!(caps & DSP_CAP_MMAP) || !(caps & DSP_CAP_TRIGGER))) {
pa_log_info("OSS device not mmap capable, falling back to UNIX read/write mode.");
- use_mmap = 0;
+ use_mmap = FALSE;
}
if (use_mmap && mode == O_WRONLY) {
pa_log_info("Device opened for playback only, cannot do memory mapping, falling back to UNIX write() mode.");
- use_mmap = 0;
+ use_mmap = FALSE;
}
if (pa_oss_get_hw_description(dev, hwdesc, sizeof(hwdesc)) >= 0)
@@ -1216,6 +1225,7 @@ int pa__init(pa_module*m) {
pa_log_info("Device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR"));
+ orig_frag_size = frag_size;
if (nfrags >= 2 && frag_size >= 1)
if (pa_oss_set_fragments(fd, nfrags, frag_size) < 0)
goto fail;
@@ -1235,6 +1245,7 @@ int pa__init(pa_module*m) {
m->userdata = u;
u->fd = fd;
u->mixer_fd = -1;
+ u->mixer_devmask = 0;
u->use_getospace = u->use_getispace = TRUE;
u->use_getodelay = TRUE;
u->mode = mode;
@@ -1242,6 +1253,7 @@ int pa__init(pa_module*m) {
u->device_name = pa_xstrdup(dev);
u->in_nfrags = u->out_nfrags = (uint32_t) (u->nfrags = nfrags);
u->out_fragment_size = u->in_fragment_size = (uint32_t) (u->frag_size = frag_size);
+ u->orig_frag_size = orig_frag_size;
u->use_mmap = use_mmap;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
@@ -1383,7 +1395,6 @@ int pa__init(pa_module*m) {
if ((u->mixer_fd = pa_oss_open_mixer_for_device(u->device_name)) >= 0) {
pa_bool_t do_close = TRUE;
- u->mixer_devmask = 0;
if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0)
pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno));
@@ -1394,6 +1405,7 @@ int pa__init(pa_module*m) {
u->sink->flags |= PA_SINK_HW_VOLUME_CTRL;
u->sink->get_volume = sink_get_volume;
u->sink->set_volume = sink_set_volume;
+ u->sink->n_volume_steps = 101;
do_close = FALSE;
}
@@ -1402,6 +1414,7 @@ int pa__init(pa_module*m) {
u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL;
u->source->get_volume = source_get_volume;
u->source->set_volume = source_set_volume;
+ u->source->n_volume_steps = 101;
do_close = FALSE;
}
}
@@ -1409,6 +1422,7 @@ int pa__init(pa_module*m) {
if (do_close) {
pa_close(u->mixer_fd);
u->mixer_fd = -1;
+ u->mixer_devmask = 0;
}
}
diff --git a/src/modules/oss-util.c b/src/modules/oss/oss-util.c
index f766030d..f04b875d 100644
--- a/src/modules/oss-util.c
+++ b/src/modules/oss/oss-util.c
@@ -45,6 +45,7 @@
int pa_oss_open(const char *device, int *mode, int* pcaps) {
int fd = -1;
int caps;
+ char *t;
pa_assert(device);
pa_assert(mode);
@@ -92,7 +93,8 @@ int pa_oss_open(const char *device, int *mode, int* pcaps) {
success:
- pa_log_debug("capabilities:%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+ t = pa_sprintf_malloc(
+ "%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
*pcaps & DSP_CAP_BATCH ? " BATCH" : "",
#ifdef DSP_CAP_BIND
*pcaps & DSP_CAP_BIND ? " BIND" : "",
@@ -140,6 +142,9 @@ success:
#endif
*pcaps & DSP_CAP_TRIGGER ? " TRIGGER" : "");
+ pa_log_debug("capabilities:%s", t);
+ pa_xfree(t);
+
pa_make_fd_cloexec(fd);
return fd;
@@ -164,6 +169,10 @@ int pa_oss_auto_format(int fd, pa_sample_spec *ss) {
[PA_SAMPLE_FLOAT32BE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_S32LE] = AFMT_QUERY, /* not supported */
[PA_SAMPLE_S32BE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24LE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24BE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24_32LE] = AFMT_QUERY, /* not supported */
+ [PA_SAMPLE_S24_32BE] = AFMT_QUERY, /* not supported */
};
pa_assert(fd >= 0);
@@ -241,6 +250,8 @@ int pa_oss_set_fragments(int fd, int nfrags, int frag_size) {
int arg;
arg = ((int) nfrags << 16) | simple_log2(frag_size);
+ pa_log_debug("Asking for %i fragments of size %i (requested %i)", nfrags, 1 << simple_log2(frag_size), frag_size);
+
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg) < 0) {
pa_log("SNDCTL_DSP_SETFRAGMENT: %s", pa_cstrerror(errno));
return -1;
@@ -298,7 +309,11 @@ static int get_device_number(const char *dev) {
int r;
if (!(p = rp = pa_readlink(dev))) {
+#ifdef ENOLINK
if (errno != EINVAL && errno != ENOLINK) {
+#else
+ if (errno != EINVAL) {
+#endif
r = -1;
goto finish;
}
diff --git a/src/modules/oss-util.h b/src/modules/oss/oss-util.h
index 654f7bba..654f7bba 100644
--- a/src/modules/oss-util.h
+++ b/src/modules/oss/oss-util.h
diff --git a/src/modules/raop/base64.c b/src/modules/raop/base64.c
index 8918def8..059c7028 100644
--- a/src/modules/raop/base64.c
+++ b/src/modules/raop/base64.c
@@ -45,6 +45,7 @@ static int pos(char c)
if (c >= '0' && c <= '9') return c - '0' + 52;
if (c == '+') return 62;
if (c == '/') return 63;
+ return -1;
}
int pa_base64_encode(const void *data, int size, char **str)
@@ -97,8 +98,12 @@ static unsigned int token_decode(const char *token)
marker++;
else if (marker > 0)
return DECODE_ERROR;
- else
- val += pos(token[i]);
+ else {
+ int lpos = pos(token[i]);
+ if (lpos < 0)
+ return DECODE_ERROR;
+ val += lpos;
+ }
}
if (marker > 2)
return DECODE_ERROR;
diff --git a/src/modules/rtp/module-rtp-recv.c b/src/modules/rtp/module-rtp-recv.c
index e35773cc..00d21255 100644
--- a/src/modules/rtp/module-rtp-recv.c
+++ b/src/modules/rtp/module-rtp-recv.c
@@ -313,7 +313,7 @@ static int rtpoll_work_cb(pa_rtpoll_item *i) {
if (pa_memblockq_is_readable(s->memblockq) &&
s->sink_input->thread_info.underrun_for > 0) {
pa_log_debug("Requesting rewind due to end of underrun");
- pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE);
+ pa_sink_input_request_rewind(s->sink_input, 0, FALSE, TRUE, FALSE);
}
return 1;
@@ -415,7 +415,7 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
goto fail;
}
- if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, TRUE))) {
+ if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) {
pa_log("Sink does not exist.");
goto fail;
}
diff --git a/src/modules/rtp/module-rtp-send.c b/src/modules/rtp/module-rtp-send.c
index 9c0f07f1..a6d682bb 100644
--- a/src/modules/rtp/module-rtp-send.c
+++ b/src/modules/rtp/module-rtp-send.c
@@ -196,7 +196,7 @@ int pa__init(pa_module*m) {
goto fail;
}
- if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE, 1))) {
+ if (!(s = pa_namereg_get(m->core, pa_modargs_get_value(ma, "source", NULL), PA_NAMEREG_SOURCE))) {
pa_log("Source does not exist.");
goto fail;
}
diff --git a/src/modules/rtp/rtp.c b/src/modules/rtp/rtp.c
index 88351010..c09c321f 100644
--- a/src/modules/rtp/rtp.c
+++ b/src/modules/rtp/rtp.c
@@ -35,6 +35,10 @@
#include <sys/filio.h>
#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
#include <pulsecore/core-error.h>
#include <pulsecore/log.h>
#include <pulsecore/macro.h>
diff --git a/src/modules/rtp/sap.c b/src/modules/rtp/sap.c
index b0c95aa5..7764f7bd 100644
--- a/src/modules/rtp/sap.c
+++ b/src/modules/rtp/sap.c
@@ -38,6 +38,10 @@
#include <sys/filio.h>
#endif
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
+
#include <pulse/xmalloc.h>
#include <pulsecore/core-error.h>