diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/modules/alsa/alsa-sink.c | 2 | ||||
| -rw-r--r-- | src/modules/module-device-restore.c | 2 | ||||
| -rw-r--r-- | src/modules/module-lirc.c | 2 | ||||
| -rw-r--r-- | src/modules/module-match.c | 2 | ||||
| -rw-r--r-- | src/modules/module-mmkbd-evdev.c | 6 | ||||
| -rw-r--r-- | src/modules/module-tunnel.c | 4 | ||||
| -rw-r--r-- | src/modules/oss/module-oss.c | 8 | ||||
| -rw-r--r-- | src/modules/raop/module-raop-sink.c | 6 | ||||
| -rw-r--r-- | src/pulsecore/cli-command.c | 4 | ||||
| -rw-r--r-- | src/pulsecore/cli-text.c | 6 | ||||
| -rw-r--r-- | src/pulsecore/core.h | 1 | ||||
| -rw-r--r-- | src/pulsecore/protocol-native.c | 4 | ||||
| -rw-r--r-- | src/pulsecore/sink-input.c | 164 | ||||
| -rw-r--r-- | src/pulsecore/sink-input.h | 15 | ||||
| -rw-r--r-- | src/pulsecore/sink.c | 381 | ||||
| -rw-r--r-- | src/pulsecore/sink.h | 12 | 
16 files changed, 333 insertions, 286 deletions
| diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index 12538368..e3707ae7 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -1009,7 +1009,7 @@ static int mixer_callback(snd_mixer_elem_t *elem, unsigned int mask) {          return 0;      if (mask & SND_CTL_EVENT_MASK_VALUE) { -        pa_sink_get_volume(u->sink, TRUE, FALSE); +        pa_sink_get_volume(u->sink, TRUE);          pa_sink_get_mute(u->sink, TRUE);      } diff --git a/src/modules/module-device-restore.c b/src/modules/module-device-restore.c index 120b762c..da6c9666 100644 --- a/src/modules/module-device-restore.c +++ b/src/modules/module-device-restore.c @@ -218,7 +218,7 @@ static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint3          if (sink->save_volume) {              entry.channel_map = sink->channel_map; -            entry.volume = *pa_sink_get_volume(sink, FALSE, TRUE); +            entry.volume = *pa_sink_get_volume(sink, FALSE);              entry.volume_valid = TRUE;          } diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index 2bb8014d..fdfdc797 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -120,7 +120,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event                      pa_log("Failed to get sink '%s'", u->sink_name);                  else {                      int i; -                    pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE); +                    pa_cvolume cv = *pa_sink_get_volume(s, FALSE);  #define DELTA (PA_VOLUME_NORM/20) diff --git a/src/modules/module-match.c b/src/modules/module-match.c index 14e01127..0bd781d2 100644 --- a/src/modules/module-match.c +++ b/src/modules/module-match.c @@ -216,7 +216,7 @@ static void callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, v                  pa_cvolume cv;                  pa_log_debug("changing volume of sink input '%s' to 0x%03x", n, r->volume);                  pa_cvolume_set(&cv, si->sample_spec.channels, r->volume); -                pa_sink_input_set_volume(si, &cv, TRUE, TRUE); +                pa_sink_input_set_volume(si, &cv, TRUE, FALSE);              }          }      } diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index b30fae51..7be48700 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -102,7 +102,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event                      pa_log("Failed to get sink '%s'", u->sink_name);                  else {                      int i; -                    pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE); +                    pa_cvolume cv = *pa_sink_get_volume(s, FALSE);  #define DELTA (PA_VOLUME_NORM/20) @@ -115,7 +115,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event                                      cv.values[i] = PA_VOLUME_MAX;                              } -                            pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); +                            pa_sink_set_volume(s, &cv, TRUE, TRUE);                              break;                          case DOWN: @@ -126,7 +126,7 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event                                      cv.values[i] = PA_VOLUME_MUTED;                              } -                            pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); +                            pa_sink_set_volume(s, &cv, TRUE, TRUE);                              break;                          case MUTE_TOGGLE: diff --git a/src/modules/module-tunnel.c b/src/modules/module-tunnel.c index eaccea4e..5ccb81d0 100644 --- a/src/modules/module-tunnel.c +++ b/src/modules/module-tunnel.c @@ -1162,7 +1162,7 @@ static void sink_input_info_cb(pa_pdispatch *pd, uint32_t command,  uint32_t tag      pa_assert(u->sink);      if ((u->version < 11 || !!mute == !!u->sink->muted) && -        pa_cvolume_equal(&volume, &u->sink->virtual_volume)) +        pa_cvolume_equal(&volume, &u->sink->real_volume))          return;      pa_sink_volume_changed(u->sink, &volume); @@ -1763,7 +1763,7 @@ static void sink_set_volume(pa_sink *sink) {      pa_tagstruct_putu32(t, PA_COMMAND_SET_SINK_INPUT_VOLUME);      pa_tagstruct_putu32(t, tag = u->ctag++);      pa_tagstruct_putu32(t, u->device_index); -    pa_tagstruct_put_cvolume(t, &sink->virtual_volume); +    pa_tagstruct_put_cvolume(t, &sink->real_volume);      pa_pstream_send_tagstruct(u->pstream, t);  } diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c index 0848d43b..71536260 100644 --- a/src/modules/oss/module-oss.c +++ b/src/modules/oss/module-oss.c @@ -812,11 +812,11 @@ static void sink_get_volume(pa_sink *s) {      pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));      if (u->mixer_devmask & SOUND_MASK_VOLUME) -        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0) +        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_VOLUME, &s->sample_spec, &s->real_volume) >= 0)              return;      if (u->mixer_devmask & SOUND_MASK_PCM) -        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->virtual_volume) >= 0) +        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_PCM, &s->sample_spec, &s->real_volume) >= 0)              return;      pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); @@ -830,11 +830,11 @@ static void sink_set_volume(pa_sink *s) {      pa_assert(u->mixer_devmask & (SOUND_MASK_VOLUME|SOUND_MASK_PCM));      if (u->mixer_devmask & SOUND_MASK_VOLUME) -        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->virtual_volume) >= 0) +        if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_VOLUME, &s->sample_spec, &s->real_volume) >= 0)              return;      if (u->mixer_devmask & SOUND_MASK_PCM) -        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->virtual_volume) >= 0) +        if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_PCM, &s->sample_spec, &s->real_volume) >= 0)              return;      pa_log_info("Device doesn't support writing mixer settings: %s", pa_cstrerror(errno)); diff --git a/src/modules/raop/module-raop-sink.c b/src/modules/raop/module-raop-sink.c index 9699132d..ac48ab10 100644 --- a/src/modules/raop/module-raop-sink.c +++ b/src/modules/raop/module-raop-sink.c @@ -283,15 +283,15 @@ static void sink_set_volume_cb(pa_sink *s) {      /* Calculate the max volume of all channels.         We'll use this as our (single) volume on the APEX device and emulate         any variation in channel volumes in software */ -    v = pa_cvolume_max(&s->virtual_volume); +    v = pa_cvolume_max(&s->real_volume);      /* Create a pa_cvolume version of our single value */      pa_cvolume_set(&hw, s->sample_spec.channels, v);      /* Perform any software manipulation of the volume needed */ -    pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &hw); +    pa_sw_cvolume_divide(&s->soft_volume, &s->real_volume, &hw); -    pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); +    pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->real_volume));      pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &hw));      pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c index e2c3c066..6ec74647 100644 --- a/src/pulsecore/cli-command.c +++ b/src/pulsecore/cli-command.c @@ -530,7 +530,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu      }      pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume); -    pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE); +    pa_sink_set_volume(sink, &cvolume, TRUE, TRUE);      return 0;  } @@ -1586,7 +1586,7 @@ static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_b              nl = 1;          } -        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE, TRUE))); +        pa_strbuf_printf(buf, "set-sink-volume %s 0x%03x\n", sink->name, pa_cvolume_avg(pa_sink_get_volume(sink, FALSE)));          pa_strbuf_printf(buf, "set-sink-mute %s %s\n", sink->name, pa_yes_no(pa_sink_get_mute(sink, FALSE)));          pa_strbuf_printf(buf, "suspend-sink %s %s\n", sink->name, pa_yes_no(pa_sink_get_state(sink) == PA_SINK_SUSPENDED));      } diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c index a5530991..c7a178d6 100644 --- a/src/pulsecore/cli-text.c +++ b/src/pulsecore/cli-text.c @@ -262,10 +262,10 @@ char *pa_sink_list_to_string(pa_core *c) {              sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "",              sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "",              sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "", -            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE, FALSE)), +            pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)),              sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t        " : "", -            sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE, FALSE)) : "", -            pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE, FALSE), &sink->channel_map), +            sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "", +            pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map),              pa_volume_snprint(v, sizeof(v), sink->base_volume),              sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t             " : "",              sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "", diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h index e7abd61b..f6ec7122 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -83,7 +83,6 @@ typedef enum pa_core_hook {      PA_CORE_HOOK_SINK_INPUT_MOVE_FAIL,      PA_CORE_HOOK_SINK_INPUT_STATE_CHANGED,      PA_CORE_HOOK_SINK_INPUT_PROPLIST_CHANGED, -    PA_CORE_HOOK_SINK_INPUT_SET_VOLUME,      PA_CORE_HOOK_SINK_INPUT_SEND_EVENT,      PA_CORE_HOOK_SOURCE_OUTPUT_NEW,      PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE, diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c index 280707e2..b1285e15 100644 --- a/src/pulsecore/protocol-native.c +++ b/src/pulsecore/protocol-native.c @@ -2840,7 +2840,7 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin          PA_TAG_SAMPLE_SPEC, &fixed_ss,          PA_TAG_CHANNEL_MAP, &sink->channel_map,          PA_TAG_U32, sink->module ? sink->module->index : PA_INVALID_INDEX, -        PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE, FALSE), +        PA_TAG_CVOLUME, pa_sink_get_volume(sink, FALSE),          PA_TAG_BOOLEAN, pa_sink_get_mute(sink, FALSE),          PA_TAG_U32, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX,          PA_TAG_STRING, sink->monitor_source ? sink->monitor_source->name : NULL, @@ -3388,7 +3388,7 @@ static void command_set_volume(      if (sink) {          pa_log_debug("Client %s changes volume of sink %s.", client_name, sink->name); -        pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE); +        pa_sink_set_volume(sink, &volume, TRUE, TRUE);      } else if (source) {          pa_log_debug("Client %s changes volume of sink %s.", client_name, source->name);          pa_source_set_volume(source, &volume, TRUE); diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c index f6d9ac73..a29334f9 100644 --- a/src/pulsecore/sink-input.c +++ b/src/pulsecore/sink-input.c @@ -47,6 +47,7 @@  static PA_DEFINE_CHECK_TYPE(pa_sink_input, pa_msgobject);  static void sink_input_free(pa_object *o); +static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v);  pa_sink_input_new_data* pa_sink_input_new_data_init(pa_sink_input_new_data *data) {      pa_assert(data); @@ -270,18 +271,20 @@ int pa_sink_input_new(      i->channel_map = data->channel_map;      if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !data->volume_is_absolute) { +        pa_cvolume remapped; +          /* When the 'absolute' bool is not set then we'll treat the volume           * as relative to the sink volume even in flat volume mode */ - -        pa_cvolume v = data->sink->reference_volume; -        pa_cvolume_remap(&v, &data->sink->channel_map, &data->channel_map); -        pa_sw_cvolume_multiply(&i->virtual_volume, &data->volume, &v); +        remapped = data->sink->reference_volume; +        pa_cvolume_remap(&remapped, &data->sink->channel_map, &data->channel_map); +        pa_sw_cvolume_multiply(&i->volume, &data->volume, &remapped);      } else -        i->virtual_volume = data->volume; +        i->volume = data->volume;      i->volume_factor = data->volume_factor; -    pa_cvolume_init(&i->soft_volume); -    memset(i->relative_volume, 0, sizeof(i->relative_volume)); +    i->real_ratio = i->reference_ratio = data->volume; +    pa_cvolume_reset(&i->soft_volume, i->sample_spec.channels); +    pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels);      i->save_volume = data->save_volume;      i->save_sink = data->save_sink;      i->save_muted = data->save_muted; @@ -445,11 +448,8 @@ void pa_sink_input_unlink(pa_sink_input *i) {      if (linked && i->sink) {          /* We might need to update the sink's volume if we are in flat volume mode. */ -        if (i->sink->flags & PA_SINK_FLAT_VOLUME) { -            pa_cvolume new_volume; -            pa_sink_update_flat_volume(i->sink, &new_volume); -            pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); -        } +        if (i->sink->flags & PA_SINK_FLAT_VOLUME) +            pa_sink_set_volume(i->sink, NULL, FALSE, FALSE);          if (i->sink->asyncmsgq)              pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_REMOVE_INPUT, i, 0, NULL) == 0); @@ -526,12 +526,10 @@ void pa_sink_input_put(pa_sink_input *i) {      i->state = state;      /* We might need to update the sink's volume if we are in flat volume mode. */ -    if (i->sink->flags & PA_SINK_FLAT_VOLUME) { -        pa_cvolume new_volume; -        pa_sink_update_flat_volume(i->sink, &new_volume); -        pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); -    } else -        pa_sink_input_set_relative_volume(i, &i->virtual_volume); +    if (i->sink->flags & PA_SINK_FLAT_VOLUME) +        pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume); +    else +        set_real_ratio(i, &i->volume);      i->thread_info.soft_volume = i->soft_volume;      i->thread_info.muted = i->muted; @@ -910,6 +908,27 @@ pa_usec_t pa_sink_input_get_requested_latency(pa_sink_input *i) {  }  /* Called from main context */ +static void set_real_ratio(pa_sink_input *i, const pa_cvolume *v) { +    pa_sink_input_assert_ref(i); +    pa_assert_ctl_context(); +    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); +    pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec)); + +    /* This basically calculates: +     * +     * i->real_ratio := v +     * i->soft_volume := i->real_ratio * i->volume_factor */ + +    if (v) +        i->real_ratio = *v; +    else +        pa_cvolume_reset(&i->real_ratio, i->sample_spec.channels); + +    pa_sw_cvolume_multiply(&i->soft_volume, &i->real_ratio, &i->volume_factor); +    /* We don't copy the data to the thread_info data. That's left for someone else to do */ +} + +/* Called from main context */  void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute) {      pa_cvolume v; @@ -926,29 +945,24 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo          volume = pa_sw_cvolume_multiply(&v, &v, volume);      } -    if (pa_cvolume_equal(volume, &i->virtual_volume)) +    if (pa_cvolume_equal(volume, &i->volume)) { +        i->save_volume = i->save_volume || save;          return; +    } -    i->virtual_volume = *volume; +    i->volume = *volume;      i->save_volume = save; -    if (i->sink->flags & PA_SINK_FLAT_VOLUME) { -        pa_cvolume new_volume; - +    if (i->sink->flags & PA_SINK_FLAT_VOLUME)          /* We are in flat volume mode, so let's update all sink input           * volumes and update the flat volume of the sink */ -        pa_sink_update_flat_volume(i->sink, &new_volume); -        pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE); - -    } else { +        pa_sink_set_volume(i->sink, NULL, TRUE, save); +    else {          /* OK, we are in normal volume mode. The volume only affects           * ourselves */ -        pa_sink_input_set_relative_volume(i, volume); - -        /* Hooks have the ability to play games with i->soft_volume */ -        pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i); +        set_real_ratio(i, volume);          /* Copy the new soft_volume to the thread_info struct */          pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL) == 0); @@ -964,68 +978,15 @@ pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bo      pa_assert_ctl_context();      pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); -    if ((i->sink->flags & PA_SINK_FLAT_VOLUME) && !absolute) { -        pa_cvolume v = i->sink->reference_volume; -        pa_cvolume_remap(&v, &i->sink->channel_map, &i->channel_map); -        pa_sw_cvolume_divide(volume, &i->virtual_volume, &v); -    } else -        *volume = i->virtual_volume; +    if (absolute || !(i->sink->flags & PA_SINK_FLAT_VOLUME)) +        *volume = i->volume; +    else +        *volume = i->reference_ratio;      return volume;  }  /* Called from main context */ -pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v) { -    unsigned c; - -    pa_sink_input_assert_ref(i); -    pa_assert_ctl_context(); -    pa_assert(v); -    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); - -    /* This always returns the relative volume. Converts the float -     * version into a pa_cvolume */ - -    v->channels = i->sample_spec.channels; - -    for (c = 0; c < v->channels; c++) -        v->values[c] = pa_sw_volume_from_linear(i->relative_volume[c]); - -    return v; -} - -/* Called from main context */ -void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v) { -    unsigned c; -    pa_cvolume _v; - -    pa_sink_input_assert_ref(i); -    pa_assert_ctl_context(); -    pa_assert(PA_SINK_INPUT_IS_LINKED(i->state)); -    pa_assert(!v || pa_cvolume_compatible(v, &i->sample_spec)); - -    if (!v) -        v = pa_cvolume_reset(&_v, i->sample_spec.channels); - -    /* This basically calculates: -     * -     * i->relative_volume := v -     * i->soft_volume := i->relative_volume * i->volume_factor */ - -    i->soft_volume.channels = i->sample_spec.channels; - -    for (c = 0; c < i->sample_spec.channels; c++) { -        i->relative_volume[c] = pa_sw_volume_to_linear(v->values[c]); - -        i->soft_volume.values[c] = pa_sw_volume_from_linear( -                i->relative_volume[c] * -                pa_sw_volume_to_linear(i->volume_factor.values[c])); -    } - -    /* We don't copy the data to the thread_info data. That's left for someone else to do */ -} - -/* Called from main context */  void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save) {      pa_sink_input_assert_ref(i);      pa_assert_ctl_context(); @@ -1198,20 +1159,10 @@ int pa_sink_input_start_move(pa_sink_input *i) {      if (pa_sink_input_get_state(i) == PA_SINK_INPUT_CORKED)          pa_assert_se(i->sink->n_corked-- >= 1); -    if (i->sink->flags & PA_SINK_FLAT_VOLUME) { -        pa_cvolume new_volume; - -        /* Make the virtual volume relative */ -        pa_sink_input_get_relative_volume(i, &i->virtual_volume); - -        /* And reset the the relative volume */ -        pa_sink_input_set_relative_volume(i, NULL); - +    if (i->sink->flags & PA_SINK_FLAT_VOLUME)          /* We might need to update the sink's volume if we are in flat           * volume mode. */ -        pa_sink_update_flat_volume(i->sink, &new_volume); -        pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); -    } +        pa_sink_set_volume(i->sink, NULL, FALSE, FALSE);      pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0); @@ -1295,16 +1246,15 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {      pa_sink_update_status(dest);      if (i->sink->flags & PA_SINK_FLAT_VOLUME) { -        pa_cvolume new_volume; +        pa_cvolume remapped; -        /* Make relative volume absolute again */ -        pa_cvolume t = dest->reference_volume; -        pa_cvolume_remap(&t, &dest->channel_map, &i->channel_map); -        pa_sw_cvolume_multiply(&i->virtual_volume, &i->virtual_volume, &t); +        /* Make relative volumes absolute */ +        remapped = dest->reference_volume; +        pa_cvolume_remap(&remapped, &dest->channel_map, &i->channel_map); +        pa_sw_cvolume_multiply(&i->volume, &i->reference_ratio, &remapped);          /* We might need to update the sink's volume if we are in flat volume mode. */ -        pa_sink_update_flat_volume(i->sink, &new_volume); -        pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE); +        pa_sink_set_volume(i->sink, NULL, FALSE, i->save_volume);      }      pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0); diff --git a/src/pulsecore/sink-input.h b/src/pulsecore/sink-input.h index b5502c45..ea0f8c0e 100644 --- a/src/pulsecore/sink-input.h +++ b/src/pulsecore/sink-input.h @@ -94,10 +94,12 @@ struct pa_sink_input {      pa_sink_input *sync_prev, *sync_next;      /* Also see http://pulseaudio.org/wiki/InternalVolumes */ -    pa_cvolume virtual_volume;  /* The volume clients are informed about */ -    pa_cvolume volume_factor;   /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */ -    double relative_volume[PA_CHANNELS_MAX]; /* The calculated volume relative to the sink volume as linear factors. */ -    pa_cvolume soft_volume;     /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as relative_volume * volume_factor  */ +    pa_cvolume volume;             /* The volume clients are informed about */ +    pa_cvolume reference_ratio;    /* The ratio of the stream's volume to the sink's reference volume */ +    pa_cvolume real_ratio;         /* The ratio of the stream's volume to the sink's real volume */ +    pa_cvolume volume_factor;      /* An internally used volume factor that can be used by modules to apply effects and suchlike without having that visible to the outside */ +    pa_cvolume soft_volume;        /* The internal software volume we apply to all PCM data while it passes through. Usually calculated as real_ratio * volume_factor */ +      pa_bool_t muted:1;      /* if TRUE then the source we are connected to and/or the volume @@ -325,8 +327,6 @@ pa_usec_t pa_sink_input_get_latency(pa_sink_input *i, pa_usec_t *sink_latency);  void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_bool_t save, pa_bool_t absolute);  pa_cvolume *pa_sink_input_get_volume(pa_sink_input *i, pa_cvolume *volume, pa_bool_t absolute); -pa_cvolume *pa_sink_input_get_relative_volume(pa_sink_input *i, pa_cvolume *v); -  void pa_sink_input_set_mute(pa_sink_input *i, pa_bool_t mute, pa_bool_t save);  pa_bool_t pa_sink_input_get_mute(pa_sink_input *i); @@ -369,9 +369,6 @@ pa_bool_t pa_sink_input_safe_to_remove(pa_sink_input *i);  pa_memchunk* pa_sink_input_get_silence(pa_sink_input *i, pa_memchunk *ret); -/* To be used by sink.c only */ -void pa_sink_input_set_relative_volume(pa_sink_input *i, const pa_cvolume *v); -  #define pa_sink_input_assert_io_context(s) \      pa_assert(pa_thread_mq_get() || !PA_SINK_INPUT_IS_LINKED((s)->state)) diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c index 717584f2..1cce8e6b 100644 --- a/src/pulsecore/sink.c +++ b/src/pulsecore/sink.c @@ -212,7 +212,7 @@ pa_sink* pa_sink_new(          pa_cvolume_reset(&data->volume, data->sample_spec.channels);      pa_return_null_if_fail(pa_cvolume_valid(&data->volume)); -    pa_return_null_if_fail(data->volume.channels == data->sample_spec.channels); +    pa_return_null_if_fail(pa_cvolume_compatible(&data->volume, &data->sample_spec));      if (!data->muted_is_set)          data->muted = FALSE; @@ -249,7 +249,7 @@ pa_sink* pa_sink_new(      s->inputs = pa_idxset_new(NULL, NULL);      s->n_corked = 0; -    s->reference_volume = s->virtual_volume = data->volume; +    s->reference_volume = s->real_volume = data->volume;      pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);      s->base_volume = PA_VOLUME_NORM;      s->n_volume_steps = PA_VOLUME_NORM+1; @@ -434,6 +434,11 @@ void pa_sink_put(pa_sink* s) {      if ((s->flags & PA_SINK_DECIBEL_VOLUME) && s->core->flat_volumes)          s->flags |= PA_SINK_FLAT_VOLUME; +    /* We assume that if the sink implementor changed the default +     * volume he did so in real_volume, because that is the usual +     * place where he is supposed to place his changes.  */ +    s->reference_volume = s->real_volume; +      s->thread_info.soft_volume = s->soft_volume;      s->thread_info.soft_muted = s->muted; @@ -1212,105 +1217,144 @@ pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s) {      return usec;  } -static void compute_new_soft_volume(pa_sink_input *i, const pa_cvolume *new_volume) { -    unsigned c; +/* Called from main context */ +static void compute_reference_ratios(pa_sink *s) { +    uint32_t idx; +    pa_sink_input *i; + +    pa_sink_assert_ref(s); +    pa_assert_ctl_context(); +    pa_assert(PA_SINK_IS_LINKED(s->state)); +    pa_assert(s->flags & PA_SINK_FLAT_VOLUME); -    pa_sink_input_assert_ref(i); -    pa_assert(new_volume->channels == i->sample_spec.channels); +    PA_IDXSET_FOREACH(i, s->inputs, idx) { +        unsigned c; +        pa_cvolume remapped; -    /* -     * This basically calculates: -     * -     * i->relative_volume := i->virtual_volume / new_volume -     * i->soft_volume := i->relative_volume * i->volume_factor -     */ +        /* +         * Calculates the reference volume from the sink's reference +         * volume. This basically calculates: +         * +         * i->reference_ratio = i->volume / s->reference_volume +         */ -    /* The new sink volume passed in here must already be remapped to -     * the sink input's channel map! */ +        remapped = s->reference_volume; +        pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map); -    i->soft_volume.channels = i->sample_spec.channels; +        i->reference_ratio.channels = i->sample_spec.channels; -    for (c = 0; c < i->sample_spec.channels; c++) +        for (c = 0; c < i->sample_spec.channels; c++) { -        if (new_volume->values[c] <= PA_VOLUME_MUTED) -            /* We leave i->relative_volume untouched */ -            i->soft_volume.values[c] = PA_VOLUME_MUTED; -        else { -            i->relative_volume[c] = -                pa_sw_volume_to_linear(i->virtual_volume.values[c]) / -                pa_sw_volume_to_linear(new_volume->values[c]); +            /* We don't update when the sink volume is 0 anyway */ +            if (remapped.values[c] <= PA_VOLUME_MUTED) +                continue; -            i->soft_volume.values[c] = pa_sw_volume_from_linear( -                    i->relative_volume[c] * -                    pa_sw_volume_to_linear(i->volume_factor.values[c])); +            /* Don't update the reference ratio unless necessary */ +            if (pa_sw_volume_multiply( +                        i->reference_ratio.values[c], +                        remapped.values[c]) == i->volume.values[c]) +                continue; + +            i->reference_ratio.values[c] = pa_sw_volume_divide( +                    i->volume.values[c], +                    remapped.values[c]);          } +    } +} + +/* Called from main context */ +static void compute_real_ratios(pa_sink *s) { +    pa_sink_input *i; +    uint32_t idx; -    /* Hooks have the ability to play games with i->soft_volume */ -    pa_hook_fire(&i->core->hooks[PA_CORE_HOOK_SINK_INPUT_SET_VOLUME], i); +    pa_sink_assert_ref(s); +    pa_assert_ctl_context(); +    pa_assert(PA_SINK_IS_LINKED(s->state)); +    pa_assert(s->flags & PA_SINK_FLAT_VOLUME); -    /* We don't copy the soft_volume to the thread_info data -     * here. That must be done by the caller */ +    PA_IDXSET_FOREACH(i, s->inputs, idx) { +        unsigned c; +        pa_cvolume remapped; + +        /* +         * This basically calculates: +         * +         * i->real_ratio := i->volume / s->real_volume +         * i->soft_volume := i->real_ratio * i->volume_factor +         */ + +        remapped = s->real_volume; +        pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map); + +        i->real_ratio.channels = i->sample_spec.channels; +        i->soft_volume.channels = i->sample_spec.channels; + +        for (c = 0; c < i->sample_spec.channels; c++) { + +            if (remapped.values[c] <= PA_VOLUME_MUTED) { +                /* We leave i->real_ratio untouched */ +                i->soft_volume.values[c] = PA_VOLUME_MUTED; +                continue; +            } + +            /* Don't lose accuracy unless necessary */ +            if (pa_sw_volume_multiply( +                        i->real_ratio.values[c], +                        remapped.values[c]) != i->volume.values[c]) + +                i->real_ratio.values[c] = pa_sw_volume_divide( +                        i->volume.values[c], +                        remapped.values[c]); + +            i->soft_volume.values[c] = pa_sw_volume_multiply( +                    i->real_ratio.values[c], +                    i->volume_factor.values[c]); +        } + +        /* We don't copy the soft_volume to the thread_info data +         * here. That must be done by the caller */ +    }  }  /* Called from main thread */ -void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume) { +static void compute_real_volume(pa_sink *s) {      pa_sink_input *i;      uint32_t idx;      pa_sink_assert_ref(s);      pa_assert_ctl_context(); -    pa_assert(new_volume);      pa_assert(PA_SINK_IS_LINKED(s->state));      pa_assert(s->flags & PA_SINK_FLAT_VOLUME); -    /* This is called whenever a sink input volume changes or a sink -     * input is added/removed and we might need to fix up the sink -     * volume accordingly. Please note that we don't actually update -     * the sinks volume here, we only return how it needs to be -     * updated. The caller should then call pa_sink_set_volume().*/ +    /* This determines the maximum volume of all streams and sets +     * s->real_volume accordingly. */      if (pa_idxset_isempty(s->inputs)) {          /* In the special case that we have no sink input we leave the           * volume unmodified. */ -        *new_volume = s->reference_volume; +        s->real_volume = s->reference_volume;          return;      } -    pa_cvolume_mute(new_volume, s->channel_map.channels); +    pa_cvolume_mute(&s->real_volume, s->channel_map.channels);      /* First let's determine the new maximum volume of all inputs       * connected to this sink */ -    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { -        unsigned c; -        pa_cvolume remapped_volume; - -        remapped_volume = i->virtual_volume; -        pa_cvolume_remap(&remapped_volume, &i->channel_map, &s->channel_map); +    PA_IDXSET_FOREACH(i, s->inputs, idx) { +        pa_cvolume remapped; -        for (c = 0; c < new_volume->channels; c++) -            if (remapped_volume.values[c] > new_volume->values[c]) -                new_volume->values[c] = remapped_volume.values[c]; +        remapped = i->volume; +        pa_cvolume_remap(&remapped, &i->channel_map, &s->channel_map); +        pa_cvolume_merge(&s->real_volume, &s->real_volume, &remapped);      } -    /* Then, let's update the soft volumes of all inputs connected -     * to this sink */ -    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { -        pa_cvolume remapped_new_volume; - -        remapped_new_volume = *new_volume; -        pa_cvolume_remap(&remapped_new_volume, &s->channel_map, &i->channel_map); -        compute_new_soft_volume(i, &remapped_new_volume); - -        /* We don't copy soft_volume to the thread_info data here -         * (i.e. issue PA_SINK_INPUT_MESSAGE_SET_VOLUME) because we -         * want the update to be atomically with the sink volume -         * update, hence we do it within the pa_sink_set_volume() call -         * below */ -    } +    /* Then, let's update the real ratios/soft volumes of all inputs +     * connected to this sink */ +    compute_real_ratios(s);  }  /* Called from main thread */ -void pa_sink_propagate_flat_volume(pa_sink *s) { +static void propagate_reference_volume(pa_sink *s) {      pa_sink_input *i;      uint32_t idx; @@ -1323,64 +1367,77 @@ void pa_sink_propagate_flat_volume(pa_sink *s) {       * caused by a sink input volume change. We need to fix up the       * sink input volumes accordingly */ -    for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) { -        pa_cvolume sink_volume, new_virtual_volume; -        unsigned c; - -        /* This basically calculates i->virtual_volume := i->relative_volume * s->virtual_volume  */ - -        sink_volume = s->virtual_volume; -        pa_cvolume_remap(&sink_volume, &s->channel_map, &i->channel_map); - -        for (c = 0; c < i->sample_spec.channels; c++) -            new_virtual_volume.values[c] = pa_sw_volume_from_linear( -                    i->relative_volume[c] * -                    pa_sw_volume_to_linear(sink_volume.values[c])); +    PA_IDXSET_FOREACH(i, s->inputs, idx) { +        pa_cvolume old_volume, remapped; -        new_virtual_volume.channels = i->sample_spec.channels; +        old_volume = i->volume; -        if (!pa_cvolume_equal(&new_virtual_volume, &i->virtual_volume)) { -            i->virtual_volume = new_virtual_volume; +        /* This basically calculates: +         * +         * i->volume := s->reference_volume * i->reference_ratio  */ -            /* Hmm, the soft volume might no longer actually match -             * what has been chosen as new virtual volume here, -             * especially when the old volume was -             * PA_VOLUME_MUTED. Hence let's recalculate the soft -             * volumes here. */ -            compute_new_soft_volume(i, &sink_volume); +        remapped = s->reference_volume; +        pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map); +        pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio); -            /* The virtual volume changed, let's tell people so */ +        /* The reference volume changed, let's tell people so */ +        if (!pa_cvolume_equal(&old_volume, &i->volume))              pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index); -        }      } - -    /* If the soft_volume of any of the sink inputs got changed, let's -     * make sure the thread copies are synced up. */ -    pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SYNC_VOLUMES, NULL, 0, NULL) == 0);  }  /* Called from main thread */ -void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save) { -    pa_bool_t virtual_volume_changed; +void pa_sink_set_volume( +        pa_sink *s, +        const pa_cvolume *volume, +        pa_bool_t sendmsg, +        pa_bool_t save) { + +    pa_cvolume old_reference_volume; +    pa_bool_t reference_changed;      pa_sink_assert_ref(s);      pa_assert_ctl_context();      pa_assert(PA_SINK_IS_LINKED(s->state)); -    pa_assert(volume); -    pa_assert(pa_cvolume_valid(volume)); -    pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); +    pa_assert(!volume || pa_cvolume_valid(volume)); +    pa_assert(!volume || pa_cvolume_compatible(volume, &s->sample_spec)); +    pa_assert(volume || (s->flags & PA_SINK_FLAT_VOLUME)); + +    /* If volume is NULL we synchronize the sink's real and reference +     * volumes with the stream volumes. If it is not NULL we update +     * the reference_volume with it. */ + +    old_reference_volume = s->reference_volume; + +    if (volume) { + +        s->reference_volume = *volume; + +        if (s->flags & PA_SINK_FLAT_VOLUME) { +            /* OK, propagate this volume change back to the inputs */ +            propagate_reference_volume(s); + +            /* And now recalculate the real volume */ +            compute_real_volume(s); +        } else +            s->real_volume = s->reference_volume; + +    } else { +        pa_assert(s->flags & PA_SINK_FLAT_VOLUME); + +        /* Ok, let's determine the new real volume */ +        compute_real_volume(s); -    virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume); -    s->virtual_volume = *volume; -    s->save_volume = (!virtual_volume_changed && s->save_volume) || save; +        /* Let's 'push' the reference volume if necessary */ +        pa_cvolume_merge(&s->reference_volume, &s->reference_volume, &s->real_volume); -    if (become_reference) -        s->reference_volume = s->virtual_volume; +        /* We need to fix the reference ratios of all streams now that +         * we changed the reference volume */ +        compute_reference_ratios(s); +    } -    /* Propagate this volume change back to the inputs */ -    if (virtual_volume_changed) -        if (propagate && (s->flags & PA_SINK_FLAT_VOLUME)) -            pa_sink_propagate_flat_volume(s); +    reference_changed = !pa_cvolume_equal(&old_reference_volume, &s->reference_volume); +    s->save_volume = (!reference_changed && s->save_volume) || save;      if (s->set_volume) {          /* If we have a function set_volume(), then we do not apply a @@ -1393,13 +1450,13 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat      } else          /* If we have no function set_volume(), then the soft volume           * becomes the virtual volume */ -        s->soft_volume = s->virtual_volume; +        s->soft_volume = s->real_volume;      /* This tells the sink that soft and/or virtual volume changed */      if (sendmsg)          pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); -    if (virtual_volume_changed) +    if (reference_changed)          pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  } @@ -1407,67 +1464,114 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat  void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume) {      pa_sink_assert_ref(s);      pa_assert_ctl_context(); -    pa_assert(volume); -    s->soft_volume = *volume; +    if (!volume) +        pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); +    else +        s->soft_volume = *volume;      if (PA_SINK_IS_LINKED(s->state))          pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0);      else -        s->thread_info.soft_volume = *volume; +        s->thread_info.soft_volume = s->soft_volume;  } -/* Called from main thread */ -const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_bool_t reference) { +static void propagate_real_volume(pa_sink *s, const pa_cvolume *old_real_volume) { +    pa_sink_input *i; +    uint32_t idx; +    pa_cvolume old_reference_volume; +      pa_sink_assert_ref(s);      pa_assert_ctl_context();      pa_assert(PA_SINK_IS_LINKED(s->state)); -    if (s->refresh_volume || force_refresh) { -        struct pa_cvolume old_virtual_volume = s->virtual_volume; +    /* This is called when the hardware's real volume changes due to +     * some external event. We copy the real volume into our +     * reference volume and then rebuild the stream volumes based on +     * i->real_ratio which should stay fixed. */ -        if (s->get_volume) -            s->get_volume(s); +    if (pa_cvolume_equal(old_real_volume, &s->real_volume)) +        return; -        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); +    old_reference_volume = s->reference_volume; -        if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) { +    /* 1. Make the real volume the reference volume */ +    s->reference_volume = s->real_volume; -            s->reference_volume = s->virtual_volume; +    if (s->flags & PA_SINK_FLAT_VOLUME) { -            /* Something got changed in the hardware. It probably -             * makes sense to save changed hw settings given that hw -             * volume changes not triggered by PA are almost certainly -             * done by the user. */ -            s->save_volume = TRUE; +        PA_IDXSET_FOREACH(i, s->inputs, idx) { +            pa_cvolume old_volume, remapped; -            if (s->flags & PA_SINK_FLAT_VOLUME) -                pa_sink_propagate_flat_volume(s); +            old_volume = i->volume; -            pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +            /* 2. Since the sink's reference and real volumes are equal +             * now our ratios should be too. */ +            i->reference_ratio = i->real_ratio; + +            /* 3. Recalculate the new stream reference volume based on the +             * reference ratio and the sink's reference volume. +             * +             * This basically calculates: +             * +             * i->volume = s->reference_volume * i->reference_ratio +             * +             * This is identical to propagate_reference_volume() */ +            remapped = s->reference_volume; +            pa_cvolume_remap(&remapped, &s->channel_map, &i->channel_map); +            pa_sw_cvolume_multiply(&i->volume, &remapped, &i->reference_ratio); + +            /* Notify if something changed */ +            if (!pa_cvolume_equal(&old_volume, &i->volume)) +                pa_subscription_post(i->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE, i->index);          }      } -    return reference ? &s->reference_volume : &s->virtual_volume; +    /* Something got changed in the hardware. It probably makes sense +     * to save changed hw settings given that hw volume changes not +     * triggered by PA are almost certainly done by the user. */ +    s->save_volume = TRUE; + +    if (!pa_cvolume_equal(&old_reference_volume, &s->reference_volume)) +        pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);  }  /* Called from main thread */ -void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) { +const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh) {      pa_sink_assert_ref(s);      pa_assert_ctl_context();      pa_assert(PA_SINK_IS_LINKED(s->state)); -    /* The sink implementor may call this if the volume changed to make sure everyone is notified */ -    if (pa_cvolume_equal(&s->virtual_volume, new_volume)) -        return; +    if (s->refresh_volume || force_refresh) { +        struct pa_cvolume old_real_volume; -    s->reference_volume = s->virtual_volume = *new_volume; -    s->save_volume = TRUE; +        old_real_volume = s->real_volume; -    if (s->flags & PA_SINK_FLAT_VOLUME) -        pa_sink_propagate_flat_volume(s); +        if (s->get_volume) +            s->get_volume(s); -    pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); +        pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SINK_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); + +        propagate_real_volume(s, &old_real_volume); +    } + +    return &s->reference_volume; +} + +/* Called from main thread */ +void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_real_volume) { +    pa_cvolume old_real_volume; + +    pa_sink_assert_ref(s); +    pa_assert_ctl_context(); +    pa_assert(PA_SINK_IS_LINKED(s->state)); + +    /* The sink implementor may call this if the volume changed to make sure everyone is notified */ + +    old_real_volume = s->real_volume; +    s->real_volume = *new_real_volume; + +    propagate_real_volume(s, &old_real_volume);  }  /* Called from main thread */ @@ -1516,7 +1620,6 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {          }      } -      return s->muted;  } diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h index 3cd7e59d..936d1c2a 100644 --- a/src/pulsecore/sink.h +++ b/src/pulsecore/sink.h @@ -90,9 +90,10 @@ struct pa_sink {      unsigned n_volume_steps; /* shall be constant */      /* Also see http://pulseaudio.org/wiki/InternalVolumes */ -    pa_cvolume virtual_volume;   /* The volume clients are informed about */ -    pa_cvolume reference_volume; /* The volume taken as refernce base for relative sink input volumes */ +    pa_cvolume reference_volume; /* The volume exported and taken as reference base for relative sink input volumes */ +    pa_cvolume real_volume;      /* The volume that the hardware is configured to  */      pa_cvolume soft_volume;      /* The internal software volume we apply to all PCM data while it passes through */ +      pa_bool_t muted:1;      pa_bool_t refresh_volume:1; @@ -303,11 +304,8 @@ int pa_sink_update_status(pa_sink*s);  int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause);  int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause); -void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume); -void pa_sink_propagate_flat_volume(pa_sink *s); - -void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save); -const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference); +void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t sendmsg, pa_bool_t save); +const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh);  void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save);  pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh); | 
