On Thu, 2011-07-07 at 10:55 +0100, Colin Guthrie wrote: > Some sink flags are really just a product of what callbacks > set on the sink. Rather than assert when the flags don't > match the callbacks registered, just set them automatically. For the hw volume/mute flags the automation is a bit broken: the hw flags are enabled also for virtual devices that use the volume/mute callbacks for something (like adjusting the output stream volume). Many virtual devices seem to set the hw volume/mute flags explicitly for some reason, so this was broken already before, but for another reason... Of course it's not a big deal in this case if the flags don't correspond to the reality, since the flags are mostly useful for debugging purposes, but if the flags are not trustworthy, why have them at all? Just so that the alsa modules can report their status? I'd either make the hw flags non-automatic (but still dynamic) or remove the flags altogether and use proplists to report the status in the alsa modules. Another thing (this is not something that needs to be fixed quickly, just FYI): another problem with the flags is that the virtual device flags depend on the master device flags. If the virtual device is moved to another master device, the flags don't get updated. > --- > src/modules/alsa/alsa-sink.c | 11 +++------ > src/modules/alsa/alsa-source.c | 11 +++------ > src/modules/bluetooth/module-bluetooth-device.c | 4 +- > src/modules/echo-cancel/module-echo-cancel.c | 4 +- > src/modules/module-equalizer-sink.c | 2 +- > src/modules/module-ladspa-sink.c | 2 +- > src/modules/module-solaris.c | 4 +- > src/modules/module-tunnel.c | 2 +- > src/modules/oss/module-oss.c | 3 -- > src/modules/raop/module-raop-sink.c | 2 +- > src/pulse/def.h | 18 +++++++++++----- > src/pulsecore/sink.c | 24 +++++++++++++++++----- > src/pulsecore/source.c | 24 +++++++++++++++++----- > 13 files changed, 66 insertions(+), 45 deletions(-) > > diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c > index 0164040..8f31901 100644 > --- a/src/modules/alsa/alsa-sink.c > +++ b/src/modules/alsa/alsa-sink.c > @@ -1759,9 +1759,9 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v > return 0; > } > > - if (!u->mixer_path->has_volume) > + if (!u->mixer_path->has_volume) { > pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); > - else { > + } else { > > if (u->mixer_path->has_dB) { > pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); > @@ -1779,13 +1779,11 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v > > u->sink->get_volume = sink_get_volume_cb; > u->sink->set_volume = sink_set_volume_cb; > - u->sink->write_volume = sink_write_volume_cb; > > - u->sink->flags |= PA_SINK_HW_VOLUME_CTRL; > if (u->mixer_path->has_dB) { > u->sink->flags |= PA_SINK_DECIBEL_VOLUME; > if (sync_volume) { > - u->sink->flags |= PA_SINK_SYNC_VOLUME; > + u->sink->write_volume = sink_write_volume_cb; > pa_log_info("Successfully enabled synchronous volume."); > } > } > @@ -1798,11 +1796,10 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v > } else { > u->sink->get_mute = sink_get_mute_cb; > u->sink->set_mute = sink_set_mute_cb; > - u->sink->flags |= PA_SINK_HW_MUTE_CTRL; > pa_log_info("Using hardware mute control."); > } > > - if (u->sink->flags & (PA_SINK_HW_VOLUME_CTRL|PA_SINK_HW_MUTE_CTRL)) { > + if (u->mixer_path->has_volume || u->mixer_path->has_mute) { > int (*mixer_callback)(snd_mixer_elem_t *, unsigned int); > if (u->sink->flags & PA_SINK_SYNC_VOLUME) { > u->mixer_pd = pa_alsa_mixer_pdata_new(); > diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c > index f847b1e..52dd65e 100644 > --- a/src/modules/alsa/alsa-source.c > +++ b/src/modules/alsa/alsa-source.c > @@ -1534,9 +1534,9 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v > return 0; > } > > - if (!u->mixer_path->has_volume) > + if (!u->mixer_path->has_volume) { > pa_log_info("Driver does not support hardware volume control, falling back to software volume control."); > - else { > + } else { > > if (u->mixer_path->has_dB) { > pa_log_info("Hardware volume ranges from %0.2f dB to %0.2f dB.", u->mixer_path->min_dB, u->mixer_path->max_dB); > @@ -1554,13 +1554,11 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v > > u->source->get_volume = source_get_volume_cb; > u->source->set_volume = source_set_volume_cb; > - u->source->write_volume = source_write_volume_cb; > > - u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL; > if (u->mixer_path->has_dB) { > u->source->flags |= PA_SOURCE_DECIBEL_VOLUME; > if (sync_volume) { > - u->source->flags |= PA_SOURCE_SYNC_VOLUME; > + u->source->write_volume = source_write_volume_cb; > pa_log_info("Successfully enabled synchronous volume."); > } > } > @@ -1573,11 +1571,10 @@ static int setup_mixer(struct userdata *u, pa_bool_t ignore_dB, pa_bool_t sync_v > } else { > u->source->get_mute = source_get_mute_cb; > u->source->set_mute = source_set_mute_cb; > - u->source->flags |= PA_SOURCE_HW_MUTE_CTRL; > pa_log_info("Using hardware mute control."); > } > > - if (u->source->flags & (PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_HW_MUTE_CTRL)) { > + if (u->mixer_path->has_volume || u->mixer_path->has_mute) { > int (*mixer_callback)(snd_mixer_elem_t *, unsigned int); > if (u->source->flags & PA_SOURCE_SYNC_VOLUME) { > u->mixer_pd = pa_alsa_mixer_pdata_new(); > diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c > index 288ad2f..448e252 100644 > --- a/src/modules/bluetooth/module-bluetooth-device.c > +++ b/src/modules/bluetooth/module-bluetooth-device.c > @@ -2022,7 +2022,7 @@ static int add_sink(struct userdata *u) { > return -1; > } > > - u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY | (u->profile == PROFILE_HSP ? PA_SINK_HW_VOLUME_CTRL : 0)); > + u->sink = pa_sink_new(u->core, &data, PA_SINK_HARDWARE|PA_SINK_LATENCY); > pa_sink_new_data_done(&data); > > if (!u->sink) { > @@ -2084,7 +2084,7 @@ static int add_source(struct userdata *u) { > return -1; > } > > - u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY | (u->profile == PROFILE_HSP ? PA_SOURCE_HW_VOLUME_CTRL : 0)); > + u->source = pa_source_new(u->core, &data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); > pa_source_new_data_done(&data); > > if (!u->source) { > diff --git a/src/modules/echo-cancel/module-echo-cancel.c b/src/modules/echo-cancel/module-echo-cancel.c > index b84bf1d..7939f5e 100644 > --- a/src/modules/echo-cancel/module-echo-cancel.c > +++ b/src/modules/echo-cancel/module-echo-cancel.c > @@ -1519,7 +1519,7 @@ int pa__init(pa_module*m) { > } > > u->source = pa_source_new(m->core, &source_data, > - PA_SOURCE_HW_MUTE_CTRL|PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_DECIBEL_VOLUME| > + PA_SOURCE_DECIBEL_VOLUME| > (source_master->flags & (PA_SOURCE_LATENCY|PA_SOURCE_DYNAMIC_LATENCY))); > pa_source_new_data_done(&source_data); > > @@ -1567,7 +1567,7 @@ int pa__init(pa_module*m) { > } > > u->sink = pa_sink_new(m->core, &sink_data, > - PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| > + PA_SINK_DECIBEL_VOLUME| > (sink_master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); > pa_sink_new_data_done(&sink_data); > > diff --git a/src/modules/module-equalizer-sink.c b/src/modules/module-equalizer-sink.c > index e7d8790..753202d 100644 > --- a/src/modules/module-equalizer-sink.c > +++ b/src/modules/module-equalizer-sink.c > @@ -1178,7 +1178,7 @@ int pa__init(pa_module*m) { > } > > u->sink = pa_sink_new(m->core, &sink_data, > - PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| > + PA_SINK_DECIBEL_VOLUME| > (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); > pa_sink_new_data_done(&sink_data); > > diff --git a/src/modules/module-ladspa-sink.c b/src/modules/module-ladspa-sink.c > index 9cce269..acf2d0e 100644 > --- a/src/modules/module-ladspa-sink.c > +++ b/src/modules/module-ladspa-sink.c > @@ -883,7 +883,7 @@ int pa__init(pa_module*m) { > } > > u->sink = pa_sink_new(m->core, &sink_data, > - PA_SINK_HW_MUTE_CTRL|PA_SINK_HW_VOLUME_CTRL|PA_SINK_DECIBEL_VOLUME| > + PA_SINK_DECIBEL_VOLUME| > (master->flags & (PA_SINK_LATENCY|PA_SINK_DYNAMIC_LATENCY))); > pa_sink_new_data_done(&sink_data); > > diff --git a/src/modules/module-solaris.c b/src/modules/module-solaris.c > index 0e4e401..89ebdee 100644 > --- a/src/modules/module-solaris.c > +++ b/src/modules/module-solaris.c > @@ -933,7 +933,7 @@ int pa__init(pa_module *m) { > goto fail; > } > > - u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL); > + u->source = pa_source_new(m->core, &source_new_data, PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY); > pa_source_new_data_done(&source_new_data); > pa_xfree(name_buf); > > @@ -981,7 +981,7 @@ int pa__init(pa_module *m) { > goto fail; > } > > - u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL|PA_SINK_HW_MUTE_CTRL); > + u->sink = pa_sink_new(m->core, &sink_new_data, PA_SINK_HARDWARE|PA_SINK_LATENCY); > pa_sink_new_data_done(&sink_new_data); > > pa_assert(u->sink); > diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c > index 4b1ae7d..aa7ad13 100644 > --- a/src/modules/module-tunnel.c > +++ b/src/modules/module-tunnel.c > @@ -2003,7 +2003,7 @@ int pa__init(pa_module*m) { > goto fail; > } > > - u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL|PA_SINK_HW_MUTE_CTRL); > + u->sink = pa_sink_new(m->core, &data, PA_SINK_NETWORK|PA_SINK_LATENCY); > pa_sink_new_data_done(&data); > > if (!u->sink) { > diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c > index 2a99d11..f40cb1d 100644 > --- a/src/modules/oss/module-oss.c > +++ b/src/modules/oss/module-oss.c > @@ -1418,11 +1418,9 @@ int pa__init(pa_module*m) { > > if (ioctl(fd, SOUND_MIXER_READ_DEVMASK, &u->mixer_devmask) < 0) > pa_log_warn("SOUND_MIXER_READ_DEVMASK failed: %s", pa_cstrerror(errno)); > - > else { > if (u->sink && (u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM))) { > pa_log_debug("Found hardware mixer track for playback."); > - u->sink->flags |= PA_SINK_HW_VOLUME_CTRL; > u->sink->get_volume = sink_get_volume; > u->sink->set_volume = sink_set_volume; > u->sink->n_volume_steps = 101; > @@ -1431,7 +1429,6 @@ int pa__init(pa_module*m) { > > if (u->source && (u->mixer_devmask & (SOUND_MASK_RECLEV|SOUND_MASK_IGAIN))) { > pa_log_debug("Found hardware mixer track for recording."); > - u->source->flags |= PA_SOURCE_HW_VOLUME_CTRL; > u->source->get_volume = source_get_volume; > u->source->set_volume = source_set_volume; > u->source->n_volume_steps = 101; > diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c > index 87e7bc1..b62d8fc 100644 > --- a/src/modules/raop/module-raop-sink.c > +++ b/src/modules/raop/module-raop-sink.c > @@ -596,7 +596,7 @@ int pa__init(pa_module*m) { > u->sink->userdata = u; > u->sink->set_volume = sink_set_volume_cb; > u->sink->set_mute = sink_set_mute_cb; > - u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK|PA_SINK_HW_VOLUME_CTRL; > + u->sink->flags = PA_SINK_LATENCY|PA_SINK_NETWORK; > > pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); > pa_sink_set_rtpoll(u->sink, u->rtpoll); > diff --git a/src/pulse/def.h b/src/pulse/def.h > index 16b2e59..32169de 100644 > --- a/src/pulse/def.h > +++ b/src/pulse/def.h > @@ -712,7 +712,8 @@ typedef enum pa_sink_flags { > /**< Flag to pass when no specific options are needed (used to avoid casting) \since 0.9.19 */ > > PA_SINK_HW_VOLUME_CTRL = 0x0001U, > - /**< Supports hardware volume control */ > + /**< Supports hardware volume control. This is a dynamic flag and may > + * change at runtime after the sink has initialized */ > > PA_SINK_LATENCY = 0x0002U, > /**< Supports latency querying */ > @@ -725,10 +726,12 @@ typedef enum pa_sink_flags { > /**< Is a networked sink of some kind. \since 0.9.7 */ > > PA_SINK_HW_MUTE_CTRL = 0x0010U, > - /**< Supports hardware mute control \since 0.9.11 */ > + /**< Supports hardware mute control. This is a dynamic flag and may > + * change at runtime after the sink has initialized \since 0.9.11 */ > > PA_SINK_DECIBEL_VOLUME = 0x0020U, > - /**< Volume can be translated to dB with pa_sw_volume_to_dB() > + /**< Volume can be translated to dB with pa_sw_volume_to_dB(). This is a > + * dynamic flag and may change at runtime after the sink has initialized > * \since 0.9.11 */ > > PA_SINK_FLAT_VOLUME = 0x0040U, > @@ -820,7 +823,8 @@ typedef enum pa_source_flags { > /**< Flag to pass when no specific options are needed (used to avoid casting) \since 0.9.19 */ > > PA_SOURCE_HW_VOLUME_CTRL = 0x0001U, > - /**< Supports hardware volume control */ > + /**< Supports hardware volume control. This is a dynamic flag and may > + * change at runtime after the source has initialized */ > > PA_SOURCE_LATENCY = 0x0002U, > /**< Supports latency querying */ > @@ -833,10 +837,12 @@ typedef enum pa_source_flags { > /**< Is a networked source of some kind. \since 0.9.7 */ > > PA_SOURCE_HW_MUTE_CTRL = 0x0010U, > - /**< Supports hardware mute control \since 0.9.11 */ > + /**< Supports hardware mute control. This is a dynamic flag and may > + * change at runtime after the source has initialized \since 0.9.11 */ > > PA_SOURCE_DECIBEL_VOLUME = 0x0020U, > - /**< Volume can be translated to dB with pa_sw_volume_to_dB() > + /**< Volume can be translated to dB with pa_sw_volume_to_dB(). This is a > + * dynamic flag and may change at runtime after the source has initialized > * \since 0.9.11 */ > > PA_SOURCE_DYNAMIC_LATENCY = 0x0040U, > diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c > index 45761a6..15c07a5 100644 > --- a/src/pulsecore/sink.c > +++ b/src/pulsecore/sink.c > @@ -462,8 +462,19 @@ void pa_sink_put(pa_sink* s) { > pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency); > > /* Generally, flags should be initialized via pa_sink_new(). As a > - * special exception we allow volume related flags to be set > - * between _new() and _put(). */ > + * special exception we allow some volume related flags to be set > + * between _new() and _put(). However some are dictated by what > + * callbacks were registered so we set those here. > + * > + * Note: All of these flags set here can change over the life time > + * of the sink. */ > + s->flags &= ~(PA_SINK_HW_VOLUME_CTRL|PA_SINK_HW_MUTE_CTRL|PA_SINK_SYNC_VOLUME|PA_SINK_FLAT_VOLUME); This breaks the "force flat volume" feature of module-virtual-sink. Would it do any harm to leave PA_SINK_FLAT_VOLUME out of this list? > + if (s->get_volume || s->set_volume) > + s->flags |= PA_SINK_HW_VOLUME_CTRL; > + if (s->get_mute || s->set_mute) > + s->flags |= PA_SINK_HW_MUTE_CTRL; I don't think providing get_volume is really enough to claim hw volume control support. The same goes for get_mute. I'd check only for set_volume and set_mute. > + if (s->write_volume) > + s->flags |= PA_SINK_SYNC_VOLUME; > > /* XXX: Currently decibel volume is disabled for all sinks that use volume > * sharing. When the master sink supports decibel volume, it would be good > @@ -472,12 +483,17 @@ void pa_sink_put(pa_sink* s) { > * a master sink to another. One solution for this problem would be to > * remove user-visible volume altogether from filter sinks when volume > * sharing is used, but the current approach was easier to implement... */ > + /* We always support decibel volumes in software, otherwise we leave it to > + * the sink implementor to set this flag as needed. > + * > + * Note: This flag can also change over the life time of the sink. */ > if (!(s->flags & PA_SINK_HW_VOLUME_CTRL) && !(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER)) > s->flags |= PA_SINK_DECIBEL_VOLUME; > > if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes) > s->flags |= PA_SINK_FLAT_VOLUME; > > + Extra empty line. > if (s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) { > pa_sink *root_sink = s->input_to_master->sink; > > @@ -506,10 +522,6 @@ void pa_sink_put(pa_sink* s) { > pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0)); > pa_assert(!(s->flags & PA_SINK_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_LATENCY)); > pa_assert(!(s->flags & PA_SINK_DYNAMIC_LATENCY) == !(s->monitor_source->flags & PA_SOURCE_DYNAMIC_LATENCY)); > - pa_assert(!(s->flags & PA_SINK_HW_VOLUME_CTRL) || s->set_volume); > - pa_assert(!(s->flags & PA_SINK_SYNC_VOLUME) || (s->flags & PA_SINK_HW_VOLUME_CTRL)); > - pa_assert(!(s->flags & PA_SINK_SYNC_VOLUME) || s->write_volume); > - pa_assert(!(s->flags & PA_SINK_HW_MUTE_CTRL) || s->set_mute); > > pa_assert(s->monitor_source->thread_info.fixed_latency == s->thread_info.fixed_latency); > pa_assert(s->monitor_source->thread_info.min_latency == s->thread_info.min_latency); > diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c > index 7024802..121c358 100644 > --- a/src/pulsecore/source.c > +++ b/src/pulsecore/source.c > @@ -394,8 +394,19 @@ void pa_source_put(pa_source *s) { > pa_assert(s->thread_info.min_latency <= s->thread_info.max_latency); > > /* Generally, flags should be initialized via pa_source_new(). As a > - * special exception we allow volume related flags to be set > - * between _new() and _put(). */ > + * special exception we allow some volume related flags to be set > + * between _new() and _put(). However some are dictated by what > + * callbacks were registered so we set those here. > + * > + * Note: All of these flags set here can change over the life time > + * of the source. */ > + s->flags &= ~(PA_SOURCE_HW_VOLUME_CTRL|PA_SOURCE_HW_MUTE_CTRL|PA_SOURCE_SYNC_VOLUME|PA_SOURCE_FLAT_VOLUME); I noticed that you've implemented the "force flat volume" feature also for module-virtual-source. Too bad you broke it now :) (See the comment above about module-virtual-sink.) > + if (s->get_volume || s->set_volume) > + s->flags |= PA_SOURCE_HW_VOLUME_CTRL; > + if (s->get_mute || s->set_mute) > + s->flags |= PA_SOURCE_HW_MUTE_CTRL; Same comment as for sinks. > + if (s->write_volume) > + s->flags |= PA_SOURCE_SYNC_VOLUME; > > /* XXX: Currently decibel volume is disabled for all sources that use volume > * sharing. When the master source supports decibel volume, it would be good > @@ -404,12 +415,17 @@ void pa_source_put(pa_source *s) { > * a master source to another. One solution for this problem would be to > * remove user-visible volume altogether from filter sources when volume > * sharing is used, but the current approach was easier to implement... */ > + /* We always support decibel volumes in software, otherwise we leave it to > + * the source implementor to set this flag as needed. > + * > + * Note: This flag can also change over the life time of the source. */ > if (!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) && !(s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) > s->flags |= PA_SOURCE_DECIBEL_VOLUME; > > if ((s->flags & PA_SOURCE_DECIBEL_VOLUME) && s->core->flat_volumes) > s->flags |= PA_SOURCE_FLAT_VOLUME; > > + > if (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER) { > pa_source *root_source = s->output_from_master->source; > > @@ -436,10 +452,6 @@ void pa_source_put(pa_source *s) { > && ((s->flags & PA_SOURCE_DECIBEL_VOLUME || (s->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))))); > pa_assert(!(s->flags & PA_SOURCE_DECIBEL_VOLUME) || s->n_volume_steps == PA_VOLUME_NORM+1); > pa_assert(!(s->flags & PA_SOURCE_DYNAMIC_LATENCY) == (s->thread_info.fixed_latency != 0)); > - pa_assert(!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) || s->set_volume); > - pa_assert(!(s->flags & PA_SOURCE_SYNC_VOLUME) || (s->flags & PA_SOURCE_HW_VOLUME_CTRL)); > - pa_assert(!(s->flags & PA_SOURCE_SYNC_VOLUME) || s->write_volume); > - pa_assert(!(s->flags & PA_SOURCE_HW_MUTE_CTRL) || s->set_mute); > > pa_assert_se(source_set_state(s, PA_SOURCE_IDLE) == 0); >