From: Arun Raghavan <git@xxxxxxxxxxxxxxxx> This makes it easier for clients to deal with the volume of a stream (they currently have to use pa_context_set_sink_input_volume(), which is a bit clunky given everything else is based around the pa_stream_* API). In addition, this will also provide us a bit of context with respect to whether the client is controlling the stream volume from a "stream" perspective (mixer within an app) or a "global" perspective (a mixer app). --- src/map-file | 1 + src/pulse/internal.h | 3 +++ src/pulse/stream.c | 64 ++++++++++++++++++++++++++++++++++++++++++++-------- src/pulse/stream.h | 9 ++++++++ 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/src/map-file b/src/map-file index 93a62b8..93f7178 100644 --- a/src/map-file +++ b/src/map-file @@ -326,6 +326,7 @@ pa_stream_set_started_callback; pa_stream_set_state_callback; pa_stream_set_suspended_callback; pa_stream_set_underflow_callback; +pa_stream_set_volume; pa_stream_set_write_callback; pa_stream_trigger; pa_stream_unref; diff --git a/src/pulse/internal.h b/src/pulse/internal.h index eefd181..74b2b32 100644 --- a/src/pulse/internal.h +++ b/src/pulse/internal.h @@ -151,6 +151,9 @@ struct pa_stream { pa_proplist *proplist; + pa_cvolume volume; + bool volume_set; + bool channel_valid:1; bool suspended:1; bool corked:1; diff --git a/src/pulse/stream.c b/src/pulse/stream.c index 6fea996..0f5dd73 100644 --- a/src/pulse/stream.c +++ b/src/pulse/stream.c @@ -196,6 +196,9 @@ static pa_stream *pa_stream_new_with_proplist_internal( s->smoother = NULL; + pa_cvolume_init(&s->volume); + s->volume_set = false; + /* Refcounting is strictly one-way: from the "bigger" to the "smaller" object. */ PA_LLIST_PREPEND(pa_stream, c->streams, s); pa_stream_ref(s); @@ -1191,8 +1194,6 @@ static int create_stream( pa_tagstruct *t; uint32_t tag; - bool volume_set = !!volume; - pa_cvolume cv; uint32_t i; pa_assert(s); @@ -1286,14 +1287,19 @@ static int create_stream( PA_TAG_BOOLEAN, s->corked, PA_TAG_INVALID); - if (!volume) { + if (volume) { + s->volume = *volume; + s->volume_set = true; + } + + if (!s->volume_set) { if (pa_sample_spec_valid(&s->sample_spec)) - volume = pa_cvolume_reset(&cv, s->sample_spec.channels); + pa_cvolume_reset(&s->volume, s->sample_spec.channels); else { /* This is not really relevant, since no volume was set, and * the real number of channels is embedded in the format_info * structure */ - volume = pa_cvolume_reset(&cv, PA_CHANNELS_MAX); + pa_cvolume_reset(&s->volume, PA_CHANNELS_MAX); } } @@ -1306,7 +1312,7 @@ static int create_stream( PA_TAG_U32, s->syncid, PA_TAG_INVALID); - pa_tagstruct_put_cvolume(t, volume); + pa_tagstruct_put_cvolume(t, &s->volume); } else pa_tagstruct_putu32(t, s->buffer_attr.fragsize); @@ -1343,7 +1349,7 @@ static int create_stream( if (s->context->version >= 14) { if (s->direction == PA_STREAM_PLAYBACK) - pa_tagstruct_put_boolean(t, volume_set); + pa_tagstruct_put_boolean(t, s->volume_set); pa_tagstruct_put_boolean(t, flags & PA_STREAM_EARLY_REQUESTS); } @@ -1372,9 +1378,9 @@ static int create_stream( } if (s->context->version >= 22 && s->direction == PA_STREAM_RECORD) { - pa_tagstruct_put_cvolume(t, volume); + pa_tagstruct_put_cvolume(t, &s->volume); pa_tagstruct_put_boolean(t, flags & PA_STREAM_START_MUTED); - pa_tagstruct_put_boolean(t, volume_set); + pa_tagstruct_put_boolean(t, s->volume_set); pa_tagstruct_put_boolean(t, flags & (PA_STREAM_START_MUTED|PA_STREAM_START_UNMUTED)); pa_tagstruct_put_boolean(t, flags & PA_STREAM_RELATIVE_VOLUME); pa_tagstruct_put_boolean(t, flags & (PA_STREAM_PASSTHROUGH)); @@ -2922,3 +2928,43 @@ uint32_t pa_stream_get_monitor_stream(pa_stream *s) { return s->direct_on_input; } + +int pa_stream_set_volume(pa_stream *s, pa_cvolume *v, pa_stream_success_cb_t cb, void *userdata) { + pa_operation *o; + pa_tagstruct *t; + uint32_t tag; + + pa_assert(s); + pa_assert(v); + pa_assert(PA_REFCNT_VALUE(s) >= 1); + + PA_CHECK_VALIDITY(s->context, !pa_detect_fork(), PA_ERR_FORKED); + PA_CHECK_VALIDITY(s->context, s->state != PA_STREAM_FAILED && s->state != PA_STREAM_TERMINATED, PA_ERR_BADSTATE); + PA_CHECK_VALIDITY(s->context, userdata == NULL || cb != NULL, PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, s->direction != PA_STREAM_UPLOAD, PA_ERR_INVALID); /* TODO: do we want to support this? */ + PA_CHECK_VALIDITY(s->context, pa_cvolume_valid(v), PA_ERR_INVALID); + PA_CHECK_VALIDITY(s->context, pa_cvolume_compatible(v, &s->sample_spec), PA_ERR_INVALID); + + if (s->state == PA_STREAM_UNCONNECTED) { + /* Just cache the value till the stream is being connected */ + s->volume = *v; + s->volume_set = true; + + if (cb) + cb(s, 1, userdata); + + return 0; + } + + o = pa_operation_new(s->context, s, (pa_operation_cb_t) cb, userdata); + + t = pa_tagstruct_command(s->context, + s->direction == PA_STREAM_PLAYBACK ? PA_COMMAND_SET_SINK_INPUT_VOLUME : PA_COMMAND_SET_SOURCE_OUTPUT_VOLUME, + &tag); + pa_tagstruct_putu32(t, s->stream_index); + pa_tagstruct_put_cvolume(t, v); + pa_pstream_send_tagstruct(s->context->pstream, t); + pa_pdispatch_register_reply(s->context->pdispatch, tag, DEFAULT_TIMEOUT, pa_stream_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref); + + return 0; +} diff --git a/src/pulse/stream.h b/src/pulse/stream.h index 70fa415..cc74629 100644 --- a/src/pulse/stream.h +++ b/src/pulse/stream.h @@ -819,6 +819,15 @@ int pa_stream_set_monitor_stream(pa_stream *s, uint32_t sink_input_idx); * \since 0.9.11 */ uint32_t pa_stream_get_monitor_stream(pa_stream *s); +/** Set the volume on the given stream. The operation is asynchronous and a + * callback may optionally be provided to be invoked when the volume has been + * set. + * + * Returns 0 on success, negative error value on failure. + * + * \since 9.0 */ +int pa_stream_set_volume(pa_stream *s, pa_cvolume *v, pa_stream_success_cb_t cb, void *userdata); + PA_C_DECL_END #endif -- 2.5.0