#include <pulse/util.h>
#include <pulse/internal.h>
+#include <pulsecore/core-format.h>
#include <pulsecore/mix.h>
+#include <pulsecore/stream-util.h>
#include <pulsecore/core-subscribe.h>
#include <pulsecore/log.h>
#include <pulsecore/namereg.h>
pa_zero(*data);
data->resample_method = PA_RESAMPLER_INVALID;
data->proplist = pa_proplist_new();
- data->volume_writable = TRUE;
+ data->volume_writable = true;
return data;
}
data->channel_map = *map;
}
-pa_bool_t pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data) {
+bool pa_source_output_new_data_is_passthrough(pa_source_output_new_data *data) {
pa_assert(data);
if (PA_LIKELY(data->format) && PA_UNLIKELY(!pa_format_info_is_pcm(data->format)))
- return TRUE;
+ return true;
if (PA_UNLIKELY(data->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
void pa_source_output_new_data_set_volume(pa_source_output_new_data *data, const pa_cvolume *volume) {
if (data->volume_factor_is_set)
pa_sw_cvolume_multiply(&data->volume_factor, &data->volume_factor, volume_factor);
else {
- data->volume_factor_is_set = TRUE;
+ data->volume_factor_is_set = true;
data->volume_factor = *volume_factor;
}
}
if (data->volume_factor_source_is_set)
pa_sw_cvolume_multiply(&data->volume_factor_source, &data->volume_factor_source, volume_factor);
else {
- data->volume_factor_source_is_set = TRUE;
+ data->volume_factor_source_is_set = true;
data->volume_factor_source = *volume_factor;
}
}
-void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, pa_bool_t mute) {
+void pa_source_output_new_data_set_muted(pa_source_output_new_data *data, bool mute) {
pa_assert(data);
- data->muted_is_set = TRUE;
+ data->muted_is_set = true;
data->muted = !!mute;
}
-pa_bool_t pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, pa_bool_t save) {
- pa_bool_t ret = TRUE;
+bool pa_source_output_new_data_set_source(pa_source_output_new_data *data, pa_source *s, bool save) {
+ bool ret = true;
pa_idxset *formats = NULL;
pa_assert(data);
/* Source doesn't support any of the formats requested by the client */
if (formats)
pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
- ret = FALSE;
+ ret = false;
}
}
return ret;
}
-pa_bool_t pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) {
+bool pa_source_output_new_data_set_formats(pa_source_output_new_data *data, pa_idxset *formats) {
pa_assert(data);
pa_assert(formats);
if (data->req_formats)
- pa_idxset_free(formats, (pa_free_cb_t) pa_format_info_free);
+ pa_idxset_free(data->req_formats, (pa_free_cb_t) pa_format_info_free);
data->req_formats = formats;
return pa_source_output_new_data_set_source(data, data->source, data->save_source);
}
- return TRUE;
+ return true;
}
void pa_source_output_new_data_done(pa_source_output_new_data *data) {
pa_source_output *o;
pa_resampler *resampler = NULL;
- char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
- pa_channel_map original_cm;
+ char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], fmt[PA_FORMAT_INFO_SNPRINT_MAX];
+ pa_channel_map volume_map;
int r;
char *pt;
- pa_sample_spec ss;
- pa_channel_map map;
pa_assert(_o);
pa_assert(core);
pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist);
if (data->destination_source && (data->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER))
- data->volume_writable = FALSE;
+ data->volume_writable = false;
if (!data->req_formats) {
/* From this point on, we want to work only with formats, and get back
* to using the sample spec and channel map after all decisions w.r.t.
* routing are complete. */
- pa_idxset *tmp = pa_idxset_new(NULL, NULL);
- pa_format_info *f = pa_format_info_from_sample_spec(&data->sample_spec,
- data->channel_map_is_set ? &data->channel_map : NULL);
- pa_idxset_put(tmp, f, NULL);
- pa_source_output_new_data_set_formats(data, tmp);
+ pa_format_info *f;
+ pa_idxset *formats;
+
+ f = pa_format_info_from_sample_spec2(&data->sample_spec, data->channel_map_is_set ? &data->channel_map : NULL,
+ !(data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT),
+ !(data->flags & PA_SOURCE_OUTPUT_FIX_RATE),
+ !(data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS));
+ if (!f)
+ return -PA_ERR_INVALID;
+
+ formats = pa_idxset_new(NULL, NULL);
+ pa_idxset_put(formats, f, NULL);
+ pa_source_output_new_data_set_formats(data, formats);
}
if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0)
pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID);
if (!data->source) {
- pa_source *source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
- pa_return_val_if_fail(source, -PA_ERR_NOENTITY);
- pa_source_output_new_data_set_source(data, source, FALSE);
- }
+ pa_source *source;
- /* Routing's done, we have a source. Now let's fix the format and set up the
- * sample spec */
+ if (data->direct_on_input) {
+ source = data->direct_on_input->sink->monitor_source;
+ pa_return_val_if_fail(source, -PA_ERR_INVALID);
+ } else {
+ source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE);
+ pa_return_val_if_fail(source, -PA_ERR_NOENTITY);
+ }
+
+ pa_source_output_new_data_set_source(data, source, false);
+ }
/* If something didn't pick a format for us, pick the top-most format since
* we assume this is sorted in priority order */
if (!data->format && data->nego_formats && !pa_idxset_isempty(data->nego_formats))
data->format = pa_format_info_copy(pa_idxset_first(data->nego_formats, NULL));
- pa_return_val_if_fail(data->format, -PA_ERR_NOTSUPPORTED);
+ if (PA_LIKELY(data->format)) {
+ pa_log_debug("Negotiated format: %s", pa_format_info_snprint(fmt, sizeof(fmt), data->format));
+ } else {
+ pa_format_info *format;
+ uint32_t idx;
- /* Now populate the sample spec and format according to the final
- * format that we've negotiated */
- pa_return_val_if_fail(pa_format_info_to_sample_spec(data->format, &ss, &map) == 0, -PA_ERR_INVALID);
- pa_source_output_new_data_set_sample_spec(data, &ss);
- if (pa_format_info_is_pcm(data->format) && pa_channel_map_valid(&map))
- pa_source_output_new_data_set_channel_map(data, &map);
+ pa_log_info("Source does not support any requested format:");
+ PA_IDXSET_FOREACH(format, data->req_formats, idx)
+ pa_log_info(" -- %s", pa_format_info_snprint(fmt, sizeof(fmt), format));
+
+ return -PA_ERR_NOTSUPPORTED;
+ }
pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE);
pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID);
- if (!data->sample_spec_is_set)
- data->sample_spec = data->source->sample_spec;
-
- pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID);
+ /* Routing is done. We have a source and a format. */
- if (!data->channel_map_is_set) {
- if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec))
- data->channel_map = data->source->channel_map;
- else
- pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
+ if (data->volume_is_set && pa_format_info_is_pcm(data->format)) {
+ /* If volume is set, we need to save the original data->channel_map,
+ * so that we can remap the volume from the original channel map to the
+ * final channel map of the stream in case data->channel_map gets
+ * modified in pa_format_info_to_sample_spec2(). */
+ r = pa_stream_get_volume_channel_map(&data->volume, data->channel_map_is_set ? &data->channel_map : NULL, data->format, &volume_map);
+ if (r < 0)
+ return r;
}
- pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID);
+ /* Now populate the sample spec and channel map according to the final
+ * format that we've negotiated */
+ r = pa_format_info_to_sample_spec2(data->format, &data->sample_spec, &data->channel_map, &data->source->sample_spec,
+ &data->source->channel_map);
+ if (r < 0)
+ return r;
/* Don't restore (or save) stream volume for passthrough streams and
* prevent attenuation/gain */
if (pa_source_output_new_data_is_passthrough(data)) {
- data->volume_is_set = TRUE;
+ data->volume_is_set = true;
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- data->volume_is_absolute = TRUE;
- data->save_volume = FALSE;
+ data->volume_is_absolute = true;
+ data->save_volume = false;
}
if (!data->volume_is_set) {
pa_cvolume_reset(&data->volume, data->sample_spec.channels);
- data->volume_is_absolute = FALSE;
- data->save_volume = FALSE;
+ data->volume_is_absolute = false;
+ data->save_volume = false;
}
if (!data->volume_writable)
data->save_volume = false;
- pa_return_val_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec), -PA_ERR_INVALID);
+ if (data->volume_is_set)
+ /* The original volume channel map may be different than the final
+ * stream channel map, so remapping may be needed. */
+ pa_cvolume_remap(&data->volume, &volume_map, &data->channel_map);
if (!data->volume_factor_is_set)
pa_cvolume_reset(&data->volume_factor, data->sample_spec.channels);
pa_return_val_if_fail(pa_cvolume_compatible(&data->volume_factor_source, &data->source->sample_spec), -PA_ERR_INVALID);
if (!data->muted_is_set)
- data->muted = FALSE;
-
- if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT) {
- pa_return_val_if_fail(pa_format_info_is_pcm(data->format), -PA_ERR_INVALID);
- data->sample_spec.format = data->source->sample_spec.format;
- pa_format_info_set_sample_format(data->format, data->sample_spec.format);
- }
-
- if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE) {
- pa_return_val_if_fail(pa_format_info_is_pcm(data->format), -PA_ERR_INVALID);
- pa_format_info_set_rate(data->format, data->sample_spec.rate);
- data->sample_spec.rate = data->source->sample_spec.rate;
- }
-
- original_cm = data->channel_map;
-
- if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) {
- pa_return_val_if_fail(pa_format_info_is_pcm(data->format), -PA_ERR_INVALID);
- data->sample_spec.channels = data->source->sample_spec.channels;
- data->channel_map = data->source->channel_map;
- pa_format_info_set_channels(data->format, data->sample_spec.channels);
- pa_format_info_set_channel_map(data->format, &data->channel_map);
- }
-
- pa_assert(pa_sample_spec_valid(&data->sample_spec));
- pa_assert(pa_channel_map_valid(&data->channel_map));
+ data->muted = false;
if (!(data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) &&
- !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec)){
+ !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec)) {
/* try to change source rate. This is done before the FIXATE hook since
module-suspend-on-idle can resume a source */
pa_log_info("Trying to change sample rate");
- if (pa_source_update_rate(data->source, data->sample_spec.rate, pa_source_output_new_data_is_passthrough(data)) == TRUE)
+ if (pa_source_update_rate(data->source, data->sample_spec.rate, pa_source_output_new_data_is_passthrough(data)) >= 0)
pa_log_info("Rate changed to %u Hz", data->source->sample_spec.rate);
}
return -PA_ERR_NOTSUPPORTED;
}
- /* Due to the fixing of the sample spec the volume might not match anymore */
- pa_cvolume_remap(&data->volume, &original_cm, &data->channel_map);
-
if (data->resample_method == PA_RESAMPLER_INVALID)
data->resample_method = core->resample_method;
o->userdata = NULL;
o->thread_info.state = o->state;
- o->thread_info.attached = FALSE;
+ o->thread_info.attached = false;
o->thread_info.sample_spec = o->sample_spec;
o->thread_info.resampler = resampler;
o->thread_info.soft_volume = o->soft_volume;
/* Called from main context */
void pa_source_output_unlink(pa_source_output*o) {
- pa_bool_t linked;
+ bool linked;
pa_assert(o);
pa_assert_ctl_context();
/* We might need to update the source's volume if we are in flat volume mode. */
if (pa_source_flat_volume_enabled(o->source))
- pa_source_set_volume(o->source, NULL, FALSE, FALSE);
+ pa_source_set_volume(o->source, NULL, false, false);
if (o->source->asyncmsgq)
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
if (PA_SOURCE_OUTPUT_IS_LINKED(o->state))
pa_source_output_unlink(o);
- pa_log_info("Freeing output %u \"%s\"", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)));
+ pa_log_info("Freeing output %u \"%s\"", o->index,
+ o->proplist ? pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)) : "");
if (o->thread_info.delay_memblockq)
pa_memblockq_free(o->thread_info.delay_memblockq);
/* We might need to update the source's volume if we are in flat volume mode. */
if (pa_source_flat_volume_enabled(o->source))
- pa_source_set_volume(o->source, NULL, FALSE, o->save_volume);
+ pa_source_set_volume(o->source, NULL, false, o->save_volume);
else {
if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
pa_assert(pa_cvolume_is_norm(&o->volume));
/* Called from thread context */
void pa_source_output_push(pa_source_output *o, const pa_memchunk *chunk) {
- pa_bool_t need_volume_factor_source;
- pa_bool_t volume_is_norm;
+ bool need_volume_factor_source;
+ bool volume_is_norm;
size_t length;
size_t limit, mbs = 0;
if (pa_memblockq_push(o->thread_info.delay_memblockq, chunk) < 0) {
pa_log_debug("Delay queue overflow!");
- pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, TRUE);
+ pa_memblockq_seek(o->thread_info.delay_memblockq, (int64_t) chunk->length, PA_SEEK_RELATIVE, true);
}
limit = o->process_rewind ? 0 : o->source->thread_info.max_rewind;
/* Implement the delay queue */
while ((length = pa_memblockq_get_length(o->thread_info.delay_memblockq)) > limit) {
pa_memchunk qchunk;
- pa_bool_t nvfs = need_volume_factor_source;
+ bool nvfs = need_volume_factor_source;
length -= limit;
if (o->thread_info.muted) {
pa_silence_memchunk(&qchunk, &o->source->sample_spec);
- nvfs = FALSE;
+ nvfs = false;
} else if (!o->thread_info.resampler && nvfs) {
pa_cvolume v;
pa_sw_cvolume_multiply(&v, &o->thread_info.soft_volume, &o->volume_factor_source);
pa_volume_memchunk(&qchunk, &o->source->sample_spec, &v);
- nvfs = FALSE;
+ nvfs = false;
} else
pa_volume_memchunk(&qchunk, &o->source->sample_spec, &o->thread_info.soft_volume);
usec = PA_CLAMP(usec, o->source->thread_info.min_latency, o->source->thread_info.max_latency);
o->thread_info.requested_source_latency = usec;
- pa_source_invalidate_requested_latency(o->source, TRUE);
+ pa_source_invalidate_requested_latency(o->source, true);
return usec;
}
}
/* Called from main context */
-void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {
+void pa_source_output_set_volume(pa_source_output *o, const pa_cvolume *volume, bool save, bool absolute) {
pa_cvolume v;
pa_source_output_assert_ref(o);
return;
}
- o->volume = *volume;
+ pa_source_output_set_volume_direct(o, volume);
o->save_volume = save;
if (pa_source_flat_volume_enabled(o->source)) {
/* We are in flat volume mode, so let's update all source input
* volumes and update the flat volume of the source */
- pa_source_set_volume(o->source, NULL, TRUE, save);
+ pa_source_set_volume(o->source, NULL, true, save);
} else {
/* OK, we are in normal volume mode. The volume only affects
}
/* Called from main or I/O context */
-pa_bool_t pa_source_output_is_passthrough(pa_source_output *o) {
+bool pa_source_output_is_passthrough(pa_source_output *o) {
pa_source_output_assert_ref(o);
if (PA_UNLIKELY(!pa_format_info_is_pcm(o->format)))
- return TRUE;
+ return true;
if (PA_UNLIKELY(o->flags & PA_SOURCE_OUTPUT_PASSTHROUGH))
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
/* Called from main context */
-pa_bool_t pa_source_output_is_volume_readable(pa_source_output *o) {
+bool pa_source_output_is_volume_readable(pa_source_output *o) {
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
}
/* Called from main context */
-pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, pa_bool_t absolute) {
+pa_cvolume *pa_source_output_get_volume(pa_source_output *o, pa_cvolume *volume, bool absolute) {
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
}
/* Called from main context */
-void pa_source_output_set_mute(pa_source_output *o, pa_bool_t mute, pa_bool_t save) {
+void pa_source_output_set_mute(pa_source_output *o, bool mute, bool save) {
+ bool old_mute;
+
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
- if (!o->muted == !mute) {
- o->save_muted = o->save_muted || mute;
+ old_mute = o->muted;
+
+ if (mute == old_mute) {
+ o->save_muted |= save;
return;
}
o->muted = mute;
+ pa_log_debug("The mute of source output %u changed from %s to %s.", o->index, pa_yes_no(old_mute), pa_yes_no(mute));
+
o->save_muted = save;
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_SOFT_MUTE, NULL, 0, NULL) == 0);
pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
}
-/* Called from main context */
-pa_bool_t pa_source_output_get_mute(pa_source_output *o) {
- pa_source_output_assert_ref(o);
- pa_assert_ctl_context();
- pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
-
- return o->muted;
-}
-
/* Called from main thread */
void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) {
pa_source_output_assert_ref(o);
}
/* Called from main context */
-void pa_source_output_cork(pa_source_output *o, pa_bool_t b) {
+void pa_source_output_cork(pa_source_output *o, bool b) {
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
}
/* Called from main context */
-pa_bool_t pa_source_output_may_move(pa_source_output *o) {
+bool pa_source_output_may_move(pa_source_output *o) {
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
if (o->flags & PA_SOURCE_OUTPUT_DONT_MOVE)
- return FALSE;
+ return false;
if (o->direct_on_input)
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
-static pa_bool_t find_filter_source_output(pa_source_output *target, pa_source *s) {
+static bool find_filter_source_output(pa_source_output *target, pa_source *s) {
int i = 0;
while (s && s->output_from_master) {
if (s->output_from_master == target)
- return TRUE;
+ return true;
s = s->output_from_master->source;
pa_assert(i++ < 100);
}
- return FALSE;
+ return false;
}
/* Called from main context */
-pa_bool_t pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
+bool pa_source_output_may_move_to(pa_source_output *o, pa_source *dest) {
pa_source_output_assert_ref(o);
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
pa_source_assert_ref(dest);
if (dest == o->source)
- return TRUE;
+ return true;
if (!pa_source_output_may_move(o))
- return FALSE;
+ return false;
/* Make sure we're not creating a filter source cycle */
if (find_filter_source_output(o, dest)) {
pa_log_debug("Can't connect output to %s, as that would create a cycle.", dest->name);
- return FALSE;
+ return false;
}
if (pa_idxset_size(dest->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) {
pa_log_warn("Failed to move source output: too many outputs per source.");
- return FALSE;
+ return false;
}
if (o->may_move_to)
if (!o->may_move_to(o, dest))
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
/* Called from main context */
if (pa_source_flat_volume_enabled(o->source))
/* We might need to update the source's volume if we are in flat
* volume mode. */
- pa_source_set_volume(o->source, NULL, FALSE, FALSE);
+ pa_source_set_volume(o->source, NULL, false, false);
pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0);
* then also the origin source and all streams connected to it need to update
* their volume - this function does all that by using recursion. */
static void update_volume_due_to_moving(pa_source_output *o, pa_source *dest) {
- pa_cvolume old_volume;
+ pa_cvolume new_volume;
pa_assert(o);
pa_assert(dest);
* always have volume_factor as soft_volume, so no change
* should be needed) */
- old_volume = o->volume;
- pa_cvolume_reset(&o->volume, o->volume.channels);
+ pa_cvolume_reset(&new_volume, o->volume.channels);
+ pa_source_output_set_volume_direct(o, &new_volume);
pa_cvolume_reset(&o->reference_ratio, o->reference_ratio.channels);
pa_assert(pa_cvolume_is_norm(&o->real_ratio));
pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
-
- /* Notify others about the changed source output volume. */
- if (!pa_cvolume_equal(&o->volume, &old_volume)) {
- if (o->volume_changed)
- o->volume_changed(o);
-
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- }
}
/* Additionally, the origin source volume needs updating:
* (sources that use volume sharing should always have
* soft_volume of 0 dB) */
- old_volume = o->destination_source->reference_volume;
-
- o->destination_source->reference_volume = root_source->reference_volume;
- pa_cvolume_remap(&o->destination_source->reference_volume, &root_source->channel_map, &o->destination_source->channel_map);
+ new_volume = root_source->reference_volume;
+ pa_cvolume_remap(&new_volume, &root_source->channel_map, &o->destination_source->channel_map);
+ pa_source_set_reference_volume_direct(o->destination_source, &new_volume);
o->destination_source->real_volume = root_source->real_volume;
pa_cvolume_remap(&o->destination_source->real_volume, &root_source->channel_map, &o->destination_source->channel_map);
pa_assert(pa_cvolume_is_norm(&o->destination_source->soft_volume));
- /* Notify others about the changed source volume. If you wonder whether
- * o->destination_source->set_volume() should be called somewhere, that's not
- * the case, because sources that use volume sharing shouldn't have any
- * internal volume that set_volume() would update. If you wonder
- * whether the thread_info variables should be synced, yes, they
- * should, and it's done by the PA_SOURCE_MESSAGE_FINISH_MOVE message
- * handler. */
- if (!pa_cvolume_equal(&o->destination_source->reference_volume, &old_volume))
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, o->destination_source->index);
+ /* If you wonder whether o->destination_source->set_volume() should be
+ * called somewhere, that's not the case, because sources that use
+ * volume sharing shouldn't have any internal volume that set_volume()
+ * would update. If you wonder whether the thread_info variables should
+ * be synced, yes, they should, and it's done by the
+ * PA_SOURCE_MESSAGE_FINISH_MOVE message handler. */
/* Recursively update origin source outputs. */
PA_IDXSET_FOREACH(destination_source_output, o->destination_source->outputs, idx)
update_volume_due_to_moving(destination_source_output, dest);
} else {
- old_volume = o->volume;
-
if (pa_source_flat_volume_enabled(o->source)) {
/* Ok, so this is a regular stream, and flat volume is enabled. The
* volume will have to be updated as follows:
* o->soft_volume := o->real_ratio * o->volume_factor
* (handled later by pa_source_set_volume) */
- o->volume = o->source->reference_volume;
- pa_cvolume_remap(&o->volume, &o->source->channel_map, &o->channel_map);
- pa_sw_cvolume_multiply(&o->volume, &o->volume, &o->reference_ratio);
+ new_volume = o->source->reference_volume;
+ pa_cvolume_remap(&new_volume, &o->source->channel_map, &o->channel_map);
+ pa_sw_cvolume_multiply(&new_volume, &new_volume, &o->reference_ratio);
+ pa_source_output_set_volume_direct(o, &new_volume);
} else {
/* Ok, so this is a regular stream, and flat volume is disabled.
* o->real_ratio := o->reference_ratio
* o->soft_volume := o->real_ratio * o->volume_factor */
- o->volume = o->reference_ratio;
+ pa_source_output_set_volume_direct(o, &o->reference_ratio);
o->real_ratio = o->reference_ratio;
pa_sw_cvolume_multiply(&o->soft_volume, &o->real_ratio, &o->volume_factor);
}
-
- /* Notify others about the changed source output volume. */
- if (!pa_cvolume_equal(&o->volume, &old_volume)) {
- /* XXX: In case o->source has flat volume enabled, then real_ratio
- * and soft_volume are not updated yet. Let's hope that the
- * callback implementation doesn't care about those variables... */
- if (o->volume_changed)
- o->volume_changed(o);
-
- pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
- }
}
/* If o->source == dest, then recursion has finished, and we can finally call
* pa_source_set_volume(), which will do the rest of the updates. */
if ((o->source == dest) && pa_source_flat_volume_enabled(o->source))
- pa_source_set_volume(o->source, NULL, FALSE, o->save_volume);
+ pa_source_set_volume(o->source, NULL, false, o->save_volume);
}
/* Called from main context */
-int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) {
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save) {
pa_source_output_assert_ref(o);
pa_assert_ctl_context();
pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state));
}
if (!(o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) &&
- !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec)){
+ !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec)) {
/* try to change dest sink rate if possible without glitches.
module-suspend-on-idle resumes destination source with
SOURCE_OUTPUT_MOVE_FINISH hook */
pa_log_info("Trying to change sample rate");
- if (pa_source_update_rate(dest, o->sample_spec.rate, pa_source_output_is_passthrough(o)) == TRUE)
+ if (pa_source_update_rate(dest, o->sample_spec.rate, pa_source_output_is_passthrough(o)) >= 0)
pa_log_info("Rate changed to %u Hz", dest->sample_spec.rate);
}
}
/* Called from main context */
-int pa_source_output_move_to(pa_source_output *o, pa_source *dest, pa_bool_t save) {
+int pa_source_output_move_to(pa_source_output *o, pa_source *dest, bool save) {
int r;
pa_source_output_assert_ref(o);
o->requested_resample_method,
((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) |
((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) |
- (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0));
+ (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) |
+ (o->core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0));
if (!new_resampler) {
pa_log_warn("Unsupported resampling operation.");
return 0;
}
+
+/* Called from the main thread. */
+void pa_source_output_set_volume_direct(pa_source_output *o, const pa_cvolume *volume) {
+ pa_cvolume old_volume;
+ char old_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+ char new_volume_str[PA_CVOLUME_SNPRINT_VERBOSE_MAX];
+
+ pa_assert(o);
+ pa_assert(volume);
+
+ old_volume = o->volume;
+
+ if (pa_cvolume_equal(volume, &old_volume))
+ return;
+
+ o->volume = *volume;
+ pa_log_debug("The volume of source output %u changed from %s to %s.", o->index,
+ pa_cvolume_snprint_verbose(old_volume_str, sizeof(old_volume_str), &old_volume, &o->channel_map, true),
+ pa_cvolume_snprint_verbose(new_volume_str, sizeof(new_volume_str), volume, &o->channel_map, true));
+
+ if (o->volume_changed)
+ o->volume_changed(o);
+
+ pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index);
+}