summaryrefslogtreecommitdiffstats
path: root/src/pulsecore/sink.h
diff options
context:
space:
mode:
authorJyri Sarha <jyri.sarha@nokia.com>2010-10-15 13:05:14 +0300
committerColin Guthrie <cguthrie@mandriva.org>2010-10-16 11:53:38 +0100
commit5391daf8dfcae45c6abb74c0a5ff9b7c50e3c000 (patch)
tree15c43282a7b2be2d4fc2a73b251d5ec90926236c /src/pulsecore/sink.h
parent179b291b18b9a7366955948ce0ddfb2e65a6b66e (diff)
core: Add infrastructure for synchronizing HW and SW volume changes
To make concurrent use of SW and HW volume glitchles their application needs to be synchronized. For accurate synchronization the HW volume needs to be applied in IO thread. This patch adds infrastructure to delay the applying of HW volume to match with SW volume timing. To avoid synchronization problems this patch moves many of the volume and mute related functions from main thread to IO thread. All these changes become active only if the sync volume flag for a sink has been set. So, for this patch to have any effect it needs to be taken into use by sink implementor. Signed-off-by: Jyri Sarha <jyri.sarha@nokia.com> Reviewed-by: Tanu Kaskinen <tanu.kaskinen@digia.com> Reviewd-by: Colin Guthrie <cguthrie@mandriva.org>
Diffstat (limited to 'src/pulsecore/sink.h')
-rw-r--r--src/pulsecore/sink.h97
1 files changed, 83 insertions, 14 deletions
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index ba547fc3..4d569ddc 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -25,6 +25,7 @@
typedef struct pa_sink pa_sink;
typedef struct pa_device_port pa_device_port;
+typedef struct pa_sink_volume_change pa_sink_volume_change;
#include <inttypes.h>
@@ -116,26 +117,72 @@ struct pa_sink {
* inhibited */
int (*set_state)(pa_sink *s, pa_sink_state_t state); /* may be NULL */
- /* Callled when the volume is queried. Called from main loop
- * context. If this is NULL a PA_SINK_MESSAGE_GET_VOLUME message
- * will be sent to the IO thread instead. If refresh_volume is
- * FALSE neither this function is called nor a message is sent. */
+ /* Sink drivers that support hardware volume may set this
+ * callback. This is called when the current volume needs to be
+ * re-read from the hardware.
+ *
+ * There are two ways for drivers to implement hardware volume
+ * query: either set this callback or handle
+ * PA_SINK_MESSAGE_GET_VOLUME. The callback implementation or the
+ * message handler must update s->real_volume and s->soft_volume
+ * (using pa_sink_set_soft_volume()) to match the current hardware
+ * volume.
+ *
+ * If PA_SINK_SYNC_VOLUME is not set, then this is called from the
+ * main thread before sending PA_SINK_MESSAGE_GET_VOLUME, so in
+ * this case the driver can choose whether to read the volume from
+ * the hardware in the main thread or in the IO thread.
+ *
+ * If PA_SINK_SYNC_VOLUME is set, then this is called from the IO
+ * thread within the default handler for
+ * PA_SINK_MESSAGE_GET_VOLUME (the main thread is waiting while
+ * the message is being processed), so there's no choice of where
+ * to do the volume reading - it has to be done in the IO thread
+ * always. */
void (*get_volume)(pa_sink *s); /* may be NULL */
- /* Called when the volume shall be changed. Called from main loop
- * context. If this is NULL a PA_SINK_MESSAGE_SET_VOLUME message
- * will be sent to the IO thread instead. */
+ /* Sink drivers that support hardware volume must set this
+ * callback. This is called when the hardware volume needs to be
+ * updated.
+ *
+ * If PA_SINK_SYNC_VOLUME is not set, then this is called from the
+ * main thread. The callback implementation must set the hardware
+ * volume according to s->real_volume. If the driver can't set the
+ * hardware volume to the exact requested value, it has to update
+ * s->real_volume and/or s->soft_volume so that they together
+ * match the actual hardware volume that was set.
+ *
+ * If PA_SINK_SYNC_VOLUME is set, then this is called from the IO
+ * thread. The callback implementation must not actually set the
+ * hardware volume yet, but it must check how close to the
+ * requested volume the hardware volume can be set, and update
+ * s->real_volume and/or s->soft_volume so that they together
+ * match the actual hardware volume that will be set later in the
+ * write_volume callback. */
void (*set_volume)(pa_sink *s); /* dito */
- /* Called when the mute setting is queried. Called from main loop
- * context. If this is NULL a PA_SINK_MESSAGE_GET_MUTE message
- * will be sent to the IO thread instead. If refresh_mute is
- * FALSE neither this function is called nor a message is sent.*/
+ /* Sink drivers that set PA_SINK_SYNC_VOLUME must provide this
+ * callback. This callback is not used with sinks that do not set
+ * PA_SINK_SYNC_VOLUME. This is called from the IO thread when a
+ * pending hardware volume change has to be written to the
+ * hardware. The requested volume is passed to the callback
+ * implementation in s->thread_info.current_hw_volume.
+ *
+ * The call is done inside pa_sink_volume_change_apply(), which is
+ * not called automatically - it is the driver's responsibility to
+ * schedule that function to be called at the right times in the
+ * IO thread. */
+ void (*write_volume)(pa_sink *s); /* dito */
+
+ /* Called when the mute setting is queried. A PA_SINK_MESSAGE_GET_MUTE
+ * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME
+ * flag is set otherwise from main loop context. If refresh_mute is FALSE
+ * neither this function is called nor a message is sent.*/
void (*get_mute)(pa_sink *s); /* dito */
- /* Called when the mute setting shall be changed. Called from main
- * loop context. If this is NULL a PA_SINK_MESSAGE_SET_MUTE
- * message will be sent to the IO thread instead. */
+ /* Called when the mute setting shall be changed. A PA_SINK_MESSAGE_SET_MUTE
+ * message will also be sent. Called from IO thread if PA_SINK_SYNC_VOLUME
+ * flag is set otherwise from main loop context. */
void (*set_mute)(pa_sink *s); /* dito */
/* Called when a rewind request is issued. Called from IO thread
@@ -188,6 +235,21 @@ struct pa_sink {
* decided on by the sink, and the clients have no influence
* in changing it */
pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
+
+ /* Delayed volume change events are queued here. The events
+ * are stored in expiration order. The one expiring next is in
+ * the head of the list. */
+ PA_LLIST_HEAD(pa_sink_volume_change, volume_changes);
+ pa_sink_volume_change *volume_changes_tail;
+ /* This value is updated in pa_sink_volume_change_apply() and
+ * used only by sinks with PA_SINK_SYNC_VOLUME. */
+ pa_cvolume current_hw_volume;
+
+ /* The amount of usec volume up events are delayed and volume
+ * down events are made earlier. */
+ uint32_t volume_change_safety_margin;
+ /* Usec delay added to all volume change events, may be negative. */
+ int32_t volume_change_extra_delay;
} thread_info;
void *userdata;
@@ -200,6 +262,7 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_ADD_INPUT,
PA_SINK_MESSAGE_REMOVE_INPUT,
PA_SINK_MESSAGE_GET_VOLUME,
+ PA_SINK_MESSAGE_SET_VOLUME_SYNCED,
PA_SINK_MESSAGE_SET_VOLUME,
PA_SINK_MESSAGE_SYNC_VOLUMES,
PA_SINK_MESSAGE_GET_MUTE,
@@ -219,6 +282,8 @@ typedef enum pa_sink_message {
PA_SINK_MESSAGE_GET_MAX_REQUEST,
PA_SINK_MESSAGE_SET_MAX_REWIND,
PA_SINK_MESSAGE_SET_MAX_REQUEST,
+ PA_SINK_MESSAGE_SET_PORT,
+ PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE,
PA_SINK_MESSAGE_MAX
} pa_sink_message_t;
@@ -349,6 +414,10 @@ void pa_sink_set_max_request_within_thread(pa_sink *s, size_t max_request);
void pa_sink_set_latency_range_within_thread(pa_sink *s, pa_usec_t min_latency, pa_usec_t max_latency);
void pa_sink_set_fixed_latency_within_thread(pa_sink *s, pa_usec_t latency);
+void pa_sink_update_volume_and_mute(pa_sink *s);
+
+pa_bool_t pa_sink_volume_change_apply(pa_sink *s, pa_usec_t *usec_to_next);
+
/*** To be called exclusively by sink input drivers, from IO context */
void pa_sink_request_rewind(pa_sink*s, size_t nbytes);