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);
pa_sink_set_get_volume_callback(u->sink, sink_get_volume_cb);
pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
- pa_sink_set_write_volume_callback(u->sink, 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;
+ pa_sink_set_write_volume_callback(u->sink, sink_write_volume_cb);
pa_log_info("Successfully enabled synchronous volume.");
}
}
} else {
pa_sink_set_get_mute_callback(u->sink, sink_get_mute_cb);
pa_sink_set_set_mute_callback(u->sink, 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();
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);
pa_source_set_get_volume_callback(u->source, source_get_volume_cb);
pa_source_set_set_volume_callback(u->source, source_set_volume_cb);
- pa_source_set_write_volume_callback(u->source, 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;
+ pa_source_set_write_volume_callback(u->source, source_write_volume_cb);
pa_log_info("Successfully enabled synchronous volume.");
}
}
} else {
pa_source_set_get_mute_callback(u->source, source_get_mute_cb);
pa_source_set_set_mute_callback(u->source, 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();
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) {
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) {
}
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);
}
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);
}
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);
}
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);
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);
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);
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) {
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;
pa_sink_set_get_volume_callback(u->sink, sink_get_volume);
pa_sink_set_set_volume_callback(u->sink, sink_set_volume);
u->sink->n_volume_steps = 101;
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;
pa_source_set_get_volume_callback(u->source, source_get_volume);
pa_source_set_set_volume_callback(u->source, source_set_volume);
u->source->n_volume_steps = 101;
u->sink->userdata = u;
pa_sink_set_set_volume_callback(u->sink, sink_set_volume_cb);
pa_sink_set_set_mute_callback(u->sink, 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);
/**< 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 */
/**< 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,
/**< 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 */
/**< 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,
void pa_sink_set_set_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
pa_assert(s);
+ pa_assert(!s->write_volume || cb);
+
s->set_volume = cb;
+
+ if (cb)
+ s->flags |= PA_SINK_HW_VOLUME_CTRL;
+ else
+ s->flags &= ~PA_SINK_HW_VOLUME_CTRL;
}
void pa_sink_set_write_volume_callback(pa_sink *s, pa_sink_cb_t cb) {
pa_assert(s);
+ pa_assert(!cb || s->set_volume);
+
s->write_volume = cb;
+
+ if (cb)
+ s->flags |= PA_SINK_SYNC_VOLUME;
+ else
+ s->flags &= ~PA_SINK_SYNC_VOLUME;
}
void pa_sink_set_get_mute_callback(pa_sink *s, pa_sink_cb_t cb) {
pa_assert(s);
s->set_mute = cb;
+
+ if (cb)
+ s->flags |= PA_SINK_HW_MUTE_CTRL;
+ else
+ s->flags &= ~PA_SINK_HW_MUTE_CTRL;
}
/* Called from main context */
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() by the callback setter functions above.
+ *
+ * Thus we implement a couple safeguards here which ensure the above
+ * setters were used (or at least the implementor made manual changes
+ * in a compatible way).
+ *
+ * Note: All of these flags set here can change over the life time
+ * of the sink. */
+ pa_assert(!(s->flags & PA_SINK_HW_VOLUME_CTRL) || s->set_volume);
+ pa_assert(!(s->flags & PA_SINK_SYNC_VOLUME) || s->write_volume);
+ pa_assert(!(s->flags & PA_SINK_HW_MUTE_CTRL) || s->set_mute);
/* XXX: Currently decibel volume is disabled for all sinks that use volume
* sharing. When the master sink supports decibel volume, it would be good
* 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;
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);
void pa_source_set_set_volume_callback(pa_source *s, pa_source_cb_t cb) {
pa_assert(s);
+ pa_assert(!s->write_volume || cb);
+
s->set_volume = cb;
+
+ if (cb)
+ s->flags |= PA_SOURCE_HW_VOLUME_CTRL;
+ else
+ s->flags &= ~PA_SOURCE_HW_VOLUME_CTRL;
}
void pa_source_set_write_volume_callback(pa_source *s, pa_source_cb_t cb) {
pa_assert(s);
+ pa_assert(!cb || s->set_volume);
+
s->write_volume = cb;
+
+ if (cb)
+ s->flags |= PA_SOURCE_SYNC_VOLUME;
+ else
+ s->flags &= ~PA_SOURCE_SYNC_VOLUME;
}
void pa_source_set_get_mute_callback(pa_source *s, pa_source_cb_t cb) {
pa_assert(s);
s->set_mute = cb;
+
+ if (cb)
+ s->flags |= PA_SOURCE_HW_MUTE_CTRL;
+ else
+ s->flags &= ~PA_SOURCE_HW_MUTE_CTRL;
}
/* Called from main context */
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() by the callback setter functions above.
+ *
+ * Thus we implement a couple safeguards here which ensure the above
+ * setters were used (or at least the implementor made manual changes
+ * in a compatible way).
+ *
+ * Note: All of these flags set here can change over the life time
+ * of the source. */
+ pa_assert(!(s->flags & PA_SOURCE_HW_VOLUME_CTRL) || s->set_volume);
+ pa_assert(!(s->flags & PA_SOURCE_SYNC_VOLUME) || s->write_volume);
+ pa_assert(!(s->flags & PA_SOURCE_HW_MUTE_CTRL) || s->set_mute);
/* XXX: Currently decibel volume is disabled for all sources that use volume
* sharing. When the master source supports decibel volume, it would be good
* 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;
&& ((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);