summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/cli-text.c12
-rw-r--r--src/pulsecore/dbus-shared.c12
-rw-r--r--src/pulsecore/dbus-util.c21
-rw-r--r--src/pulsecore/log.h1
-rw-r--r--src/pulsecore/memblockq.c52
-rw-r--r--src/pulsecore/protocol-native.c14
-rw-r--r--src/pulsecore/sink-input.c90
-rw-r--r--src/pulsecore/sink-input.h12
-rw-r--r--src/pulsecore/sink.c158
-rw-r--r--src/pulsecore/sink.h8
-rw-r--r--src/pulsecore/socket-server.c1
-rw-r--r--src/pulsecore/source-output.c30
-rw-r--r--src/pulsecore/source-output.h4
-rw-r--r--src/pulsecore/source.c55
-rw-r--r--src/pulsecore/source.h2
-rw-r--r--src/pulsecore/time-smoother.c42
16 files changed, 370 insertions, 144 deletions
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index 324f83c8..b0911ef1 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -288,7 +288,11 @@ char *pa_sink_list_to_string(pa_core *c) {
(double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC,
(double) min_latency / PA_USEC_PER_MSEC,
(double) max_latency / PA_USEC_PER_MSEC);
- }
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_sink_get_requested_latency(sink) / PA_USEC_PER_MSEC);
if (sink->card)
pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name);
@@ -382,7 +386,11 @@ char *pa_source_list_to_string(pa_core *c) {
(double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC,
(double) min_latency / PA_USEC_PER_MSEC,
(double) max_latency / PA_USEC_PER_MSEC);
- }
+ } else
+ pa_strbuf_printf(
+ s,
+ "\tfixed latency: %0.2f ms\n",
+ (double) pa_source_get_requested_latency(source) / PA_USEC_PER_MSEC);
if (source->monitor_of)
pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index);
diff --git a/src/pulsecore/dbus-shared.c b/src/pulsecore/dbus-shared.c
index b52c14cb..9d9445b6 100644
--- a/src/pulsecore/dbus-shared.c
+++ b/src/pulsecore/dbus-shared.c
@@ -41,7 +41,7 @@ struct pa_dbus_connection {
const char *property_name;
};
-static pa_dbus_connection* pa_dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) {
+static pa_dbus_connection* dbus_connection_new(pa_core *c, pa_dbus_wrap_connection *conn, const char *name) {
pa_dbus_connection *pconn;
pconn = pa_xnew(pa_dbus_connection, 1);
@@ -73,9 +73,7 @@ pa_dbus_connection* pa_dbus_bus_get(pa_core *c, DBusBusType type, DBusError *err
if (!(conn = pa_dbus_wrap_connection_new(c->mainloop, type, error)))
return NULL;
- pconn = pa_dbus_connection_new(c, conn, prop_name[type]);
-
- return pconn;
+ return dbus_connection_new(c, conn, prop_name[type]);
}
DBusConnection* pa_dbus_connection_get(pa_dbus_connection *c){
@@ -93,7 +91,8 @@ void pa_dbus_connection_unref(pa_dbus_connection *c) {
if (PA_REFCNT_DEC(c) > 0)
return;
- /* already disconnected, just free */
+ pa_dbus_wrap_connection_free(c->connection);
+
pa_shared_remove(c->core, c->property_name);
pa_xfree(c);
}
@@ -106,6 +105,3 @@ pa_dbus_connection* pa_dbus_connection_ref(pa_dbus_connection *c) {
return c;
}
-
-
-
diff --git a/src/pulsecore/dbus-util.c b/src/pulsecore/dbus-util.c
index d712bff3..ece36def 100644
--- a/src/pulsecore/dbus-util.c
+++ b/src/pulsecore/dbus-util.c
@@ -28,6 +28,8 @@
#include <pulse/xmalloc.h>
#include <pulse/timeval.h>
+
+#include <pulsecore/core-util.h>
#include <pulsecore/log.h>
#include "dbus-util.h"
@@ -244,7 +246,8 @@ static void wakeup_main(void *userdata) {
pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBusType type, DBusError *error) {
DBusConnection *conn;
- pa_dbus_wrap_connection *pconn = NULL;
+ pa_dbus_wrap_connection *pconn;
+ char *id;
pa_assert(type == DBUS_BUS_SYSTEM || type == DBUS_BUS_SESSION || type == DBUS_BUS_STARTER);
@@ -263,6 +266,13 @@ pa_dbus_wrap_connection* pa_dbus_wrap_connection_new(pa_mainloop_api *m, DBusBus
pconn->dispatch_event = pconn->mainloop->defer_new(pconn->mainloop, dispatch_cb, conn);
+ pa_log_debug("Successfully connected to D-Bus %s bus %s as %s",
+ type == DBUS_BUS_SYSTEM ? "system" : (type == DBUS_BUS_SESSION ? "session" : "starter"),
+ pa_strnull((id = dbus_connection_get_server_id(conn))),
+ pa_strnull(dbus_bus_get_unique_name(conn)));
+
+ dbus_free(id);
+
return pconn;
}
@@ -273,7 +283,8 @@ void pa_dbus_wrap_connection_free(pa_dbus_wrap_connection* c) {
dbus_connection_close(c->connection);
/* must process remaining messages, bit of a kludge to handle
* both unload and shutdown */
- while (dbus_connection_read_write_dispatch(c->connection, -1));
+ while (dbus_connection_read_write_dispatch(c->connection, -1))
+ ;
}
c->mainloop->defer_free(c->dispatch_event);
@@ -369,8 +380,10 @@ pa_dbus_pending *pa_dbus_pending_new(
void pa_dbus_pending_free(pa_dbus_pending *p) {
pa_assert(p);
- if (p->pending)
- dbus_pending_call_cancel(p->pending); /* p->pending is freed by cancel() */
+ if (p->pending) {
+ dbus_pending_call_cancel(p->pending);
+ dbus_pending_call_unref(p->pending);
+ }
if (p->message)
dbus_message_unref(p->message);
diff --git a/src/pulsecore/log.h b/src/pulsecore/log.h
index 8628bf40..2f379f68 100644
--- a/src/pulsecore/log.h
+++ b/src/pulsecore/log.h
@@ -113,6 +113,7 @@ void pa_log_levelv(
#define pa_log_notice(...) pa_log_level_meta(PA_LOG_NOTICE, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_warn(...) pa_log_level_meta(PA_LOG_WARN, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define pa_log_error(...) pa_log_level_meta(PA_LOG_ERROR, __FILE__, __LINE__, __func__, __VA_ARGS__)
+#define pa_logl(level, ...) pa_log_level_meta(level, __FILE__, __LINE__, __func__, __VA_ARGS__)
#else
diff --git a/src/pulsecore/memblockq.c b/src/pulsecore/memblockq.c
index d12d13a8..77f9efc9 100644
--- a/src/pulsecore/memblockq.c
+++ b/src/pulsecore/memblockq.c
@@ -90,8 +90,8 @@ pa_memblockq* pa_memblockq_new(
pa_memblockq_set_maxlength(bq, maxlength);
pa_memblockq_set_tlength(bq, tlength);
- pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_minreq(bq, minreq);
+ pa_memblockq_set_prebuf(bq, prebuf);
pa_memblockq_set_maxrewind(bq, maxrewind);
pa_log_debug("memblockq sanitized: maxlength=%lu, tlength=%lu, base=%lu, prebuf=%lu, minreq=%lu maxrewind=%lu",
@@ -784,16 +784,13 @@ void pa_memblockq_set_maxlength(pa_memblockq *bq, size_t maxlength) {
if (bq->tlength > bq->maxlength)
pa_memblockq_set_tlength(bq, bq->maxlength);
-
- if (bq->prebuf > bq->maxlength)
- pa_memblockq_set_prebuf(bq, bq->maxlength);
}
void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
size_t old_tlength;
pa_assert(bq);
- if (tlength <= 0)
+ if (tlength <= 0 || tlength == (size_t) -1)
tlength = bq->maxlength;
old_tlength = bq->tlength;
@@ -802,49 +799,46 @@ void pa_memblockq_set_tlength(pa_memblockq *bq, size_t tlength) {
if (bq->tlength > bq->maxlength)
bq->tlength = bq->maxlength;
- if (bq->prebuf > bq->tlength)
- pa_memblockq_set_prebuf(bq, bq->tlength);
-
if (bq->minreq > bq->tlength)
pa_memblockq_set_minreq(bq, bq->tlength);
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+
bq->missing += (int64_t) bq->tlength - (int64_t) old_tlength;
}
+void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
+ pa_assert(bq);
+
+ bq->minreq = (minreq/bq->base)*bq->base;
+
+ if (bq->minreq > bq->tlength)
+ bq->minreq = bq->tlength;
+
+ if (bq->minreq < bq->base)
+ bq->minreq = bq->base;
+
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ pa_memblockq_set_prebuf(bq, bq->tlength+bq->base-bq->minreq);
+}
+
void pa_memblockq_set_prebuf(pa_memblockq *bq, size_t prebuf) {
pa_assert(bq);
if (prebuf == (size_t) -1)
- prebuf = bq->tlength;
+ prebuf = bq->tlength+bq->base-bq->minreq;
bq->prebuf = ((prebuf+bq->base-1)/bq->base)*bq->base;
if (prebuf > 0 && bq->prebuf < bq->base)
bq->prebuf = bq->base;
- if (bq->prebuf > bq->tlength)
- bq->prebuf = bq->tlength;
+ if (bq->prebuf > bq->tlength+bq->base-bq->minreq)
+ bq->prebuf = bq->tlength+bq->base-bq->minreq;
if (bq->prebuf <= 0 || pa_memblockq_get_length(bq) >= bq->prebuf)
bq->in_prebuf = FALSE;
-
- if (bq->minreq > bq->prebuf)
- pa_memblockq_set_minreq(bq, bq->prebuf);
-}
-
-void pa_memblockq_set_minreq(pa_memblockq *bq, size_t minreq) {
- pa_assert(bq);
-
- bq->minreq = (minreq/bq->base)*bq->base;
-
- if (bq->minreq > bq->tlength)
- bq->minreq = bq->tlength;
-
- if (bq->minreq > bq->prebuf)
- bq->minreq = bq->prebuf;
-
- if (bq->minreq < bq->base)
- bq->minreq = bq->base;
}
void pa_memblockq_set_maxrewind(pa_memblockq *bq, size_t maxrewind) {
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index 59e5d80e..7c2183d8 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -850,7 +850,7 @@ static int playback_stream_process_msg(pa_msgobject *o, int code, void*userdata,
/* Called from main context */
static void fix_playback_buffer_attr(playback_stream *s) {
- size_t frame_size;
+ size_t frame_size, max_prebuf;
pa_usec_t orig_tlength_usec, tlength_usec, orig_minreq_usec, minreq_usec, sink_usec;
pa_assert(s);
@@ -976,8 +976,11 @@ static void fix_playback_buffer_attr(playback_stream *s) {
if (s->buffer_attr.tlength <= s->buffer_attr.minreq)
s->buffer_attr.tlength = s->buffer_attr.minreq*2 + (uint32_t) frame_size;
- if (s->buffer_attr.prebuf == (uint32_t) -1 || s->buffer_attr.prebuf > s->buffer_attr.tlength)
- s->buffer_attr.prebuf = s->buffer_attr.tlength;
+ max_prebuf = s->buffer_attr.tlength + (uint32_t)frame_size - s->buffer_attr.minreq;
+
+ if (s->buffer_attr.prebuf == (uint32_t) -1 ||
+ s->buffer_attr.prebuf > max_prebuf)
+ s->buffer_attr.prebuf = max_prebuf;
}
/* Called from main context */
@@ -1465,7 +1468,8 @@ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk
if (pa_memblockq_is_readable(s->memblockq))
s->is_underrun = FALSE;
else {
- pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
+ if (!s->is_underrun)
+ pa_log_debug("Underrun on '%s', %lu bytes in queue.", pa_strnull(pa_proplist_gets(i->proplist, PA_PROP_MEDIA_NAME)), (unsigned long) pa_memblockq_get_length(s->memblockq));
if (s->drain_request && pa_sink_input_safe_to_remove(i)) {
s->drain_request = FALSE;
@@ -4241,7 +4245,7 @@ static void pstream_memblock_callback(pa_pstream *p, uint32_t channel, int64_t o
pa_native_connection_assert_ref(c);
if (!(stream = OUTPUT_STREAM(pa_idxset_get_by_index(c->output_streams, channel)))) {
- pa_log("client sent block for invalid stream.");
+ pa_log_debug("Client sent block for invalid stream.");
/* Ignoring */
return;
}
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 1fdb3fa6..b1b9fb56 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -117,6 +117,7 @@ static void reset_callbacks(pa_sink_input *i) {
i->attach = NULL;
i->detach = NULL;
i->suspend = NULL;
+ i->suspend_within_thread = NULL;
i->moving = NULL;
i->kill = NULL;
i->get_latency = NULL;
@@ -287,6 +288,7 @@ int pa_sink_input_new(
i->volume_factor = data->volume_factor;
pa_cvolume_init(&i->soft_volume);
+ memset(i->relative_volume, 0, sizeof(i->relative_volume));
i->save_volume = data->save_volume;
i->save_sink = data->save_sink;
i->save_muted = data->save_muted;
@@ -333,8 +335,8 @@ int pa_sink_input_new(
0,
&i->sink->silence);
- pa_assert_se(pa_idxset_put(core->sink_inputs, pa_sink_input_ref(i), &i->index) == 0);
- pa_assert_se(pa_idxset_put(i->sink->inputs, i, NULL) == 0);
+ pa_assert_se(pa_idxset_put(core->sink_inputs, i, &i->index) == 0);
+ pa_assert_se(pa_idxset_put(i->sink->inputs, pa_sink_input_ref(i), NULL) == 0);
if (i->client)
pa_assert_se(pa_idxset_put(i->client->sink_inputs, i, NULL) >= 0);
@@ -529,7 +531,7 @@ void pa_sink_input_put(pa_sink_input *i) {
pa_sink_update_flat_volume(i->sink, &new_volume);
pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE);
} else
- pa_sw_cvolume_multiply(&i->soft_volume, &i->virtual_volume, &i->volume_factor);
+ pa_sink_input_set_relative_volume(i, &i->virtual_volume);
i->thread_info.soft_volume = i->soft_volume;
i->thread_info.muted = i->muted;
@@ -822,6 +824,9 @@ void pa_sink_input_update_max_request(pa_sink_input *i, size_t nbytes /* in the
pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa_usec_t usec) {
pa_sink_input_assert_ref(i);
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
+
if (usec != (pa_usec_t) -1)
usec = PA_CLAMP(usec, i->sink->thread_info.min_latency, i->sink->thread_info.max_latency);
@@ -833,8 +838,6 @@ pa_usec_t pa_sink_input_set_requested_latency_within_thread(pa_sink_input *i, pa
/* Called from main context */
pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec) {
- pa_usec_t min_latency, max_latency;
-
pa_sink_input_assert_ref(i);
if (PA_SINK_INPUT_IS_LINKED(i->state) && i->sink) {
@@ -846,10 +849,14 @@ pa_usec_t pa_sink_input_set_requested_latency(pa_sink_input *i, pa_usec_t usec)
* we have to touch the thread info data directly */
if (i->sink) {
- pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
+ if (!(i->sink->flags & PA_SINK_DYNAMIC_LATENCY))
+ usec = i->sink->fixed_latency;
- if (usec != (pa_usec_t) -1)
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_sink_get_latency_range(i->sink, &min_latency, &max_latency);
usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
}
i->thread_info.requested_sink_latency = usec;
@@ -900,11 +907,12 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
/* OK, we are in normal volume mode. The volume only affects
* ourselves */
- pa_sw_cvolume_multiply(&i->soft_volume, volume, &i->volume_factor);
+ pa_sink_input_set_relative_volume(i, volume);
/* Hooks have the ability to play games with i->soft_volume */
pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ /* Copy the new soft_volume to the thread_info struct */
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0);
}
@@ -922,24 +930,50 @@ const pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i) {
/* Called from main context */
pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) {
+ unsigned c;
+
pa_sink_input_assert_ref(i);
pa_assert(v);
pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
- *v = i->virtual_volume;
-
/* This always returns a relative volume, even in flat volume mode */
- if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
- pa_cvolume sv;
+ v->channels = i->sample_spec.channels;
+
+ for (c = 0; c < v->channels; c++)
+ v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]);
+
+ return v;
+}
+
+/* Called from main context */
+void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) {
+ unsigned c;
+ pa_cvolume _v;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(PA_SINK_INPUT_IS_LINKED(i->state));
+ pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec));
- sv = *pa_sink_get_volume(i->sink, FALSE);
+ if (!v)
+ v = pa_cvolume_reset(&_v, i->sample_spec.channels);
- pa_sw_cvolume_divide(v, v,
- pa_cvolume_remap(&sv, &i->sink->channel_map, &i->channel_map));
+ /* This basically calculates:
+ *
+ * i->relative_volume := v
+ * i->soft_volume := i->relative_volume * i->volume_factor */
+
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++) {
+ i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]);
+
+ i->soft_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(i->volume_factor.values[c]));
}
- return v;
+ /* We don't copy the data to the thread_info data. That's left for someone else to do */
}
/* Called from main context */
@@ -1109,9 +1143,11 @@ int pa_sink_input_start_move(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
- /* Make the absolute volume relative */
- i->virtual_volume = i->soft_volume;
- i->soft_volume = i->volume_factor;
+ /* Make the virtual volume relative */
+ pa_sink_input_get_relative_volume(i, &i->virtual_volume);
+
+ /* And reset the the relative volume */
+ pa_sink_input_set_relative_volume(i, NULL);
/* We might need to update the sink's volume if we are in flat
* volume mode. */
@@ -1124,6 +1160,8 @@ int pa_sink_input_start_move(pa_sink_input *i) {
pa_sink_update_status(i->sink);
i->sink = NULL;
+ pa_sink_input_unref(i);
+
return 0;
}
@@ -1171,7 +1209,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
i->sink = dest;
i->save_sink = save;
- pa_idxset_put(dest->inputs, i, NULL);
+ pa_idxset_put(dest->inputs, pa_sink_input_ref(i), NULL);
if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)
i->sink->n_corked++;
@@ -1236,11 +1274,19 @@ int pa_sink_input_move_to(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
if (!pa_sink_input_may_move_to(i, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_sink_input_start_move(i)) < 0)
+ pa_sink_input_ref(i);
+
+ if ((r = pa_sink_input_start_move(i)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
- if ((r = pa_sink_input_finish_move(i, dest, save)) < 0)
+ if ((r = pa_sink_input_finish_move(i, dest, save)) < 0) {
+ pa_sink_input_unref(i);
return r;
+ }
+
+ pa_sink_input_unref(i);
return 0;
}
diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h
index 0dd5e9aa..96ad2baf 100644
--- a/src/pulsecore/sink-input.h
+++ b/src/pulsecore/sink-input.h
@@ -91,7 +91,10 @@ struct pa_sink_input {
pa_sink_input *sync_prev, *sync_next;
- pa_cvolume virtual_volume, soft_volume, volume_factor;
+ pa_cvolume virtual_volume; /* The volume clients are informed about */
+ pa_cvolume volume_factor; /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */
+ double relative_volume[PA_CHANNELS_MAX]; /* The calculated volume relative to the sink volume as linear factors. */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as relative_volume * volume_factor */
pa_bool_t muted:1;
/* if TRUE then the source we are connected to and/or the volume
@@ -148,6 +151,10 @@ struct pa_sink_input {
* to suspends or resumes. Called from main context */
void (*suspend) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+ /* If non-NULL called whenever the sink this input is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_sink_input *i, pa_bool_t b); /* may be NULL */
+
/* If non-NULL called whenever the sink input is moved to a new
* sink. Called from main context after the sink input has been
* detached from the old sink and before it has been attached to
@@ -345,4 +352,7 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);
pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret);
+/* To be used by sink.c only */
+void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v);
+
#endif
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index a0f0ea7e..93800d14 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -50,6 +50,7 @@
#define MIX_BUFFER_LENGTH (PA_PAGE_SIZE)
#define ABSOLUTE_MIN_LATENCY (500)
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_sink, pa_msgobject);
@@ -208,6 +209,8 @@ pa_sink* pa_sink_new(
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
+ s->fixed_latency = flags & PA_SINK_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
reset_callbacks(s);
s->userdata = NULL;
@@ -363,8 +366,13 @@ void pa_sink_put(pa_sink* s) {
if (s->flags & PA_SINK_LATENCY)
s->monitor_source->flags |= PA_SOURCE_LATENCY;
- if (s->flags & PA_SINK_DYNAMIC_LATENCY)
+ if (s->flags & PA_SINK_DYNAMIC_LATENCY) {
s->monitor_source->flags |= PA_SOURCE_DYNAMIC_LATENCY;
+ s->fixed_latency = 0;
+ } else if (s->fixed_latency <= 0)
+ s->fixed_latency = DEFAULT_FIXED_LATENCY;
+
+ s->monitor_source->fixed_latency = s->fixed_latency;
pa_assert_se(sink_set_state(s, PA_SINK_IDLE) == 0);
@@ -515,8 +523,12 @@ pa_queue *pa_sink_move_all_start(pa_sink *s) {
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
+ pa_sink_input_ref(i);
+
if (pa_sink_input_start_move(i) >= 0)
- pa_queue_push(q, pa_sink_input_ref(i));
+ pa_queue_push(q, i);
+ else
+ pa_sink_input_unref(i);
}
return q;
@@ -984,6 +996,46 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {
return usec;
}
+static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) {
+ unsigned c;
+
+ pa_sink_input_assert_ref(i);
+ pa_assert(new_volume->channels == i->sample_spec.channels);
+
+ /*
+ * This basically calculates:
+ *
+ * i->relative_volume := i->virtual_volume / new_volume
+ * i->soft_volume := i->relative_volume * i->volume_factor
+ */
+
+ /* The new sink volume passed in here must already be remapped to
+ * the sink input's channel map! */
+
+ i->soft_volume.channels = i->sample_spec.channels;
+
+ for (c = 0; c < i->sample_spec.channels; c++)
+
+ if (new_volume->values[c] <= PA_VOLUME_MUTED)
+ /* We leave i->relative_volume untouched */
+ i->soft_volume.values[c] = PA_VOLUME_MUTED;
+ else {
+ i->relative_volume[c] =
+ pa_sw_volume_to_linear(i->virtual_volume.values[c]) /
+ pa_sw_volume_to_linear(new_volume->values[c]);
+
+ i->soft_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(i->volume_factor.values[c]));
+ }
+
+ /* Hooks have the ability to play games with i->soft_volume */
+ pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+
+ /* We don't copy the soft_volume to the thread_info data
+ * here. That must be done by the caller */
+}
+
/* Called from main thread */
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
pa_sink_input *i;
@@ -998,7 +1050,7 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
* might need to fix up the sink volume accordingly. Please note
* that we don't actually update the sinks volume here, we only
* return how it needs to be updated. The caller should then call
- * pa_sink_set_flat_volume().*/
+ * pa_sink_set_volume().*/
if (pa_idxset_isempty(s->inputs)) {
/* In the special case that we have no sink input we leave the
@@ -1030,26 +1082,22 @@ void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) {
remapped_new_volume = *new_volume;
pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
- pa_sw_cvolume_divide(&i->soft_volume, &i->virtual_volume, &remapped_new_volume);
- pa_sw_cvolume_multiply(&i->soft_volume, &i->soft_volume, &i->volume_factor);
-
- /* Hooks have the ability to play games with i->soft_volume */
- pa_hook_fire(&s->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i);
+ compute_new_soft_volume(i, &remapped_new_volume);
- /* We don't issue PA_SINK_INPUT_MESSAGE_SET_VOLUME because
- * we want the update to have atomically with the sink
- * volume update, hence we do it within the
- * pa_sink_set_flat_volume() call below*/
+ /* We don't copy soft_volume to the thread_info data here
+ * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we
+ * want the update to be atomically with the sink volume
+ * update, hence we do it within the pa_sink_set_volume() call
+ * below */
}
}
/* Called from main thread */
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
+void pa_sink_propagate_flat_volume(pa_sink *s) {
pa_sink_input *i;
uint32_t idx;
pa_sink_assert_ref(s);
- pa_assert(old_volume);
pa_assert(PA_SINK_IS_LINKED(s->state));
pa_assert(s->flags & PA_SINK_FLAT_VOLUME);
@@ -1058,39 +1106,43 @@ void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume) {
* sink input volumes accordingly */
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
- pa_cvolume remapped_old_volume, remapped_new_volume, fixed_volume;
+ pa_cvolume sink_volume, new_virtual_volume;
unsigned c;
- remapped_new_volume = s->virtual_volume;
- pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map);
+ /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume */
- remapped_old_volume = *old_volume;
- pa_cvolume_remap(&remapped_old_volume, &s->channel_map, &i->channel_map);
+ sink_volume = s->virtual_volume;
+ pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map);
for (c = 0; c < i->sample_spec.channels; c++)
+ new_virtual_volume.values[c] = pa_sw_volume_from_linear(
+ i->relative_volume[c] *
+ pa_sw_volume_to_linear(sink_volume.values[c]));
- if (remapped_old_volume.values[c] == PA_VOLUME_MUTED)
- fixed_volume.values[c] = PA_VOLUME_MUTED;
- else
- fixed_volume.values[c] = (pa_volume_t)
- ((uint64_t) i->virtual_volume.values[c] *
- (uint64_t) remapped_new_volume.values[c] /
- (uint64_t) remapped_old_volume.values[c]);
+ new_virtual_volume.channels = i->sample_spec.channels;
- fixed_volume.channels = i->virtual_volume.channels;
+ if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) {
+ i->virtual_volume = new_virtual_volume;
- if (!pa_cvolume_equal(&fixed_volume, &i->virtual_volume)) {
- i->virtual_volume = fixed_volume;
+ /* Hmm, the soft volume might no longer actually match
+ * what has been chosen as new virtual volume here,
+ * especially when the old volume was
+ * PA_VOLUME_MUTED. Hence let's recalculate the soft
+ * volumes here. */
+ compute_new_soft_volume(i, &sink_volume);
/* The virtual volume changed, let's tell people so */
pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);
}
}
+
+ /* If the soft_volume of any of the sink inputs got changed, let's
+ * make sure the thread copies are synced up. */
+ pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);
}
/* Called from main thread */
void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg) {
- pa_cvolume old_virtual_volume;
pa_bool_t virtual_volume_changed;
pa_sink_assert_ref(s);
@@ -1099,14 +1151,13 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
pa_assert(pa_cvolume_valid(volume));
pa_assert(pa_cvolume_compatible(volume, &s->sample_spec));
- old_virtual_volume = s->virtual_volume;
+ virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
s->virtual_volume = *volume;
- virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
/* Propagate this volume change back to the inputs */
if (virtual_volume_changed)
if (propagate && (s->flags & PA_SINK_FLAT_VOLUME))
- pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
if (s->set_volume) {
/* If we have a function set_volume(), then we do not apply a
@@ -1157,7 +1208,7 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {
if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) {
if (s->flags & PA_SINK_FLAT_VOLUME)
- pa_sink_propagate_flat_volume(s, &old_virtual_volume);
+ pa_sink_propagate_flat_volume(s);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -1565,9 +1616,13 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
pa_sink_request_rewind(s, (size_t) -1);
}
- if (s->flags & PA_SINK_FLAT_VOLUME)
- sync_input_volumes_within_thread(s);
+ if (!(s->flags & PA_SINK_FLAT_VOLUME))
+ return 0;
+
+ /* Fall through ... */
+ case PA_SINK_MESSAGE_SYNC_VOLUMES:
+ sync_input_volumes_within_thread(s);
return 0;
case PA_SINK_MESSAGE_GET_VOLUME:
@@ -1585,7 +1640,11 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
case PA_SINK_MESSAGE_GET_MUTE:
return 0;
- case PA_SINK_MESSAGE_SET_STATE:
+ case PA_SINK_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SINK_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SINK_SUSPENDED);
s->thread_info.state = PA_PTR_TO_UINT(userdata);
@@ -1594,7 +1653,17 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse
s->thread_info.rewind_requested = FALSE;
}
+ if (suspend_change) {
+ pa_sink_input *i;
+ void *state = NULL;
+
+ while ((i = pa_hashmap_iterate(s->thread_info.inputs, &state, NULL)))
+ if (i->suspend_within_thread)
+ i->suspend_within_thread(i, s->thread_info.state == PA_SINK_SUSPENDED);
+ }
+
return 0;
+ }
case PA_SINK_MESSAGE_DETACH:
@@ -1763,6 +1832,9 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
@@ -1778,13 +1850,8 @@ pa_usec_t pa_sink_get_requested_latency_within_thread(pa_sink *s) {
(result == (pa_usec_t) -1 || result > monitor_latency))
result = monitor_latency;
- if (result != (pa_usec_t) -1) {
- if (result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
-
- if (result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
- }
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
s->thread_info.requested_latency = result;
s->thread_info.requested_latency_valid = TRUE;
@@ -1873,6 +1940,9 @@ void pa_sink_invalidate_requested_latency(pa_sink *s) {
pa_sink_assert_ref(s);
+ if (!(s->flags & PA_SINK_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
if (PA_SINK_IS_LINKED(s->thread_info.state)) {
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 634bf3ef..cb4697f9 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -74,7 +74,8 @@ struct pa_sink {
pa_volume_t base_volume; /* shall be constant */
unsigned n_volume_steps; /* shall be constant */
- pa_cvolume virtual_volume, soft_volume;
+ pa_cvolume virtual_volume; /* The volume clients are informed about */
+ pa_cvolume soft_volume; /* The internal software volume we apply to all PCM data while it passes through */
pa_bool_t muted:1;
pa_bool_t refresh_volume:1;
@@ -85,6 +86,8 @@ struct pa_sink {
pa_memchunk silence;
+ pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -159,6 +162,7 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_REMOVE_INPUT,
PA_SINK_MESSAGE_GET_VOLUME,
PA_SINK_MESSAGE_SET_VOLUME,
+ PA_SINK_MESSAGE_SYNC_VOLUMES,
PA_SINK_MESSAGE_GET_MUTE,
PA_SINK_MESSAGE_SET_MUTE,
PA_SINK_MESSAGE_GET_LATENCY,
@@ -249,7 +253,7 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend);
int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend);
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
-void pa_sink_propagate_flat_volume(pa_sink *s, const pa_cvolume *old_volume);
+void pa_sink_propagate_flat_volume(pa_sink *s);
void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);
diff --git a/src/pulsecore/socket-server.c b/src/pulsecore/socket-server.c
index 8147b27f..e660700c 100644
--- a/src/pulsecore/socket-server.c
+++ b/src/pulsecore/socket-server.c
@@ -536,6 +536,7 @@ char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) {
return NULL;
pa_snprintf(c, l, "{%s}unix:%s", id, s->filename);
+ pa_xfree(id);
return c;
}
diff --git a/src/pulsecore/source-output.c b/src/pulsecore/source-output.c
index 1c37be93..3ee26735 100644
--- a/src/pulsecore/source-output.c
+++ b/src/pulsecore/source-output.c
@@ -87,6 +87,7 @@ static void reset_callbacks(pa_source_output *o) {
o->attach = NULL;
o->detach = NULL;
o->suspend = NULL;
+ o->suspend_within_thread = NULL;
o->moving = NULL;
o->kill = NULL;
o->get_latency = NULL;
@@ -519,6 +520,9 @@ void pa_source_output_update_max_rewind(pa_source_output *o, size_t nbytes /* i
pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output *o, pa_usec_t usec) {
pa_source_output_assert_ref(o);
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->fixed_latency;
+
if (usec != (pa_usec_t) -1)
usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
@@ -530,8 +534,6 @@ pa_usec_t pa_source_output_set_requested_latency_within_thread(pa_source_output
/* Called from main context */
pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) {
- pa_usec_t min_latency, max_latency;
-
pa_source_output_assert_ref(o);
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) {
@@ -543,10 +545,14 @@ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t
* have to touch the thread info data directly */
if (o->source) {
- pa_source_get_latency_range(o->source, &min_latency, &max_latency);
+ if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ usec = o->source->fixed_latency;
- if (usec != (pa_usec_t) -1)
+ if (usec != (pa_usec_t) -1) {
+ pa_usec_t min_latency, max_latency;
+ pa_source_get_latency_range(o->source, &min_latency, &max_latency);
usec = PA_CLAMP(usec, min_latency, max_latency);
+ }
}
o->thread_info.requested_source_latency = usec;
@@ -704,6 +710,8 @@ int pa_source_output_start_move(pa_source_output *o) {
pa_source_update_status(o->source);
o->source = NULL;
+ pa_source_output_unref(o);
+
return 0;
}
@@ -751,7 +759,7 @@ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t
o->source = dest;
o->save_source = save;
- pa_idxset_put(o->source->outputs, o, NULL);
+ pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL);
if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED)
o->source->n_corked++;
@@ -803,11 +811,19 @@ int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t sav
if (!pa_source_output_may_move_to(o, dest))
return -PA_ERR_NOTSUPPORTED;
- if ((r = pa_source_output_start_move(o)) < 0)
+ pa_source_output_ref(o);
+
+ if ((r = pa_source_output_start_move(o)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
- if ((r = pa_source_output_finish_move(o, dest, save)) < 0)
+ if ((r = pa_source_output_finish_move(o, dest, save)) < 0) {
+ pa_source_output_unref(o);
return r;
+ }
+
+ pa_source_output_unref(o);
return 0;
}
diff --git a/src/pulsecore/source-output.h b/src/pulsecore/source-output.h
index 9f5f7744..9824e160 100644
--- a/src/pulsecore/source-output.h
+++ b/src/pulsecore/source-output.h
@@ -120,6 +120,10 @@ struct pa_source_output {
* to suspends or resumes. Called from main context */
void (*suspend) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+ /* If non-NULL called whenever the source this output is attached
+ * to suspends or resumes. Called from IO context */
+ void (*suspend_within_thread) (pa_source_output *o, pa_bool_t b); /* may be NULL */
+
/* If non-NULL called whenever the source output is moved to a new
* source. Called from main context after the stream was detached
* from the old source and before it is attached to the new
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 252e23ab..21902509 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -43,6 +43,7 @@
#define ABSOLUTE_MIN_LATENCY (500)
#define ABSOLUTE_MAX_LATENCY (10*PA_USEC_PER_SEC)
+#define DEFAULT_FIXED_LATENCY (250*PA_USEC_PER_MSEC)
static PA_DEFINE_CHECK_TYPE(pa_source, pa_msgobject);
@@ -199,6 +200,8 @@ pa_source* pa_source_new(
s->muted = data->muted;
s->refresh_volume = s->refresh_muted = FALSE;
+ s->fixed_latency = flags & PA_SOURCE_DYNAMIC_LATENCY ? 0 : DEFAULT_FIXED_LATENCY;
+
reset_callbacks(s);
s->userdata = NULL;
@@ -303,8 +306,7 @@ void pa_source_put(pa_source *s) {
/* The following fields must be initialized properly when calling _put() */
pa_assert(s->asyncmsgq);
pa_assert(s->rtpoll);
- pa_assert(!s->thread_info.min_latency || !s->thread_info.max_latency ||
- s->thread_info.min_latency <= s->thread_info.max_latency);
+ pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency);
if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL)) {
s->flags |= PA_SOURCE_DECIBEL_VOLUME;
@@ -316,6 +318,11 @@ void pa_source_put(pa_source *s) {
if (s->flags & PA_SOURCE_DECIBEL_VOLUME)
s->n_volume_steps = PA_VOLUME_NORM+1;
+ if (s->flags & PA_SOURCE_DYNAMIC_LATENCY)
+ s->fixed_latency = 0;
+ else if (s->fixed_latency <= 0)
+ s->fixed_latency = DEFAULT_FIXED_LATENCY;
+
pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0);
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE | PA_SUBSCRIPTION_EVENT_NEW, s->index);
@@ -466,8 +473,12 @@ pa_queue *pa_source_move_all_start(pa_source *s) {
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
+ pa_source_output_ref(o);
+
if (pa_source_output_start_move(o) >= 0)
- pa_queue_push(q, pa_source_output_ref(o));
+ pa_queue_push(q, o);
+ else
+ pa_source_output_unref(o);
}
return q;
@@ -933,9 +944,26 @@ int pa_source_process_msg(pa_msgobject *object, int code, void *userdata, int64_
case PA_SOURCE_MESSAGE_GET_MUTE:
return 0;
- case PA_SOURCE_MESSAGE_SET_STATE:
+ case PA_SOURCE_MESSAGE_SET_STATE: {
+
+ pa_bool_t suspend_change =
+ (s->thread_info.state == PA_SOURCE_SUSPENDED && PA_SOURCE_IS_OPENED(PA_PTR_TO_UINT(userdata))) ||
+ (PA_SOURCE_IS_OPENED(s->thread_info.state) && PA_PTR_TO_UINT(userdata) == PA_SOURCE_SUSPENDED);
+
s->thread_info.state = PA_PTR_TO_UINT(userdata);
+
+ if (suspend_change) {
+ pa_source_output *o;
+ void *state = NULL;
+
+ while ((o = pa_hashmap_iterate(s->thread_info.outputs, &state, NULL)))
+ if (o->suspend_within_thread)
+ o->suspend_within_thread(o, s->thread_info.state == PA_SOURCE_SUSPENDED);
+ }
+
+
return 0;
+ }
case PA_SOURCE_MESSAGE_DETACH:
@@ -1075,6 +1103,9 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return PA_CLAMP(s->fixed_latency, s->thread_info.min_latency, s->thread_info.max_latency);
+
if (s->thread_info.requested_latency_valid)
return s->thread_info.requested_latency;
@@ -1084,13 +1115,8 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
(result == (pa_usec_t) -1 || result > o->thread_info.requested_source_latency))
result = o->thread_info.requested_source_latency;
- if (result != (pa_usec_t) -1) {
- if (s->thread_info.max_latency > 0 && result > s->thread_info.max_latency)
- result = s->thread_info.max_latency;
-
- if (s->thread_info.min_latency > 0 && result < s->thread_info.min_latency)
- result = s->thread_info.min_latency;
- }
+ if (result != (pa_usec_t) -1)
+ result = PA_CLAMP(result, s->thread_info.min_latency, s->thread_info.max_latency);
s->thread_info.requested_latency = result;
s->thread_info.requested_latency_valid = TRUE;
@@ -1100,7 +1126,7 @@ pa_usec_t pa_source_get_requested_latency_within_thread(pa_source *s) {
/* Called from main thread */
pa_usec_t pa_source_get_requested_latency(pa_source *s) {
- pa_usec_t usec;
+ pa_usec_t usec = 0;
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
@@ -1148,6 +1174,9 @@ void pa_source_invalidate_requested_latency(pa_source *s) {
pa_source_assert_ref(s);
+ if (!(s->flags & PA_SOURCE_DYNAMIC_LATENCY))
+ return;
+
s->thread_info.requested_latency_valid = FALSE;
if (PA_SOURCE_IS_LINKED(s->thread_info.state)) {
@@ -1217,7 +1246,7 @@ void pa_source_get_latency_range(pa_source *s, pa_usec_t *min_latency, pa_usec_t
}
}
-/* Called from IO thread, and from main thread before pa_sink_put() is called */
+/* Called from IO thread, and from main thread before pa_source_put() is called */
void pa_source_set_latency_range_within_thread(pa_source *s, pa_usec_t min_latency, pa_usec_t max_latency) {
void *state = NULL;
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 652783ef..b502c228 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -87,6 +87,8 @@ struct pa_source {
pa_memchunk silence;
+ pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
diff --git a/src/pulsecore/time-smoother.c b/src/pulsecore/time-smoother.c
index 55ac8687..9d5a0705 100644
--- a/src/pulsecore/time-smoother.c
+++ b/src/pulsecore/time-smoother.c
@@ -291,7 +291,8 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
pa_assert(s);
pa_assert(y);
- if (!s->smoothing || x >= s->px) {
+ if (x >= s->px) {
+ /* Linear interpolation right from px */
int64_t t;
/* The requested point is right of the point where we wanted
@@ -307,7 +308,22 @@ static void estimate(pa_smoother *s, pa_usec_t x, pa_usec_t *y, double *deriv) {
if (deriv)
*deriv = s->dp;
+ } else if (x <= s->ex) {
+ /* Linear interpolation left from ex */
+ int64_t t;
+
+ t = (int64_t) s->ey - (int64_t) llrint(s->de * (double) (s->ex - x));
+
+ if (t < 0)
+ t = 0;
+
+ *y = (pa_usec_t) t;
+
+ if (deriv)
+ *deriv = s->de;
+
} else {
+ /* Spline interpolation between ex and px */
double tx, ty;
/* Ok, we're not yet on track, thus let's interpolate, and
@@ -381,7 +397,9 @@ void pa_smoother_put(pa_smoother *s, pa_usec_t x, pa_usec_t y) {
s->abc_valid = FALSE;
-/* pa_log_debug("put(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, put(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
}
pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
@@ -412,7 +430,9 @@ pa_usec_t pa_smoother_get(pa_smoother *s, pa_usec_t x) {
s->last_y = y;
}
-/* pa_log_debug("get(%llu | %llu) = %llu", (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y); */
+#ifdef DEBUG_DATA
+ pa_log_debug("%p, get(%llu | %llu) = %llu", s, (unsigned long long) (x + s->time_offset), (unsigned long long) x, (unsigned long long) y);
+#endif
return y;
}
@@ -422,7 +442,9 @@ void pa_smoother_set_time_offset(pa_smoother *s, pa_usec_t offset) {
s->time_offset = offset;
-/* pa_log_debug("offset(%llu)", (unsigned long long) offset); */
+#ifdef DEBUG_DATA
+ pa_log_debug("offset(%llu)", (unsigned long long) offset);
+#endif
}
void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
@@ -431,7 +453,9 @@ void pa_smoother_pause(pa_smoother *s, pa_usec_t x) {
if (s->paused)
return;
-/* pa_log_debug("pause(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("pause(%llu)", (unsigned long long) x);
+#endif
s->paused = TRUE;
s->pause_time = x;
@@ -446,7 +470,9 @@ void pa_smoother_resume(pa_smoother *s, pa_usec_t x, pa_bool_t fix_now) {
if (x < s->pause_time)
x = s->pause_time;
-/* pa_log_debug("resume(%llu)", (unsigned long long) x); */
+#ifdef DEBUG_DATA
+ pa_log_debug("resume(%llu)", (unsigned long long) x);
+#endif
s->paused = FALSE;
s->time_offset += x - s->pause_time;
@@ -481,7 +507,9 @@ pa_usec_t pa_smoother_translate(pa_smoother *s, pa_usec_t x, pa_usec_t y_delay)
if (s->dp > nde)
nde = s->dp;
-/* pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde); */
+#ifdef DEBUG_DATA
+ pa_log_debug("translate(%llu) = %llu (%0.2f)", (unsigned long long) y_delay, (unsigned long long) ((double) y_delay / nde), nde);
+#endif
return (pa_usec_t) llrint((double) y_delay / nde);
}