Hi, Review below: 'Twas brillig, and oku at iki.fi at 13/10/10 17:40 did gyre and gimble: > + PA_SINK_SYNC_VOLUME = 0x0200U, > + /**< The HW volume changes are syncronized with SW volume. > + * \since X.X.XX */ > + > } pa_sink_flags_t; Can you put 0.9.22 here? If needed I'll do a global find and replace on any \since 0.9.22's if/when we release that version from stable queue instead, but I'll almost certainly forget to fix up x.x.xx replacements. > diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c > index ff4cc17..0f2af4f 100644 > --- a/src/pulsecore/sink.c > +++ b/src/pulsecore/sink.c > +struct sink_message_set_port { > + pa_device_port *port; > + int ret; > +}; > + I'll refer to this below[1] > @@ -1459,7 +1493,8 @@ void pa_sink_set_volume( > * apply one to s->soft_volume */ > > pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); > - s->set_volume(s); > + if (!(s->flags & PA_SINK_SYNC_VOLUME)) > + s->set_volume(s); > > } else > /* If we have no function set_volume(), then the soft volume Hmm, should this "if" have an "else" that sets send_msg to TRUE (send_msg is used just below where the context ends) otherwise if send_msg is set to false, then the message will not be pushed to the asyncq and thus the set_volume() function will never be called in pa_sink_process_msg()[2] > @@ -1474,18 +1509,22 @@ void pa_sink_set_volume( > pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); > } > > -/* Called from main thread. Only to be called by sink implementor */ > +/* Called from the io thread if sync volume is used, otherwise from the main thread. > + * Only to be called by sink implementor */ > void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) { > pa_sink_assert_ref(s); > - pa_assert_ctl_context(); > + if (s->flags & PA_SINK_SYNC_VOLUME) > + pa_sink_assert_io_context(s); > + else > + pa_assert_ctl_context(); > > if (!volume) > pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); > else > s->soft_volume = *volume; > > - if (PA_SINK_IS_LINKED(s->state)) > - pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); > + if (PA_SINK_IS_LINKED(s->state) && !(s->flags & PA_SINK_SYNC_VOLUME)) > + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); > else > s->thread_info.soft_volume = s->soft_volume; > } This is a little concerning. If I have a sink (e.g. an roap-sink) whcih does not support PA_SINK_SYNC_VOLUME flag, then you are now sending me a different message to the one I got before. While there is nothing in the tree that does this, there may be external uses of this in private trees and I don't think there is any need to change the semantics of PA_SINK_MESSAGE_SET_VOLUME. Would it be better to instead define: PA_SINK_MESSAGE_SET_VOLUME_SYNCED to go before PA_SINK_MESSAGE_SET_VOLUME, and not change the message here and instead change the o->process_msg() calls that use PA_SINK_MESSAGE_SET_VOLUME to use PA_SINK_MESSAGE_SET_VOLUME_SYNCED? I think this would be semantically the same with regards to your requirements and keeps any out-of-tree reliance on PA_SINK_MESSAGE_SET_VOLUME message semantically unchanged.[3] > @@ -1555,6 +1594,16 @@ static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) > pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); > } > > +/* Called from io thread */ > +void pa_sink_update_volume_and_mute(pa_sink *s) { > + pa_assert(s); > + pa_sink_assert_io_context(s); > + > + pa_asyncmsgq_post(pa_thread_mq_get()->outq, PA_MSGOBJECT(s), > + PA_SINK_MESSAGE_UPDATE_VOLUME_AND_MUTE, > + NULL, 0, NULL, NULL); > +} > + While the limiting of line length is appreciated, I think most of the other calls are just long lines... so probably worth leaving it on one line for consistency. > @@ -1605,7 +1654,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) { > + if (!(s->flags & PA_SINK_SYNC_VOLUME) && s->set_mute) > @@ -1624,7 +1673,7 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) { > + if (!(s->flags & PA_SINK_SYNC_VOLUME) && s->get_mute) These two, as above. > @@ -2000,6 +2049,14 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse > > case PA_SINK_MESSAGE_SET_VOLUME: > > + if (s->flags & PA_SINK_SYNC_VOLUME) { > + s->set_volume(s); > + pa_sink_volume_change_push(s); > + } > + /* Fall through ... */ [2]: Just for reference, here is where the above could result in s->set_volume() not being called. > @@ -2127,6 +2204,21 @@ int pa_sink_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offse > pa_sink_set_max_request_within_thread(s, (size_t) offset); > return 0; > > + case PA_SINK_MESSAGE_SET_PORT: > + > + pa_assert(userdata); > + if (s->set_port) > + ((struct sink_message_set_port *) userdata)->ret = s->set_port(s, ((struct sink_message_set_port *) userdata)->port); > + return 0; The casting is a bug ugly here. Can you use a variable? Comment about the ->ret assignment below too. > @@ -2568,7 +2660,7 @@ size_t pa_sink_get_max_request(pa_sink *s) { > /* Called from main context */ > int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) { > pa_device_port *port; > - > + int ret; > pa_sink_assert_ref(s); > pa_assert_ctl_context(); > > @@ -2588,7 +2680,14 @@ int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) { > return 0; > } > > - if ((s->set_port(s, port)) < 0) > + if (s->flags & PA_SINK_SYNC_VOLUME) { > + struct sink_message_set_port msg = { .port = port, ret = 0 }; > + pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_PORT, &msg, 0, NULL) == 0); [1]: This looks wrong, but perhaps it's some magic syntax I don't appreciate. AFAICT, ret (the local var) is assigned to 0 in all cases. The actual value of msg.ret is never inspected after calling sending the message. i.e. the value set in the previous hunk above is never actually inspected and pulled back into our local ret value, which leads to... > + } > + else > + ret = s->set_port(s, port); > + > + if (ret < 0) > return -PA_ERR_NOENTITY; ...ret always being 0 in the case of (s->flags & PA_SINK_SYNC_VOLUME) AFAICT. If this is intended then it is better to do the check of ret inside the else itself, but I suspect you want to get the ret value back after sending the message... > diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h > index ba547fc..6b55778 100644 > --- a/src/pulsecore/sink.h > +++ b/src/pulsecore/sink.h > @@ -201,6 +263,7 @@ typedef enum pa_sink_message { > PA_SINK_MESSAGE_REMOVE_INPUT, > PA_SINK_MESSAGE_GET_VOLUME, > PA_SINK_MESSAGE_SET_VOLUME, > + PA_SINK_MESSAGE_SET_SOFT_VOLUME, > PA_SINK_MESSAGE_SYNC_VOLUMES, > PA_SINK_MESSAGE_GET_MUTE, > PA_SINK_MESSAGE_SET_MUTE, [3]: I mentioned this above, but to reinforce that, perhaps this _SOFT_VOLUME should be dropped and a SET_VOLUME_SYNCED inserted above SET_VOLUME.... wont describe why again here, just a reminder in the right place to what I said above :) I think that is all I have to say about this one. Thanks a lot of this patch. I can see it's all very confusing to work with, but I think this is an important fix (it is something people often complain about). Col -- Colin Guthrie gmane(at)colin.guthr.ie http://colin.guthr.ie/ Day Job: Tribalogic Limited [http://www.tribalogic.net/] Open Source: Mageia Contributor [http://www.mageia.org/] PulseAudio Hacker [http://www.pulseaudio.org/] Trac Hacker [http://trac.edgewall.org/]