+static void volume_relative_adjust(pa_cvolume *cv) {
+ pa_assert((volume_flags & VOL_RELATIVE) == VOL_RELATIVE);
+
+ /* Relative volume change is additive in case of UINT or PERCENT
+ * and multiplicative for LINEAR or DECIBEL */
+ if ((volume_flags & 0x0F) == VOL_UINT || (volume_flags & 0x0F) == VOL_PERCENT) {
+ pa_volume_t v = pa_cvolume_avg(cv);
+ v = v + volume < PA_VOLUME_NORM ? PA_VOLUME_MUTED : v + volume - PA_VOLUME_NORM;
+ pa_cvolume_set(cv, 1, v);
+ }
+ if ((volume_flags & 0x0F) == VOL_LINEAR || (volume_flags & 0x0F) == VOL_DECIBEL) {
+ pa_sw_cvolume_multiply_scalar(cv, cv, volume);
+ }
+}
+
+static void get_sink_volume_callback(pa_context *c, const pa_sink_info *i, int is_last, void *userdata) {
+ pa_cvolume cv;
+
+ if (is_last < 0) {
+ pa_log(_("Failed to get sink information: %s"), pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last)
+ return;
+
+ pa_assert(i);
+
+ cv = i->volume;
+ volume_relative_adjust(&cv);
+ pa_operation_unref(pa_context_set_sink_volume_by_name(c, sink_name, &cv, simple_callback, NULL));
+}
+
+static void get_source_volume_callback(pa_context *c, const pa_source_info *i, int is_last, void *userdata) {
+ pa_cvolume cv;
+
+ if (is_last < 0) {
+ pa_log(_("Failed to get source information: %s"), pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last)
+ return;
+
+ pa_assert(i);
+
+ cv = i->volume;
+ volume_relative_adjust(&cv);
+ pa_operation_unref(pa_context_set_source_volume_by_name(c, source_name, &cv, simple_callback, NULL));
+}
+
+static void get_sink_input_volume_callback(pa_context *c, const pa_sink_input_info *i, int is_last, void *userdata) {
+ pa_cvolume cv;
+
+ if (is_last < 0) {
+ pa_log(_("Failed to get sink input information: %s"), pa_strerror(pa_context_errno(c)));
+ quit(1);
+ return;
+ }
+
+ if (is_last)
+ return;
+
+ pa_assert(i);
+
+ cv = i->volume;
+ volume_relative_adjust(&cv);
+ pa_operation_unref(pa_context_set_sink_input_volume(c, sink_input_idx, &cv, simple_callback, NULL));
+}
+