diff options
28 files changed, 213 insertions, 78 deletions
@@ -203,7 +203,13 @@ new flag at end of CREATE_PLAYBACK_STREAM: ## v19, implemented by >= 0.9.22 -New proplist field for sink input, source output introspection opcodes and at the end: +New flag at the end of sink input and source output introspection data: bool corked +## v20, implemented by >= 1.0 + +Two new flags at the end of sink input introspection data: + + bool has_volume + bool read_only_volume diff --git a/configure.ac b/configure.ac index 8b4f5866..21dae308 100644 --- a/configure.ac +++ b/configure.ac @@ -1296,7 +1296,7 @@ AC_SUBST(HAVE_DBUS) AM_CONDITIONAL([HAVE_DBUS], [test "x$HAVE_DBUS" = x1]) -# udev and HAL depend on DBUS: So double check if they were explicitly enabled. +# udev and HAL depend on D-Bus: So double check if they were explicitly enabled. if test "x$HAVE_DBUS" != "x1" ; then HAVE_HAL=0 if test "x${hal}" = xyes ; then @@ -1704,7 +1704,7 @@ echo " Enable Jack: ${ENABLE_JACK} Enable Async DNS: ${ENABLE_LIBASYNCNS} Enable LIRC: ${ENABLE_LIRC} - Enable DBUS: ${ENABLE_DBUS} + Enable D-Bus: ${ENABLE_DBUS} Enable HAL: ${ENABLE_HAL} Enable BlueZ: ${ENABLE_BLUEZ} Enable udev: ${ENABLE_UDEV} @@ -1730,9 +1730,9 @@ echo " if test "${ENABLE_DBUS}" = "no" && test "x$os_is_win32" != "x1" ; then echo " ===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING ===== -You do not have DBUS support enabled. It is strongly recommended -that you enable DBUS support if you platform supports it. -Many parts of PulseAudio use udev, from Console Kit interaction +You do not have D-Bus support enabled. It is strongly recommended +that you enable D-Bus support if your platform supports it. +Many parts of PulseAudio use D-Bus, from ConsoleKit interaction to the Device Reservation Protocol to speak to JACK, Bluetooth support and even a native control protocol for communicating and controling the PulseAudio daemon itself. @@ -1744,7 +1744,7 @@ if test "${ENABLE_UDEV}" = "no" && test "x$os_is_win32" != "x1" ; then echo " ===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING ===== You do not have udev support enabled. It is strongly recommended -that you enable udev support if you platform supports it as it is +that you enable udev support if your platform supports it as it is the primary method used to detect hardware audio devices (on Linux) and is thus a critical part of PulseAudio on that platform. ===== WARNING WARNING WARNING WARNING WARNING WARNING WARNING ===== diff --git a/src/Makefile.am b/src/Makefile.am index d49d91c7..77fc882b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1824,7 +1824,7 @@ BLUETOOTH_SBC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_sbc_la_SOURCES) libbluetooth_ipc_la_SOURCES = modules/bluetooth/ipc.c modules/bluetooth/ipc.h libbluetooth_ipc_la_LDFLAGS = -avoid-version -libbluetooth_ipc_la_LIBADD = $(AM_LIBADD)libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la +libbluetooth_ipc_la_LIBADD = $(AM_LIBADD) libpulsecore-@PA_MAJORMINOR@.la libpulsecommon-@PA_MAJORMINOR@.la libpulse.la libbluetooth_ipc_la_CFLAGS = $(AM_CFLAGS) BLUETOOTH_IPC_FILES = $(subst modules/bluetooth/,,$(libbluetooth_ipc_la_SOURCES)) rtp.h diff --git a/src/modules/coreaudio/module-coreaudio-device.c b/src/modules/coreaudio/module-coreaudio-device.c index 5e6d49c2..cc4600b5 100644 --- a/src/modules/coreaudio/module-coreaudio-device.c +++ b/src/modules/coreaudio/module-coreaudio-device.c @@ -747,7 +747,7 @@ int pa__init(pa_module *m) { ca_device_create_streams(m, TRUE); /* create the message thread */ - if (!(u->thread = pa_thread_new(thread_func, u))) { + if (!(u->thread = pa_thread_new("coreaudio", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } diff --git a/src/modules/dbus/iface-stream.c b/src/modules/dbus/iface-stream.c index 681790b7..a9eb373f 100644 --- a/src/modules/dbus/iface-stream.c +++ b/src/modules/dbus/iface-stream.c @@ -56,6 +56,9 @@ struct pa_dbusiface_stream { dbus_bool_t mute; pa_proplist *proplist; + pa_bool_t has_volume; + pa_bool_t read_only_volume; + pa_dbus_protocol *dbus_protocol; pa_subscription *subscription; pa_hook_slot *send_event_slot; @@ -189,6 +192,14 @@ static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userd pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &idx); } +/* The returned string has to be freed with pa_xfree() by the caller. */ +static char *stream_to_string(pa_dbusiface_stream *s) { + if (s->type == STREAM_TYPE_PLAYBACK) + return pa_sprintf_malloc("Playback stream %u", (unsigned) s->sink_input->index); + else + return pa_sprintf_malloc("Record stream %u", (unsigned) s->source_output->index); +} + static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *userdata) { pa_dbusiface_stream *s = userdata; const char *driver = NULL; @@ -200,12 +211,11 @@ static void handle_get_driver(DBusConnection *conn, DBusMessage *msg, void *user driver = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->driver : s->source_output->driver; if (!driver) { - if (s->type == STREAM_TYPE_PLAYBACK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Playback stream %u doesn't have a driver.", s->sink_input->index); - else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Record stream %u doesn't have a driver.", s->source_output->index); + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have a driver.", str); + pa_xfree(str); + return; } @@ -224,12 +234,11 @@ static void handle_get_owner_module(DBusConnection *conn, DBusMessage *msg, void owner_module = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->module : s->source_output->module; if (!owner_module) { - if (s->type == STREAM_TYPE_PLAYBACK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Playback stream %u doesn't have an owner module.", s->sink_input->index); - else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Record stream %u doesn't have an owner module.", s->source_output->index); + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have an owner module.", str); + pa_xfree(str); + return; } @@ -250,12 +259,11 @@ static void handle_get_client(DBusConnection *conn, DBusMessage *msg, void *user client = (s->type == STREAM_TYPE_PLAYBACK) ? s->sink_input->client : s->source_output->client; if (!client) { - if (s->type == STREAM_TYPE_PLAYBACK) - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Playback stream %u isn't associated to any client.", s->sink_input->index); - else - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, - "Record stream %u isn't associated to any client.", s->source_output->index); + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s isn't associated to any client.", str); + pa_xfree(str); + return; } @@ -332,8 +340,12 @@ static void handle_get_volume(DBusConnection *conn, DBusMessage *msg, void *user pa_assert(msg); pa_assert(s); - if (s->type == STREAM_TYPE_RECORD) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + if (!s->has_volume) { + char *str = stream_to_string(s); + + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str); + pa_xfree(str); + return; } @@ -357,8 +369,15 @@ static void handle_set_volume(DBusConnection *conn, DBusMessage *msg, DBusMessag pa_assert(iter); pa_assert(s); - if (s->type == STREAM_TYPE_RECORD) { - pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "Record streams don't have volume."); + if (!s->has_volume || s->read_only_volume) { + char *str = stream_to_string(s); + + if (!s->has_volume) + pa_dbus_send_error(conn, msg, PA_DBUS_ERROR_NO_SUCH_PROPERTY, "%s doesn't have volume.", str); + else if (s->read_only_volume) + pa_dbus_send_error(conn, msg, DBUS_ERROR_ACCESS_DENIED, "%s has read-only volume.", str); + pa_xfree(str); + return; } @@ -509,6 +528,11 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat pa_assert(msg); pa_assert(s); + if (s->has_volume) { + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; + } + if (s->type == STREAM_TYPE_PLAYBACK) { idx = s->sink_input->index; driver = s->sink_input->driver; @@ -517,8 +541,6 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat device = pa_dbusiface_core_get_sink_path(s->core, s->sink); sample_format = s->sink_input->sample_spec.format; channel_map = &s->sink_input->channel_map; - for (i = 0; i < s->volume.channels; ++i) - volume[i] = s->volume.values[i]; buffer_latency = pa_sink_input_get_latency(s->sink_input, &device_latency); resample_method = pa_resample_method_to_string(s->sink_input->actual_resample_method); } else { @@ -555,11 +577,12 @@ static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdat if (client) pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CLIENT].property_name, DBUS_TYPE_OBJECT_PATH, &client_path); + pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DEVICE].property_name, DBUS_TYPE_OBJECT_PATH, &device); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_FORMAT].property_name, DBUS_TYPE_UINT32, &sample_format); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_SAMPLE_RATE].property_name, DBUS_TYPE_UINT32, &s->sample_rate); pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_CHANNELS].property_name, DBUS_TYPE_UINT32, channels, channel_map->channels); - if (s->type == STREAM_TYPE_PLAYBACK) { + if (s->has_volume) { pa_dbus_append_basic_array_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_VOLUME].property_name, DBUS_TYPE_UINT32, volume, s->volume.channels); pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_MUTE].property_name, DBUS_TYPE_BOOLEAN, &s->mute); } @@ -707,30 +730,33 @@ static void subscription_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t } if (s->type == STREAM_TYPE_PLAYBACK) { - pa_cvolume new_volume; pa_bool_t new_mute = FALSE; - pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); + if (s->has_volume) { + pa_cvolume new_volume; - if (!pa_cvolume_equal(&s->volume, &new_volume)) { - dbus_uint32_t volume[PA_CHANNELS_MAX]; - dbus_uint32_t *volume_ptr = volume; + pa_sink_input_get_volume(s->sink_input, &new_volume, TRUE); - s->volume = new_volume; + if (!pa_cvolume_equal(&s->volume, &new_volume)) { + dbus_uint32_t volume[PA_CHANNELS_MAX]; + dbus_uint32_t *volume_ptr = volume; - for (i = 0; i < s->volume.channels; ++i) - volume[i] = s->volume.values[i]; + s->volume = new_volume; - pa_assert_se(signal_msg = dbus_message_new_signal(s->path, - PA_DBUSIFACE_STREAM_INTERFACE, - signals[SIGNAL_VOLUME_UPDATED].name)); - pa_assert_se(dbus_message_append_args(signal_msg, - DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, - DBUS_TYPE_INVALID)); + for (i = 0; i < s->volume.channels; ++i) + volume[i] = s->volume.values[i]; - pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg); - dbus_message_unref(signal_msg); - signal_msg = NULL; + pa_assert_se(signal_msg = dbus_message_new_signal(s->path, + PA_DBUSIFACE_STREAM_INTERFACE, + signals[SIGNAL_VOLUME_UPDATED].name)); + pa_assert_se(dbus_message_append_args(signal_msg, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &volume_ptr, s->volume.channels, + DBUS_TYPE_INVALID)); + + pa_dbus_protocol_send_signal(s->dbus_protocol, signal_msg); + dbus_message_unref(signal_msg); + signal_msg = NULL; + } } new_mute = pa_sink_input_get_mute(s->sink_input); @@ -822,7 +848,14 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_playback(pa_dbusiface_core *core, p s->path = pa_sprintf_malloc("%s/%s%u", PA_DBUS_CORE_OBJECT_PATH, PLAYBACK_OBJECT_NAME, sink_input->index); s->sink = pa_sink_ref(sink_input->sink); s->sample_rate = sink_input->sample_spec.rate; - pa_sink_input_get_volume(sink_input, &s->volume, TRUE); + s->has_volume = pa_sink_input_is_volume_readable(sink_input); + s->read_only_volume = s->has_volume ? !pa_sink_input_is_volume_writable(sink_input) : FALSE; + + if (s->has_volume) + pa_sink_input_get_volume(sink_input, &s->volume, TRUE); + else + pa_cvolume_init(&s->volume); + s->mute = pa_sink_input_get_mute(sink_input); s->proplist = pa_proplist_copy(sink_input->proplist); s->dbus_protocol = pa_dbus_protocol_get(sink_input->core); @@ -853,6 +886,8 @@ pa_dbusiface_stream *pa_dbusiface_stream_new_record(pa_dbusiface_core *core, pa_ pa_cvolume_init(&s->volume); s->mute = FALSE; s->proplist = pa_proplist_copy(source_output->proplist); + s->has_volume = FALSE; + s->read_only_volume = FALSE; s->dbus_protocol = pa_dbus_protocol_get(source_output->core); s->subscription = pa_subscription_new(source_output->core, PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscription_cb, s); s->send_event_slot = pa_hook_connect(&source_output->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_SEND_EVENT], diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c index 611ebb71..0108c31b 100644 --- a/src/modules/echo-cancel/module-echo-cancel.c +++ b/src/modules/echo-cancel/module-echo-cancel.c @@ -1502,6 +1502,7 @@ int pa__init(pa_module*m) { source_output_data.driver = __FILE__; source_output_data.module = m; source_output_data.source = source_master; + source_output_data.destination_source = u->source; /* FIXME source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */ @@ -1531,11 +1532,14 @@ int pa__init(pa_module*m) { u->source_output->moving = source_output_moving_cb; u->source_output->userdata = u; + u->source->output_from_master = u->source_output; + /* Create sink input */ pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = sink_master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Echo-Cancel Sink Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &sink_ss); @@ -1566,6 +1570,8 @@ int pa__init(pa_module*m) { u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + pa_sink_input_get_silence(u->sink_input, &silence); u->source_memblockq = pa_memblockq_new(0, MEMBLOCKQ_MAXLENGTH, 0, diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c index bc349ce0..a28405b6 100644 --- a/src/modules/module-equalizer-sink.c +++ b/src/modules/module-equalizer-sink.c @@ -1200,6 +1200,7 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Equalized Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); @@ -1225,9 +1226,10 @@ int pa__init(pa_module*m) { u->sink_input->moving = sink_input_moving_cb; u->sink_input->volume_changed = sink_input_volume_changed_cb; u->sink_input->mute_changed = sink_input_mute_changed_cb; - u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + dbus_init(u); /* default filter to these */ diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c index 88df67ec..cdccf1a3 100644 --- a/src/modules/module-ladspa-sink.c +++ b/src/modules/module-ladspa-sink.c @@ -832,6 +832,7 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "LADSPA Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); @@ -859,6 +860,8 @@ int pa__init(pa_module*m) { u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + pa_sink_put(u->sink); pa_sink_input_put(u->sink_input); diff --git a/src/modules/module-remap-sink.c b/src/modules/module-remap-sink.c index 43748bd0..7f64f306 100644 --- a/src/modules/module-remap-sink.c +++ b/src/modules/module-remap-sink.c @@ -420,6 +420,7 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Remapped Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); @@ -446,6 +447,8 @@ int pa__init(pa_module*m) { u->sink_input->moving = sink_input_moving_cb; u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + pa_sink_put(u->sink); pa_sink_input_put(u->sink_input); diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c index 396094ce..5c1a86c2 100644 --- a/src/modules/module-solaris.c +++ b/src/modules/module-solaris.c @@ -1015,7 +1015,7 @@ int pa__init(pa_module *m) { else pa_log_warn("Could not register SIGPOLL handler"); - if (!(u->thread = pa_thread_new(thread_func, u))) { + if (!(u->thread = pa_thread_new("solaris", thread_func, u))) { pa_log("Failed to create thread."); goto fail; } diff --git a/src/modules/module-stream-restore.c b/src/modules/module-stream-restore.c index f8fecd89..f4e4a55c 100644 --- a/src/modules/module-stream-restore.c +++ b/src/modules/module-stream-restore.c @@ -1168,6 +1168,8 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3 } if (sink_input->save_volume) { + pa_assert(pa_sink_input_is_volume_writable(sink_input)); + entry.channel_map = sink_input->channel_map; pa_sink_input_get_volume(sink_input, &entry.volume, FALSE); entry.volume_valid = TRUE; @@ -1327,8 +1329,11 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu if ((e = read_entry(u, name))) { if (u->restore_volume && e->volume_valid) { - - if (!new_data->volume_is_set) { + if (!pa_sink_input_new_data_is_volume_writable(new_data)) + pa_log_debug("Not restoring volume for sink input %s, because its volume can't be changed.", name); + else if (new_data->volume_is_set) + pa_log_debug("Not restoring volume for sink input %s, because already set.", name); + else { pa_cvolume v; pa_log_info("Restoring volume for sink input %s.", name); @@ -1339,8 +1344,7 @@ static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_inpu new_data->volume_is_absolute = FALSE; new_data->save_volume = TRUE; - } else - pa_log_debug("Not restoring volume for sink input %s, because already set.", name); + } } if (u->restore_muted && e->muted_valid) { @@ -1615,7 +1619,7 @@ static void apply_entry(struct userdata *u, const char *name, struct entry *e) { } pa_xfree(n); - if (u->restore_volume && e->volume_valid) { + if (u->restore_volume && e->volume_valid && pa_sink_input_is_volume_writable(si)) { pa_cvolume v; v = e->volume; diff --git a/src/modules/module-virtual-sink.c b/src/modules/module-virtual-sink.c index fac204d4..40bab5aa 100644 --- a/src/modules/module-virtual-sink.c +++ b/src/modules/module-virtual-sink.c @@ -556,6 +556,7 @@ int pa__init(pa_module*m) { sink_input_data.driver = __FILE__; sink_input_data.module = m; sink_input_data.sink = master; + sink_input_data.origin_sink = u->sink; pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_NAME, "Virtual Sink Stream"); pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "filter"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); @@ -583,6 +584,8 @@ int pa__init(pa_module*m) { u->sink_input->mute_changed = sink_input_mute_changed_cb; u->sink_input->userdata = u; + u->sink->input_to_master = u->sink_input; + /* (9) IF YOU REQUIRE A FIXED BLOCK SIZE MAKE SURE TO PASS A * SILENCE MEMBLOCK AS LAST PARAMETER * HERE. pa_sink_input_get_silence() IS USEFUL HERE. */ diff --git a/src/modules/module-virtual-source.c b/src/modules/module-virtual-source.c index fdf89b02..a2b073fa 100644 --- a/src/modules/module-virtual-source.c +++ b/src/modules/module-virtual-source.c @@ -629,6 +629,7 @@ int pa__init(pa_module*m) { source_output_data.driver = __FILE__; source_output_data.module = m; source_output_data.source = master; + source_output_data.destination_source = u->source; /* FIXME source_output_data.flags = PA_SOURCE_OUTPUT_DONT_INHIBIT_AUTO_SUSPEND; */ @@ -654,6 +655,8 @@ int pa__init(pa_module*m) { u->source_output->moving = source_output_moving_cb; u->source_output->userdata = u; + u->source->output_from_master = u->source_output; + pa_source_put(u->source); pa_source_output_put(u->source_output); diff --git a/src/pulse/introspect.h b/src/pulse/introspect.h index bc50611e..1cadee52 100644 --- a/src/pulse/introspect.h +++ b/src/pulse/introspect.h @@ -503,6 +503,8 @@ typedef struct pa_sink_input_info { int mute; /**< Stream muted \since 0.9.7 */ pa_proplist *proplist; /**< Property list \since 0.9.11 */ int corked; /**< Stream corked \since 1.0 */ + int has_volume; /**< Stream has volume. If not set, then the meaning of this struct's volume member is unspecified. \since 1.0 */ + int read_only_volume; /**< Stream volume can only be read. Although volume control is disabled, the stream volume is still not necessarily constant. \since 1.0 */ } pa_sink_input_info; /** Callback prototype for pa_context_get_sink_input_info() and friends*/ diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index a18ebd33..18dfb4c3 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -579,11 +579,16 @@ static int pa_cli_command_sink_input_volume(pa_core *c, pa_tokenizer *t, pa_strb return -1; } - if (!(si = pa_idxset_get_by_index(c->sink_inputs, (uint32_t) idx))) { + if (!(si = pa_idxset_get_by_index(c->sink_inputs, idx))) { pa_strbuf_puts(buf, "No sink input found with this index.\n"); return -1; } + if (!pa_sink_input_is_volume_writable(si)) { + pa_strbuf_puts(buf, "This sink input's volume can't be changed.\n"); + return -1; + } + pa_cvolume_set(&cvolume, 1, volume); pa_sink_input_set_volume(si, &cvolume, TRUE, TRUE); return 0; diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index 23a57d37..e6018da2 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -553,8 +553,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_usec_t cl; const char *cmn; pa_cvolume v; - - pa_sink_input_get_volume(i, &v, TRUE); + char *volume_str = NULL; cmn = pa_channel_map_to_pretty_name(&i->channel_map); @@ -565,6 +564,15 @@ char *pa_sink_input_list_to_string(pa_core *c) { pa_assert(i->sink); + if (pa_sink_input_is_volume_readable(i)) { + pa_sink_input_get_volume(i, &v, TRUE); + volume_str = pa_sprintf_malloc("%s\n\t %s\n\t balance %0.2f", + pa_cvolume_snprint(cv, sizeof(cv), &v), + pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &v), + pa_cvolume_get_balance(&v, &i->channel_map)); + } else + volume_str = pa_xstrdup("n/a"); + pa_strbuf_printf( s, " index: %u\n" @@ -573,8 +581,6 @@ char *pa_sink_input_list_to_string(pa_core *c) { "\tstate: %s\n" "\tsink: %u <%s>\n" "\tvolume: %s\n" - "\t %s\n" - "\t balance %0.2f\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\trequested latency: %s\n" @@ -596,9 +602,7 @@ char *pa_sink_input_list_to_string(pa_core *c) { i->flags & PA_SINK_INPUT_KILL_ON_SUSPEND ? "KILL_ON_SUSPEND " : "", state_table[pa_sink_input_get_state(i)], i->sink->index, i->sink->name, - pa_cvolume_snprint(cv, sizeof(cv), &v), - pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), &v), - pa_cvolume_get_balance(&v, &i->channel_map), + volume_str, pa_yes_no(pa_sink_input_get_mute(i)), (double) pa_sink_input_get_latency(i, NULL) / PA_USEC_PER_MSEC, clt, @@ -608,6 +612,8 @@ char *pa_sink_input_list_to_string(pa_core *c) { cmn ? cmn : "", pa_resample_method_to_string(pa_sink_input_get_resample_method(i))); + pa_xfree(volume_str); + if (i->module) pa_strbuf_printf(s, "\tmodule: %u\n", i->module->index); if (i->client) diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index cc6a6b1d..92575242 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -3056,12 +3056,19 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sample_spec fixed_ss; pa_usec_t sink_latency; pa_cvolume v; + pa_bool_t has_volume = FALSE; pa_assert(t); pa_sink_input_assert_ref(s); fixup_sample_spec(c, &fixed_ss, &s->sample_spec); + has_volume = pa_sink_input_is_volume_readable(s); + if (has_volume) + pa_sink_input_get_volume(s, &v, TRUE); + else + pa_cvolume_reset(&v, fixed_ss.channels); + pa_tagstruct_putu32(t, s->index); pa_tagstruct_puts(t, pa_strnull(pa_proplist_gets(s->proplist, PA_PROP_MEDIA_NAME))); pa_tagstruct_putu32(t, s->module ? s->module->index : PA_INVALID_INDEX); @@ -3069,7 +3076,7 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_putu32(t, s->sink->index); pa_tagstruct_put_sample_spec(t, &fixed_ss); pa_tagstruct_put_channel_map(t, &s->channel_map); - pa_tagstruct_put_cvolume(t, pa_sink_input_get_volume(s, &v, TRUE)); + pa_tagstruct_put_cvolume(t, &v); pa_tagstruct_put_usec(t, pa_sink_input_get_latency(s, &sink_latency)); pa_tagstruct_put_usec(t, sink_latency); pa_tagstruct_puts(t, pa_resample_method_to_string(pa_sink_input_get_resample_method(s))); @@ -3080,6 +3087,10 @@ static void sink_input_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_tagstruct_put_proplist(t, s->proplist); if (c->version >= 19) pa_tagstruct_put_boolean(t, (pa_sink_input_get_state(s) == PA_SINK_INPUT_CORKED)); + if (c->version >= 20) { + pa_tagstruct_put_boolean(t, has_volume); + pa_tagstruct_put_boolean(t, has_volume ? !pa_sink_input_is_volume_writable(s) : FALSE); + } } static void source_output_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source_output *s) { @@ -3461,6 +3472,7 @@ static void command_set_volume( pa_log_debug("Client %s changes volume of source %s.", client_name, source->name); pa_source_set_volume(source, &volume, TRUE); } else if (si) { + CHECK_VALIDITY(c->pstream, pa_sink_input_is_volume_writable(si), tag, PA_ERR_INVALID); CHECK_VALIDITY(c->pstream, volume.channels == 1 || pa_cvolume_compatible(&volume, &si->sample_spec), tag, PA_ERR_INVALID); pa_log_debug("Client %s changes volume of sink input %s.", diff --git a/src/pulsecore/sconv_sse.c b/src/pulsecore/sconv_sse.c index 3737af2a..2e74d744 100644 --- a/src/pulsecore/sconv_sse.c +++ b/src/pulsecore/sconv_sse.c @@ -35,7 +35,7 @@ #include "cpu-x86.h" #include "sconv.h" -#if defined (__i386__) || defined (__amd64__) +#if !defined(__APPLE__) && defined (__i386__) || defined (__amd64__) static const PA_DECLARE_ALIGNED (16, float, one[4]) = { 1.0, 1.0, 1.0, 1.0 }; static const PA_DECLARE_ALIGNED (16, float, mone[4]) = { -1.0, -1.0, -1.0, -1.0 }; @@ -217,7 +217,7 @@ static void run_test (void) { void pa_convert_func_init_sse (pa_cpu_x86_flag_t flags) { -#if defined (__i386__) || defined (__amd64__) +#if !defined(__APPLE__) && defined (__i386__) || defined (__amd64__) #ifdef RUN_TEST run_test (); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index 065fd2d3..283d0265 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -112,8 +112,15 @@ void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const data->channel_map = *map; } +pa_bool_t pa_sink_input_new_data_is_volume_writable(pa_sink_input_new_data *data) { + pa_assert(data); + + return !(data->flags & PA_SINK_INPUT_PASSTHROUGH); +} + void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume) { pa_assert(data); + pa_assert(pa_sink_input_new_data_is_volume_writable(data)); if ((data->volume_is_set = !!volume)) data->volume = *volume; @@ -205,6 +212,7 @@ int pa_sink_input_new( if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], data)) < 0) return r; + pa_assert(!data->volume_is_set || pa_sink_input_new_data_is_volume_writable(data)); pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); if (!data->sink) { @@ -321,6 +329,7 @@ int pa_sink_input_new( i->driver = pa_xstrdup(pa_path_get_filename(data->driver)); i->module = data->module; i->sink = data->sink; + i->origin_sink = data->origin_sink; i->client = data->client; i->requested_resample_method = data->resample_method; @@ -1058,12 +1067,23 @@ static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { } /* Called from main context */ -void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) { +pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i) { + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); - /* Do not allow for volume changes for non-audio types */ - if (i->flags & PA_SINK_INPUT_PASSTHROUGH) - return; + return !(i->flags & PA_SINK_INPUT_PASSTHROUGH); +} +/* Called from main context */ +pa_bool_t pa_sink_input_is_volume_writable(pa_sink_input *i) { + pa_sink_input_assert_ref(i); + pa_assert_ctl_context(); + + return !(i->flags & PA_SINK_INPUT_PASSTHROUGH); +} + +/* Called from main context */ +void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) { /* test ramping -> return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 2000 * PA_USEC_PER_MSEC); */ return pa_sink_input_set_volume_with_ramping(i, volume, save, absolute, 0); } @@ -1073,6 +1093,7 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bo pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); + pa_assert(pa_sink_input_is_volume_readable(i)); if (absolute || !(i->sink->flags & PA_SINK_FLAT_VOLUME)) *volume = i->volume; @@ -1853,6 +1874,7 @@ void pa_sink_input_set_volume_with_ramping(pa_sink_input *i, const pa_cvolume *v pa_assert(volume); pa_assert(pa_cvolume_valid(volume)); pa_assert(volume->channels == 1 || pa_cvolume_compatible(volume, &i->sample_spec)); + pa_assert(pa_sink_input_is_volume_writable(i)); if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) { v = i->sink->reference_volume; diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index f81e2d4b..9d8d169a 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -83,7 +83,8 @@ struct pa_sink_input { pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ - pa_sink *sink; /* NULL while we are being moved */ + pa_sink *sink; /* NULL while we are being moved */ + pa_sink *origin_sink; /* only set by filter sinks */ /* A sink input may be connected to multiple source outputs * directly, so that they don't get mixed data of the entire @@ -285,6 +286,7 @@ typedef struct pa_sink_input_new_data { pa_client *client; pa_sink *sink; + pa_sink *origin_sink; pa_resample_method_t resample_method; @@ -310,6 +312,7 @@ typedef struct pa_sink_input_new_data { pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_sample_spec(pa_sink_input_new_data *data, const pa_sample_spec *spec); void pa_sink_input_new_data_set_channel_map(pa_sink_input_new_data *data, const pa_channel_map *map); +pa_bool_t pa_sink_input_new_data_is_volume_writable(pa_sink_input_new_data *data); void pa_sink_input_new_data_set_volume(pa_sink_input_new_data *data, const pa_cvolume *volume); void pa_sink_input_new_data_apply_volume_factor(pa_sink_input_new_data *data, const pa_cvolume *volume_factor); void pa_sink_input_new_data_apply_volume_factor_sink(pa_sink_input_new_data *data, const pa_cvolume *volume_factor); @@ -354,6 +357,8 @@ void pa_sink_input_kill(pa_sink_input*i); pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency); +pa_bool_t pa_sink_input_is_volume_readable(pa_sink_input *i); +pa_bool_t pa_sink_input_is_volume_writable(pa_sink_input *i); void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute); pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute); diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 62000e0d..0de544c9 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -269,6 +269,7 @@ pa_sink* pa_sink_new( s->inputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; + s->input_to_master = NULL; s->reference_volume = s->real_volume = data->volume; pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 4d569ddc..8a515870 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -44,6 +44,7 @@ typedef struct pa_sink_volume_change pa_sink_volume_change; #include <pulsecore/card.h> #include <pulsecore/queue.h> #include <pulsecore/thread-mq.h> +#include <pulsecore/sink-input.h> #define PA_MAX_INPUTS_PER_SINK 32 @@ -86,6 +87,7 @@ struct pa_sink { pa_idxset *inputs; unsigned n_corked; pa_source *monitor_source; + pa_sink_input *input_to_master; /* non-NULL only for filter sinks */ pa_volume_t base_volume; /* shall be constant */ unsigned n_volume_steps; /* shall be constant */ diff --git a/src/pulsecore/sndfile-util.c b/src/pulsecore/sndfile-util.c index 9d15a868..cadda931 100644 --- a/src/pulsecore/sndfile-util.c +++ b/src/pulsecore/sndfile-util.c @@ -51,6 +51,9 @@ int pa_sndfile_read_sample_spec(SNDFILE *sf, pa_sample_spec *ss) { break; case SF_FORMAT_PCM_24: + ss->format = PA_SAMPLE_S24NE; + break; + case SF_FORMAT_PCM_32: ss->format = PA_SAMPLE_S32NE; break; @@ -106,10 +109,14 @@ int pa_sndfile_write_sample_spec(SF_INFO *sfi, pa_sample_spec *ss) { case PA_SAMPLE_S24LE: case PA_SAMPLE_S24BE: + ss->format = PA_SAMPLE_S24NE; + sfi->format |= SF_FORMAT_PCM_24; + break; + case PA_SAMPLE_S24_32LE: case PA_SAMPLE_S24_32BE: - ss->format = PA_SAMPLE_S32NE; - sfi->format |= SF_FORMAT_PCM_24; + ss->format = PA_SAMPLE_S24_32NE; + sfi->format |= SF_FORMAT_PCM_32; break; case PA_SAMPLE_S32LE: @@ -362,6 +369,7 @@ pa_sndfile_readf_t pa_sndfile_readf_function(const pa_sample_spec *ss) { return (pa_sndfile_readf_t) sf_readf_short; case PA_SAMPLE_S32NE: + case PA_SAMPLE_S24_32NE: return (pa_sndfile_readf_t) sf_readf_int; case PA_SAMPLE_FLOAT32NE: @@ -384,6 +392,7 @@ pa_sndfile_writef_t pa_sndfile_writef_function(const pa_sample_spec *ss) { return (pa_sndfile_writef_t) sf_writef_short; case PA_SAMPLE_S32NE: + case PA_SAMPLE_S24_32NE: return (pa_sndfile_writef_t) sf_writef_int; case PA_SAMPLE_FLOAT32NE: diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c index 88731e76..0bb8899c 100644 --- a/src/pulsecore/source-output.c +++ b/src/pulsecore/source-output.c @@ -208,6 +208,7 @@ int pa_source_output_new( o->driver = pa_xstrdup(pa_path_get_filename(data->driver)); o->module = data->module; o->source = data->source; + o->destination_source = data->destination_source; o->client = data->client; o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h index 273b78fc..f16f9520 100644 --- a/src/pulsecore/source-output.h +++ b/src/pulsecore/source-output.h @@ -74,7 +74,8 @@ struct pa_source_output { pa_module *module; /* may be NULL */ pa_client *client; /* may be NULL */ - pa_source *source; /* NULL while being moved */ + pa_source *source; /* NULL while being moved */ + pa_source *destination_source; /* only set by filter sources */ /* A source output can monitor just a single input of a sink, in which case we find it here */ pa_sink_input *direct_on_input; /* may be NULL */ @@ -211,6 +212,7 @@ typedef struct pa_source_output_new_data { pa_client *client; pa_source *source; + pa_source *destination_source; pa_resample_method_t resample_method; diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 412a3db9..24d0ff6b 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -221,6 +221,7 @@ pa_source* pa_source_new( s->outputs = pa_idxset_new(NULL, NULL); s->n_corked = 0; s->monitor_of = NULL; + s->output_from_master = NULL; s->volume = data->volume; pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index e3e56bc4..f3af159e 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -75,6 +75,7 @@ struct pa_source { pa_idxset *outputs; unsigned n_corked; pa_sink *monitor_of; /* may be NULL */ + pa_source_output *output_from_master; /* non-NULL only for filter sources */ pa_volume_t base_volume; /* shall be constant */ unsigned n_volume_steps; /* shall be constant */ diff --git a/src/pulsecore/svolume_arm.c b/src/pulsecore/svolume_arm.c index 3973e518..c2830cfd 100644 --- a/src/pulsecore/svolume_arm.c +++ b/src/pulsecore/svolume_arm.c @@ -39,6 +39,7 @@ #define MOD_INC() \ " subs r0, r6, %2 \n\t" \ + " itt cs \n\t" \ " addcs r0, %1 \n\t" \ " movcs r6, r0 \n\t" |