+ pa_assert(o);
+ pa_assert(dest);
+ pa_assert(o->source); /* The destination source should already be set. */
+
+ if (o->destination_source && (o->destination_source->flags & PA_SOURCE_SHARE_VOLUME_WITH_MASTER)) {
+ pa_source *root_source;
+ pa_source_output *destination_source_output;
+ uint32_t idx;
+
+ root_source = pa_source_get_master(o->source);
+
+ if (PA_UNLIKELY(!root_source))
+ return;
+
+ if (pa_source_flat_volume_enabled(o->source)) {
+ /* Ok, so the origin source uses volume sharing, and flat volume is
+ * enabled. The volume will have to be updated as follows:
+ *
+ * o->volume := o->source->real_volume
+ * (handled later by pa_source_set_volume)
+ * o->reference_ratio := o->volume / o->source->reference_volume
+ * (handled later by pa_source_set_volume)
+ * o->real_ratio stays unchanged
+ * (streams whose origin source uses volume sharing should
+ * always have real_ratio of 0 dB)
+ * o->soft_volume stays unchanged
+ * (streams whose origin source uses volume sharing should
+ * always have volume_factor as soft_volume, so no change
+ * should be needed) */
+
+ pa_assert(pa_cvolume_is_norm(&o->real_ratio));
+ pa_assert(pa_cvolume_equal(&o->soft_volume, &o->volume_factor));
+
+ /* Notifications will be sent by pa_source_set_volume(). */
+
+ } else {
+ /* Ok, so the origin source uses volume sharing, and flat volume is
+ * disabled. The volume will have to be updated as follows:
+ *
+ * o->volume := 0 dB
+ * o->reference_ratio := 0 dB
+ * o->real_ratio stays unchanged
+ * (streams whose origin source uses volume sharing should
+ * always have real_ratio of 0 dB)
+ * o->soft_volume stays unchanged
+ * (streams whose origin source uses volume sharing should
+ * always have volume_factor as soft_volume, so no change
+ * should be needed) */
+
+ 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));
+ }
+
+ /* Additionally, the origin source volume needs updating:
+ *
+ * o->destination_source->reference_volume := root_source->reference_volume
+ * o->destination_source->real_volume := root_source->real_volume
+ * o->destination_source->soft_volume stays unchanged
+ * (sources that use volume sharing should always have
+ * soft_volume of 0 dB) */
+
+ 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));
+
+ /* 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 {
+ 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->volume := o->reference_ratio * o->source->reference_volume
+ * o->reference_ratio stays unchanged
+ * o->real_ratio := o->volume / o->source->real_volume
+ * (handled later by pa_source_set_volume)
+ * o->soft_volume := o->real_ratio * o->volume_factor
+ * (handled later by pa_source_set_volume) */
+
+ 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.
+ * The volume will have to be updated as follows:
+ *
+ * o->volume := o->reference_ratio
+ * o->reference_ratio stays unchanged
+ * o->real_ratio := o->reference_ratio
+ * o->soft_volume := o->real_ratio * o->volume_factor */
+
+ 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);
+ }
+ }
+
+ /* 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);
+}
+
+/* Called from main context */
+int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, bool save) {