diff options
Diffstat (limited to 'src/modules')
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(®ex, 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> |