summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am2
-rw-r--r--src/modules/coreaudio/module-coreaudio-device.c2
-rw-r--r--src/modules/dbus/iface-stream.c121
-rw-r--r--src/modules/echo-cancel/module-echo-cancel.c6
-rw-r--r--src/modules/module-equalizer-sink.c4
-rw-r--r--src/modules/module-ladspa-sink.c3
-rw-r--r--src/modules/module-remap-sink.c3
-rw-r--r--src/modules/module-solaris.c2
-rw-r--r--src/modules/module-stream-restore.c14
-rw-r--r--src/modules/module-virtual-sink.c3
-rw-r--r--src/modules/module-virtual-source.c3
-rw-r--r--src/pulse/introspect.h2
-rw-r--r--src/pulsecore/cli-command.c7
-rw-r--r--src/pulsecore/cli-text.c20
-rw-r--r--src/pulsecore/protocol-native.c14
-rw-r--r--src/pulsecore/sconv_sse.c4
-rw-r--r--src/pulsecore/sink-input.c30
-rw-r--r--src/pulsecore/sink-input.h7
-rw-r--r--src/pulsecore/sink.c1
-rw-r--r--src/pulsecore/sink.h2
-rw-r--r--src/pulsecore/sndfile-util.c13
-rw-r--r--src/pulsecore/source-output.c1
-rw-r--r--src/pulsecore/source-output.h4
-rw-r--r--src/pulsecore/source.c1
-rw-r--r--src/pulsecore/source.h1
-rw-r--r--src/pulsecore/svolume_arm.c1
26 files changed, 200 insertions, 71 deletions
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"