diff options
author | Tanu Kaskinen <tanuk@iki.fi> | 2009-08-19 09:20:02 +0300 |
---|---|---|
committer | Tanu Kaskinen <tanuk@iki.fi> | 2009-08-19 09:20:02 +0300 |
commit | 292d6dcb5f1568fdc66a813cc464c66571092991 (patch) | |
tree | 1786a2b46e93e97a2e012faf40f46ea00d1cae80 /src | |
parent | b4e0d5d1e17409557d21dae9a770d1429e17cb15 (diff) | |
parent | f4f16ab10d31e61f1ca8867c2dad9874025637f8 (diff) |
Merge branch 'master' of git://0pointer.de/pulseaudio into dbus-work
Diffstat (limited to 'src')
34 files changed, 737 insertions, 492 deletions
diff --git a/src/map-file b/src/map-file index dafef48f..54c518ec 100644 --- a/src/map-file +++ b/src/map-file @@ -123,13 +123,18 @@ pa_cvolume_avg_mask; pa_cvolume_channels_equal_to; pa_cvolume_compatible; pa_cvolume_compatible_with_channel_map; +pa_cvolume_dec; pa_cvolume_equal; pa_cvolume_get_balance; pa_cvolume_get_fade; pa_cvolume_get_position; +pa_cvolume_inc; pa_cvolume_init; pa_cvolume_max; pa_cvolume_max_mask; +pa_cvolume_merge; +pa_cvolume_min; +pa_cvolume_min_mask; pa_cvolume_remap; pa_cvolume_scale; pa_cvolume_scale_mask; diff --git a/src/modules/alsa/alsa-sink.c b/src/modules/alsa/alsa-sink.c index a91b4b8a..e3707ae7 100644 --- a/src/modules/alsa/alsa-sink.c +++ b/src/modules/alsa/alsa-sink.c @@ -68,6 +68,8 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms -- Sleep at least 10ms on each iteration */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms -- Wakeup at least this long before the buffer runs empty*/ +#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) /* don't require volume adjustments to be perfectly correct. don't necessarily extend granularity in software unless the differences get greater than this level */ + struct userdata { pa_core *core; pa_module *module; @@ -1007,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); } @@ -1034,15 +1036,11 @@ static void sink_get_volume_cb(pa_sink *s) { if (pa_cvolume_equal(&u->hardware_volume, &r)) return; - s->virtual_volume = u->hardware_volume = r; - - if (u->mixer_path->has_dB) { - pa_cvolume reset; + s->real_volume = u->hardware_volume = r; - /* Hmm, so the hardware volume changed, let's reset our software volume */ - pa_cvolume_reset(&reset, s->sample_spec.channels); - pa_sink_set_soft_volume(s, &reset); - } + /* Hmm, so the hardware volume changed, let's reset our software volume */ + if (u->mixer_path->has_dB) + pa_sink_set_soft_volume(s, NULL); } static void sink_set_volume_cb(pa_sink *s) { @@ -1055,7 +1053,7 @@ static void sink_set_volume_cb(pa_sink *s) { pa_assert(u->mixer_handle); /* Shift up by the base volume */ - pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); + pa_sw_cvolume_divide_scalar(&r, &s->real_volume, s->base_volume); if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1066,13 +1064,26 @@ static void sink_set_volume_cb(pa_sink *s) { u->hardware_volume = r; if (u->mixer_path->has_dB) { + pa_cvolume new_soft_volume; + pa_bool_t accurate_enough; /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); + pa_sw_cvolume_divide(&new_soft_volume, &s->real_volume, &u->hardware_volume); + + /* If the adjustment to do in software is only minimal we + * can skip it. That saves us CPU at the expense of a bit of + * accuracy */ + accurate_enough = + (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) && + (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY)); - 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), &u->hardware_volume)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume), + pa_yes_no(accurate_enough)); + + if (!accurate_enough) + s->soft_volume = new_soft_volume; } else { pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); @@ -1080,7 +1091,7 @@ static void sink_set_volume_cb(pa_sink *s) { /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ - s->virtual_volume = r; + s->real_volume = r; } } diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index 9a51f857..7da37553 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -65,6 +65,8 @@ #define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ #define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ +#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) + struct userdata { pa_core *core; pa_module *module; @@ -987,15 +989,11 @@ static void source_get_volume_cb(pa_source *s) { if (pa_cvolume_equal(&u->hardware_volume, &r)) return; - s->virtual_volume = u->hardware_volume = r; - - if (u->mixer_path->has_dB) { - pa_cvolume reset; + s->volume = u->hardware_volume = r; - /* Hmm, so the hardware volume changed, let's reset our software volume */ - pa_cvolume_reset(&reset, s->sample_spec.channels); - pa_source_set_soft_volume(s, &reset); - } + /* Hmm, so the hardware volume changed, let's reset our software volume */ + if (u->mixer_path->has_dB) + pa_source_set_soft_volume(s, NULL); } static void source_set_volume_cb(pa_source *s) { @@ -1008,7 +1006,7 @@ static void source_set_volume_cb(pa_source *s) { pa_assert(u->mixer_handle); /* Shift up by the base volume */ - pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); + pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume); if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -1019,13 +1017,26 @@ static void source_set_volume_cb(pa_source *s) { u->hardware_volume = r; if (u->mixer_path->has_dB) { + pa_cvolume new_soft_volume; + pa_bool_t accurate_enough; /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); + pa_sw_cvolume_divide(&new_soft_volume, &s->volume, &u->hardware_volume); + + /* If the adjustment to do in software is only minimal we + * can skip it. That saves us CPU at the expense of a bit of + * accuracy */ + accurate_enough = + (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) && + (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY)); - 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->volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume), + pa_yes_no(accurate_enough)); + + if (!accurate_enough) + s->soft_volume = new_soft_volume; } else { pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); @@ -1033,7 +1044,7 @@ static void source_set_volume_cb(pa_source *s) { /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ - s->virtual_volume = r; + s->volume = r; } } diff --git a/src/modules/bluetooth/bluetooth-util.c b/src/modules/bluetooth/bluetooth-util.c index 16c29248..f576823d 100644 --- a/src/modules/bluetooth/bluetooth-util.c +++ b/src/modules/bluetooth/bluetooth-util.c @@ -758,7 +758,7 @@ pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) { if (pa_dbus_add_matches( pa_dbus_connection_get(y->connection), &err, - "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'", + "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'", @@ -809,7 +809,7 @@ void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) { if (y->connection) { pa_dbus_remove_matches(pa_dbus_connection_get(y->connection), - "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'", + "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'", "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'", "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'", diff --git a/src/modules/bluetooth/module-bluetooth-device.c b/src/modules/bluetooth/module-bluetooth-device.c index d6321fc4..4e23862c 100644 --- a/src/modules/bluetooth/module-bluetooth-device.c +++ b/src/modules/bluetooth/module-bluetooth-device.c @@ -1476,12 +1476,12 @@ static void sink_set_volume_cb(pa_sink *s) { if (u->profile != PROFILE_HSP) return; - gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM; + gain = (pa_cvolume_max(&s->real_volume) * 15) / PA_VOLUME_NORM; if (gain > 15) gain = 15; - pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); + pa_cvolume_set(&s->real_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetSpeakerGain")); pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); @@ -1500,12 +1500,12 @@ static void source_set_volume_cb(pa_source *s) { if (u->profile != PROFILE_HSP) return; - gain = (pa_cvolume_max(&s->virtual_volume) * 15) / PA_VOLUME_NORM; + gain = (pa_cvolume_max(&s->volume) * 15) / PA_VOLUME_NORM; if (gain > 15) gain = 15; - pa_cvolume_set(&s->virtual_volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); + pa_cvolume_set(&s->volume, u->sample_spec.channels, (pa_volume_t) (gain * PA_VOLUME_NORM / 15)); pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->path, "org.bluez.Headset", "SetMicrophoneGain")); pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_UINT16, &gain, DBUS_TYPE_INVALID)); diff --git a/src/modules/module-console-kit.c b/src/modules/module-console-kit.c index a666073c..103f5c48 100644 --- a/src/modules/module-console-kit.c +++ b/src/modules/module-console-kit.c @@ -187,7 +187,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo } add_session(u, path); - return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(message, "org.freedesktop.ConsoleKit.Seat", "SessionRemoved")) { @@ -202,7 +201,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo } remove_session(u, path); - return DBUS_HANDLER_RESULT_HANDLED; } finish: 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-hal-detect.c b/src/modules/module-hal-detect.c index ec370d61..6034d0ee 100644 --- a/src/modules/module-hal-detect.c +++ b/src/modules/module-hal-detect.c @@ -623,8 +623,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo } - return DBUS_HANDLER_RESULT_HANDLED; - } else if (dbus_message_is_signal(message, "org.pulseaudio.Server", "DirtyGiveUpMessage")) { /* We use this message to avoid a dirty race condition when we get an ACLAdded message before the previously owning PA @@ -668,7 +666,6 @@ static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *message, vo /* Yes, we don't check the UDI for validity, but hopefully HAL will */ device_added_cb(u->context, udi); - return DBUS_HANDLER_RESULT_HANDLED; } finish: diff --git a/src/modules/module-lirc.c b/src/modules/module-lirc.c index 06efeb8f..d0e902f6 100644 --- a/src/modules/module-lirc.c +++ b/src/modules/module-lirc.c @@ -63,6 +63,8 @@ struct userdata { float mute_toggle_save; }; +#define DELTA (PA_VOLUME_NORM/20) + static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) { struct userdata *u = userdata; char *name = NULL, *code = NULL; @@ -119,32 +121,17 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) pa_log("Failed to get sink '%s'", u->sink_name); else { - int i; - pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE); - -#define DELTA (PA_VOLUME_NORM/20) + pa_cvolume cv = *pa_sink_get_volume(s, FALSE); switch (volchange) { case UP: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] < PA_VOLUME_MAX - DELTA) - cv.values[i] += DELTA; - else - cv.values[i] = PA_VOLUME_MAX; - } - - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); + pa_cvolume_inc(&cv, DELTA); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case DOWN: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] > DELTA) - cv.values[i] -= DELTA; - else - cv.values[i] = PA_VOLUME_MUTED; - } - - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); + pa_cvolume_dec(&cv, DELTA); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case MUTE: @@ -156,7 +143,6 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event break; case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE); break; diff --git a/src/modules/module-match.c b/src/modules/module-match.c index 625f2a8b..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); } } } @@ -243,6 +243,9 @@ int pa__init(pa_module*m) { if (load_rules(u, pa_modargs_get_value(ma, "table", NULL)) < 0) goto fail; + /* FIXME: Doing this asynchronously is just broken. This needs to + * use a hook! */ + u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT, callback, u); pa_modargs_free(ma); diff --git a/src/modules/module-mmkbd-evdev.c b/src/modules/module-mmkbd-evdev.c index b30fae51..516bf413 100644 --- a/src/modules/module-mmkbd-evdev.c +++ b/src/modules/module-mmkbd-evdev.c @@ -65,6 +65,8 @@ struct userdata { pa_module *module; }; +#define DELTA (PA_VOLUME_NORM/20) + static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void*userdata) { struct userdata *u = userdata; @@ -85,14 +87,27 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event } if (ev.type == EV_KEY && (ev.value == 1 || ev.value == 2)) { - enum { INVALID, UP, DOWN, MUTE_TOGGLE } volchange = INVALID; + enum { + INVALID, + UP, + DOWN, + MUTE_TOGGLE + } volchange = INVALID; pa_log_debug("Key code=%u, value=%u", ev.code, ev.value); switch (ev.code) { - case KEY_VOLUMEDOWN: volchange = DOWN; break; - case KEY_VOLUMEUP: volchange = UP; break; - case KEY_MUTE: volchange = MUTE_TOGGLE; break; + case KEY_VOLUMEDOWN: + volchange = DOWN; + break; + + case KEY_VOLUMEUP: + volchange = UP; + break; + + case KEY_MUTE: + volchange = MUTE_TOGGLE; + break; } if (volchange != INVALID) { @@ -101,36 +116,20 @@ static void io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK))) pa_log("Failed to get sink '%s'", u->sink_name); else { - int i; - pa_cvolume cv = *pa_sink_get_volume(s, FALSE, FALSE); - -#define DELTA (PA_VOLUME_NORM/20) + pa_cvolume cv = *pa_sink_get_volume(s, FALSE); switch (volchange) { case UP: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] < PA_VOLUME_MAX - DELTA) - cv.values[i] += DELTA; - else - cv.values[i] = PA_VOLUME_MAX; - } - - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); + pa_cvolume_inc(&cv, DELTA); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case DOWN: - for (i = 0; i < cv.channels; i++) { - if (cv.values[i] > DELTA) - cv.values[i] -= DELTA; - else - cv.values[i] = PA_VOLUME_MUTED; - } - - pa_sink_set_volume(s, &cv, TRUE, TRUE, TRUE, TRUE); + pa_cvolume_dec(&cv, DELTA); + pa_sink_set_volume(s, &cv, TRUE, TRUE); break; case MUTE_TOGGLE: - pa_sink_set_mute(s, !pa_sink_get_mute(s, FALSE), TRUE); break; 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/module-udev-detect.c b/src/modules/module-udev-detect.c index 11de1ccb..0b30fd54 100644 --- a/src/modules/module-udev-detect.c +++ b/src/modules/module-udev-detect.c @@ -39,6 +39,9 @@ PA_MODULE_AUTHOR("Lennart Poettering"); PA_MODULE_DESCRIPTION("Detect available audio hardware and load matching drivers"); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_LOAD_ONCE(TRUE); +PA_MODULE_USAGE( + "tsched=<enable system timer based scheduling mode?> " + "ignore_dB=<ignore dB information from the device?>"); struct device { char *path; @@ -50,7 +53,9 @@ struct device { struct userdata { pa_core *core; pa_hashmap *devices; - pa_bool_t use_tsched; + + pa_bool_t use_tsched:1; + pa_bool_t ignore_dB:1; struct udev* udev; struct udev_monitor *monitor; @@ -62,6 +67,7 @@ struct userdata { static const char* const valid_modargs[] = { "tsched", + "ignore_dB", NULL }; @@ -140,12 +146,14 @@ static void card_changed(struct userdata *u, struct udev_device *dev) { args = pa_sprintf_malloc("device_id=\"%s\" " "name=\"%s\" " "card_name=\"%s\" " - "tsched=%i " + "tsched=%s " + "ignore_dB=%s " "card_properties=\"module-udev-detect.discovered=1\"", path_get_card_id(path), n, card_name, - (int) u->use_tsched); + pa_yes_no(u->use_tsched), + pa_yes_no(u->ignore_dB)); pa_log_debug("Loading module-alsa-card with arguments '%s'", args); m = pa_module_load(u->core, "module-alsa-card", args); @@ -364,6 +372,7 @@ int pa__init(pa_module *m) { struct udev_enumerate *enumerate = NULL; struct udev_list_entry *item = NULL, *first = NULL; int fd; + pa_bool_t use_tsched = TRUE, ignore_dB = FALSE; pa_assert(m); @@ -375,13 +384,19 @@ int pa__init(pa_module *m) { m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); - u->use_tsched = TRUE; u->inotify_fd = -1; - if (pa_modargs_get_value_boolean(ma, "tsched", &u->use_tsched) < 0) { - pa_log("Failed to parse tsched argument."); + if (pa_modargs_get_value_boolean(ma, "tsched", &use_tsched) < 0) { + pa_log("Failed to parse tsched= argument."); + goto fail; + } + u->use_tsched = use_tsched; + + if (pa_modargs_get_value_boolean(ma, "ignore_dB", &ignore_dB) < 0) { + pa_log("Failed to parse ignore_dB= argument."); goto fail; } + u->ignore_dB = ignore_dB; if (!(u->udev = udev_new())) { pa_log("Failed to initialize udev library."); diff --git a/src/modules/oss/module-oss.c b/src/modules/oss/module-oss.c index c44b882b..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)); @@ -848,11 +848,11 @@ static void source_get_volume(pa_source *s) { pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); if (u->mixer_devmask & SOUND_MASK_IGAIN) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_IGAIN, &s->sample_spec, &s->volume) >= 0) return; if (u->mixer_devmask & SOUND_MASK_RECLEV) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_READ_RECLEV, &s->sample_spec, &s->volume) >= 0) return; pa_log_info("Device doesn't support reading mixer settings: %s", pa_cstrerror(errno)); @@ -866,11 +866,11 @@ static void source_set_volume(pa_source *s) { pa_assert(u->mixer_devmask & (SOUND_MASK_IGAIN|SOUND_MASK_RECLEV)); if (u->mixer_devmask & SOUND_MASK_IGAIN) - if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_set_volume(u->mixer_fd, SOUND_MIXER_WRITE_IGAIN, &s->sample_spec, &s->volume) >= 0) return; if (u->mixer_devmask & SOUND_MASK_RECLEV) - if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->virtual_volume) >= 0) + if (pa_oss_get_volume(u->mixer_fd, SOUND_MIXER_WRITE_RECLEV, &s->sample_spec, &s->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/modules/reserve-monitor.c b/src/modules/reserve-monitor.c index 13ecde2b..ab453e61 100644 --- a/src/modules/reserve-monitor.c +++ b/src/modules/reserve-monitor.c @@ -38,6 +38,7 @@ struct rm_monitor { char *device_name; char *service_name; + char *match; DBusConnection *connection; @@ -51,12 +52,18 @@ struct rm_monitor { #define SERVICE_PREFIX "org.freedesktop.ReserveDevice1." +#define SERVICE_FILTER \ + "type='signal'," \ + "sender='" DBUS_SERVICE_DBUS "'," \ + "interface='" DBUS_INTERFACE_DBUS "'," \ + "member='NameOwnerChanged'," \ + "arg0='%s'" + static DBusHandlerResult filter_handler( DBusConnection *c, DBusMessage *s, void *userdata) { - DBusMessage *reply; rm_monitor *m; DBusError error; @@ -97,31 +104,10 @@ static DBusHandlerResult filter_handler( } } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - invalid: - if (!(reply = dbus_message_new_error( - s, - DBUS_ERROR_INVALID_ARGS, - "Invalid arguments"))) - goto oom; - - if (!dbus_connection_send(c, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } int rm_watch( @@ -175,11 +161,13 @@ int rm_watch( m->filtering = 1; - dbus_bus_add_match(m->connection, - "type='signal'," - "sender='" DBUS_SERVICE_DBUS "'," - "interface='" DBUS_INTERFACE_DBUS "'," - "member='NameOwnerChanged'", error); + if (!(m->match = malloc(sizeof(SERVICE_FILTER) - 2 + strlen(m->service_name)))) { + r = -ENOMEM; + goto fail; + } + + sprintf(m->match, SERVICE_FILTER, m->service_name); + dbus_bus_add_match(m->connection, m->match, error); if (dbus_error_is_set(error)) { r = -EIO; @@ -220,10 +208,8 @@ void rm_release(rm_monitor *m) { if (m->matching) dbus_bus_remove_match( m->connection, - "type='signal'," - "sender='" DBUS_SERVICE_DBUS "'," - "interface='" DBUS_INTERFACE_DBUS "'," - "member='NameOwnerChanged'", NULL); + m->match, + NULL); if (m->filtering) dbus_connection_remove_filter( @@ -233,6 +219,7 @@ void rm_release(rm_monitor *m) { free(m->device_name); free(m->service_name); + free(m->match); if (m->connection) dbus_connection_unref(m->connection); diff --git a/src/modules/reserve.c b/src/modules/reserve.c index 5597f177..b4c168cf 100644 --- a/src/modules/reserve.c +++ b/src/modules/reserve.c @@ -291,7 +291,6 @@ static DBusHandlerResult filter_handler( DBusMessage *m, void *userdata) { - DBusMessage *reply; rd_device *d; DBusError error; @@ -323,35 +322,13 @@ static DBusHandlerResult filter_handler( rd_release(d); } - return DBUS_HANDLER_RESULT_HANDLED; } } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - invalid: - if (!(reply = dbus_message_new_error( - m, - DBUS_ERROR_INVALID_ARGS, - "Invalid arguments"))) - goto oom; - - if (!dbus_connection_send(c, reply, NULL)) - goto oom; - - dbus_message_unref(reply); - - dbus_error_free(&error); - - return DBUS_HANDLER_RESULT_HANDLED; - -oom: - if (reply) - dbus_message_unref(reply); - dbus_error_free(&error); - return DBUS_HANDLER_RESULT_NEED_MEMORY; + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } diff --git a/src/pulse/channelmap.c b/src/pulse/channelmap.c index 88823012..9b516262 100644 --- a/src/pulse/channelmap.c +++ b/src/pulse/channelmap.c @@ -219,11 +219,11 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p case 6: m->map[0] = PA_CHANNEL_POSITION_FRONT_LEFT; - m->map[1] = PA_CHANNEL_POSITION_REAR_LEFT; + m->map[1] = PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; m->map[2] = PA_CHANNEL_POSITION_FRONT_CENTER; m->map[3] = PA_CHANNEL_POSITION_FRONT_RIGHT; - m->map[4] = PA_CHANNEL_POSITION_REAR_RIGHT; - m->map[5] = PA_CHANNEL_POSITION_LFE; + m->map[4] = PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + m->map[5] = PA_CHANNEL_POSITION_REAR_CENTER; return m; case 5: @@ -247,7 +247,7 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p m->map[0] = PA_CHANNEL_POSITION_LEFT; m->map[1] = PA_CHANNEL_POSITION_CENTER; m->map[2] = PA_CHANNEL_POSITION_RIGHT; - m->map[3] = PA_CHANNEL_POSITION_LFE; + m->map[3] = PA_CHANNEL_POSITION_REAR_CENTER; return m; default: @@ -299,6 +299,8 @@ pa_channel_map* pa_channel_map_init_auto(pa_channel_map *m, unsigned channels, p case PA_CHANNEL_MAP_WAVEEX: + /* Following http://www.microsoft.com/whdc/device/audio/multichaud.mspx#EKLAC */ + switch (channels) { case 1: m->map[0] = PA_CHANNEL_POSITION_MONO; @@ -451,6 +453,10 @@ int pa_channel_map_equal(const pa_channel_map *a, const pa_channel_map *b) { pa_assert(b); pa_return_val_if_fail(pa_channel_map_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + pa_return_val_if_fail(pa_channel_map_valid(b), 0); if (a->channels != b->channels) @@ -639,6 +645,10 @@ int pa_channel_map_superset(const pa_channel_map *a, const pa_channel_map *b) { pa_assert(b); pa_return_val_if_fail(pa_channel_map_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + pa_return_val_if_fail(pa_channel_map_valid(b), 0); am = pa_channel_map_mask(a); diff --git a/src/pulse/channelmap.h b/src/pulse/channelmap.h index d7901ac2..469effc8 100644 --- a/src/pulse/channelmap.h +++ b/src/pulse/channelmap.h @@ -216,17 +216,27 @@ typedef enum pa_channel_map_def { PA_CHANNEL_MAP_AIFF, /**< The mapping from RFC3551, which is based on AIFF-C */ +/** \cond fulldocs */ PA_CHANNEL_MAP_ALSA, - /**< The default mapping used by ALSA */ + /**< The default mapping used by ALSA. This mapping is probably + * not too useful since ALSA's default channel mapping depends on + * the device string used. */ +/** \endcond */ PA_CHANNEL_MAP_AUX, /**< Only aux channels */ PA_CHANNEL_MAP_WAVEEX, - /**< Microsoft's WAVEFORMATEXTENSIBLE mapping */ + /**< Microsoft's WAVEFORMATEXTENSIBLE mapping. This mapping works + * as if all LSBs of dwChannelMask are set. */ +/** \cond fulldocs */ PA_CHANNEL_MAP_OSS, - /**< The default channel mapping used by OSS as defined in the OSS 4.0 API specs */ + /**< The default channel mapping used by OSS as defined in the OSS + * 4.0 API specs. This mapping is probably not too useful since + * the OSS API has changed in this respect and no longer knows a + * default channel mapping based on the number of channels. */ +/** \endcond */ /**< Upper limit of valid channel mapping definitions */ PA_CHANNEL_MAP_DEF_MAX, @@ -282,7 +292,7 @@ pa_channel_map* pa_channel_map_init_extend(pa_channel_map *m, unsigned channels, /** Return a text label for the specified channel position */ const char* pa_channel_position_to_string(pa_channel_position_t pos) PA_GCC_PURE; -/* The inverse of pa_channel_position_to_string(). \since 0.9.16 */ +/** The inverse of pa_channel_position_to_string(). \since 0.9.16 */ pa_channel_position_t pa_channel_position_from_string(const char *s) PA_GCC_PURE; /** Return a human readable text label for the specified channel position. \since 0.9.7 */ diff --git a/src/pulse/sample.c b/src/pulse/sample.c index 0f19f8eb..d5d38eda 100644 --- a/src/pulse/sample.c +++ b/src/pulse/sample.c @@ -125,6 +125,10 @@ int pa_sample_spec_equal(const pa_sample_spec*a, const pa_sample_spec*b) { pa_assert(b); pa_return_val_if_fail(pa_sample_spec_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + pa_return_val_if_fail(pa_sample_spec_valid(b), 0); return diff --git a/src/pulse/volume.c b/src/pulse/volume.c index c23f360b..234c3f72 100644 --- a/src/pulse/volume.c +++ b/src/pulse/volume.c @@ -40,6 +40,10 @@ int pa_cvolume_equal(const pa_cvolume *a, const pa_cvolume *b) { pa_assert(b); pa_return_val_if_fail(pa_cvolume_valid(a), 0); + + if (PA_UNLIKELY(a == b)) + return 1; + pa_return_val_if_fail(pa_cvolume_valid(b), 0); if (a->channels != b->channels) @@ -122,7 +126,7 @@ pa_volume_t pa_cvolume_avg_mask(const pa_cvolume *a, const pa_channel_map *cm, p } pa_volume_t pa_cvolume_max(const pa_cvolume *a) { - pa_volume_t m = 0; + pa_volume_t m = PA_VOLUME_MUTED; unsigned c; pa_assert(a); @@ -135,8 +139,22 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) { return m; } +pa_volume_t pa_cvolume_min(const pa_cvolume *a) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c; + + pa_assert(a); + pa_return_val_if_fail(pa_cvolume_valid(a), PA_VOLUME_MUTED); + + for (c = 0; c < a->channels; c++) + if (a->values[c] < m) + m = a->values[c]; + + return m; +} + pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { - pa_volume_t m = 0; + pa_volume_t m = PA_VOLUME_MUTED; unsigned c, n; pa_assert(a); @@ -158,17 +176,42 @@ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, p return m; } +pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) { + pa_volume_t m = PA_VOLUME_MAX; + unsigned c, n; + + pa_assert(a); + + if (!cm) + return pa_cvolume_min(a); + + pa_return_val_if_fail(pa_cvolume_compatible_with_channel_map(a, cm), PA_VOLUME_MUTED); + + for (c = n = 0; c < a->channels; c++) { + + if (!(PA_CHANNEL_POSITION_MASK(cm->map[c]) & mask)) + continue; + + if (a->values[c] < m) + m = a->values[c]; + } + + return m; +} + pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) { - return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) * pa_sw_volume_to_linear(b)); + + /* cbrt((a/PA_VOLUME_NORM)^3*(b/PA_VOLUME_NORM)^3)*PA_VOLUME_NORM = a*b/PA_VOLUME_NORM */ + + return (pa_volume_t) (((uint64_t) a * (uint64_t) b + (uint64_t) PA_VOLUME_NORM / 2ULL) / (uint64_t) PA_VOLUME_NORM); } pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) { - double v = pa_sw_volume_to_linear(b); - if (v <= 0) + if (b <= PA_VOLUME_MUTED) return 0; - return pa_sw_volume_from_linear(pa_sw_volume_to_linear(a) / v); + return (pa_volume_t) (((uint64_t) a * (uint64_t) PA_VOLUME_NORM + (uint64_t) b / 2ULL) / (uint64_t) b); } /* Amplitude, not power */ @@ -249,7 +292,7 @@ char *pa_cvolume_snprint(char *s, size_t l, const pa_cvolume *c) { l -= pa_snprintf(e, l, "%s%u: %3u%%", first ? "" : " ", channel, - (c->values[channel]*100)/PA_VOLUME_NORM); + (c->values[channel]*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM); e = strchr(e, 0); first = FALSE; @@ -269,7 +312,7 @@ char *pa_volume_snprint(char *s, size_t l, pa_volume_t v) { return s; } - pa_snprintf(s, l, "%3u%%", (v*100)/PA_VOLUME_NORM); + pa_snprintf(s, l, "%3u%%", (v*100+PA_VOLUME_NORM/2)/PA_VOLUME_NORM); return s; } @@ -815,3 +858,55 @@ pa_volume_t pa_cvolume_get_position( return v; } + +pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b) { + unsigned i; + + pa_assert(dest); + pa_assert(a); + pa_assert(b); + + pa_return_val_if_fail(pa_cvolume_valid(a), NULL); + pa_return_val_if_fail(pa_cvolume_valid(b), NULL); + + for (i = 0; i < a->channels && i < b->channels; i++) + dest->values[i] = PA_MAX(a->values[i], b->values[i]); + + dest->channels = (uint8_t) i; + + return dest; +} + +pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + + m = pa_cvolume_max(v); + + if (m >= PA_VOLUME_MAX - inc) + m = PA_VOLUME_MAX; + else + m += inc; + + return pa_cvolume_scale(v, m); +} + +pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec) { + pa_volume_t m; + + pa_assert(v); + + pa_return_val_if_fail(pa_cvolume_valid(v), NULL); + + m = pa_cvolume_max(v); + + if (m <= PA_VOLUME_MUTED + dec) + m = PA_VOLUME_MUTED; + else + m -= dec; + + return pa_cvolume_scale(v, m); +} diff --git a/src/pulse/volume.h b/src/pulse/volume.h index 05b7ebb4..543b0af1 100644 --- a/src/pulse/volume.h +++ b/src/pulse/volume.h @@ -195,6 +195,16 @@ pa_volume_t pa_cvolume_max(const pa_cvolume *a) PA_GCC_PURE; * \since 0.9.16 */ pa_volume_t pa_cvolume_max_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE; +/** Return the minimum volume of all channels. \since 0.9.16 */ +pa_volume_t pa_cvolume_min(const pa_cvolume *a) PA_GCC_PURE; + +/** Return the minimum volume of all channels that are included in the + * specified channel map with the specified channel position mask. If + * cm is NULL this call is identical to pa_cvolume_min(). If no + * channel is selected the returned value will be PA_VOLUME_MUTED. + * \since 0.9.16 */ +pa_volume_t pa_cvolume_min_mask(const pa_cvolume *a, const pa_channel_map *cm, pa_channel_position_mask_t mask) PA_GCC_PURE; + /** Return TRUE when the passed cvolume structure is valid, FALSE otherwise */ int pa_cvolume_valid(const pa_cvolume *v) PA_GCC_PURE; @@ -213,11 +223,13 @@ int pa_cvolume_channels_equal_to(const pa_cvolume *a, pa_volume_t v) PA_GCC_PURE pa_volume_t pa_sw_volume_multiply(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; /** Multiply two per-channel volumes and return the result in - * *dest. This is only valid for software volumes! */ + * *dest. This is only valid for software volumes! a, b and dest may + * point to the same structure. */ pa_cvolume *pa_sw_cvolume_multiply(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); /** Multiply a per-channel volume with a scalar volume and return the - * result in *dest. This is only valid for software volumes! \since + * result in *dest. This is only valid for software volumes! a + * and dest may point to the same structure. \since * 0.9.16 */ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b); @@ -228,11 +240,13 @@ pa_cvolume *pa_sw_cvolume_multiply_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t pa_sw_volume_divide(pa_volume_t a, pa_volume_t b) PA_GCC_CONST; /** Divide two per-channel volumes and return the result in - * *dest. This is only valid for software volumes! \since 0.9.13 */ + * *dest. This is only valid for software volumes! a, b + * and dest may point to the same structure. \since 0.9.13 */ pa_cvolume *pa_sw_cvolume_divide(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); /** Divide a per-channel volume by a scalar volume and return the - * result in *dest. This is only valid for software volumes! \since + * result in *dest. This is only valid for software volumes! a + * and dest may point to the same structure. \since * 0.9.16 */ pa_cvolume *pa_sw_cvolume_divide_scalar(pa_cvolume *dest, const pa_cvolume *a, pa_volume_t b); @@ -326,6 +340,19 @@ pa_cvolume* pa_cvolume_set_position(pa_cvolume *cv, const pa_channel_map *map, p * position by calling pa_channel_map_has_position(). \since 0.9.16 */ pa_volume_t pa_cvolume_get_position(pa_cvolume *cv, const pa_channel_map *map, pa_channel_position_t t) PA_GCC_PURE; +/** This goes through all channels in a and b and sets the + * corresponding channel in dest to the greater volume of both. a, b + * and dest may point to the same structure. \since 0.9.16 */ +pa_cvolume* pa_cvolume_merge(pa_cvolume *dest, const pa_cvolume *a, const pa_cvolume *b); + +/** Increase the volume passed in by 'inc'. The proportions between + * the channels are kept. \since 0.9.16 */ +pa_cvolume* pa_cvolume_inc(pa_cvolume *v, pa_volume_t inc); + +/** Increase the volume passed in by 'inc'. The proportions between + * the channels are kept. \since 0.9.16 */ +pa_cvolume* pa_cvolume_dec(pa_cvolume *v, pa_volume_t dec); + PA_C_DECL_END #endif 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 bb30854e..fc4eabfa 100644 --- a/src/pulsecore/core.h +++ b/src/pulsecore/core.h @@ -90,7 +90,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/macro.h b/src/pulsecore/macro.h index cf662510..ce88c1b9 100644 --- a/src/pulsecore/macro.h +++ b/src/pulsecore/macro.h @@ -59,39 +59,36 @@ #endif /* Rounds down */ -static inline void* pa_align_ptr(const void *p) { - return (void*) (((size_t) p) & ~(sizeof(void*)-1)); +static inline void* PA_ALIGN_PTR(const void *p) { + return (void*) (((size_t) p) & ~(sizeof(void*) - 1)); } -#define PA_ALIGN_PTR(x) (pa_align_ptr(x)) /* Rounds up */ -static inline size_t pa_align(size_t l) { - return (((l + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*)); +static inline size_t PA_ALIGN(size_t l) { + return ((l + sizeof(void*) - 1) & ~(sizeof(void*) - 1)); } -#define PA_ALIGN(x) (pa_align(x)) /* Rounds down */ -static inline void* pa_page_align_ptr(const void *p) { - return (void*) (((size_t) p) & ~(PA_PAGE_SIZE-1)); +static inline void* PA_PAGE_ALIGN_PTR(const void *p) { + return (void*) (((size_t) p) & ~(PA_PAGE_SIZE - 1)); } -#define PA_PAGE_ALIGN_PTR(x) (pa_page_align_ptr(x)) /* Rounds up */ -static inline size_t pa_page_align(size_t l) { - return ((l + PA_PAGE_SIZE - 1) / PA_PAGE_SIZE) * PA_PAGE_SIZE; +static inline size_t PA_PAGE_ALIGN(size_t l) { + return (l + PA_PAGE_SIZE - 1) & ~(PA_PAGE_SIZE - 1); } -#define PA_PAGE_ALIGN(x) (pa_page_align(x)) #define PA_ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) -/* The users of PA_MIN and PA_MAX should be aware that these macros on - * non-GCC executed code with side effects twice. It is thus - * considered misuse to use code with side effects as arguments to MIN - * and MAX. */ +/* The users of PA_MIN and PA_MAX, PA_CLAMP, PA_ROUND_UP should be + * aware that these macros on non-GCC executed code with side effects + * twice. It is thus considered misuse to use code with side effects + * as arguments to MIN and MAX. */ #ifdef __GNUC__ #define PA_MAX(a,b) \ - __extension__ ({ typeof(a) _a = (a); \ + __extension__ ({ \ + typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a > _b ? _a : _b; \ }) @@ -101,7 +98,8 @@ static inline size_t pa_page_align(size_t l) { #ifdef __GNUC__ #define PA_MIN(a,b) \ - __extension__ ({ typeof(a) _a = (a); \ + __extension__ ({ \ + typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a < _b ? _a : _b; \ }) @@ -111,7 +109,8 @@ static inline size_t pa_page_align(size_t l) { #ifdef __GNUC__ #define PA_CLAMP(x, low, high) \ - __extension__ ({ typeof(x) _x = (x); \ + __extension__ ({ \ + typeof(x) _x = (x); \ typeof(low) _low = (low); \ typeof(high) _high = (high); \ ((_x > _high) ? _high : ((_x < _low) ? _low : _x)); \ @@ -122,7 +121,8 @@ static inline size_t pa_page_align(size_t l) { #ifdef __GNUC__ #define PA_CLAMP_UNLIKELY(x, low, high) \ - __extension__ ({ typeof(x) _x = (x); \ + __extension__ ({ \ + typeof(x) _x = (x); \ typeof(low) _low = (low); \ typeof(high) _high = (high); \ (PA_UNLIKELY(_x > _high) ? _high : (PA_UNLIKELY(_x < _low) ? _low : _x)); \ @@ -135,6 +135,28 @@ static inline size_t pa_page_align(size_t l) { * make sense: we cannot know if it is more likely that the values is * lower or greater than the boundaries.*/ +#ifdef __GNUC__ +#define PA_ROUND_UP(a, b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + ((_a + _b - 1) / _b) * _b; \ + }) +#else +#define PA_ROUND_UP(a, b) ((((a) + (b) - 1) / (b)) * (b)) +#endif + +#ifdef __GNUC__ +#define PA_ROUND_DOWN(a, b) \ + __extension__ ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + (_a / _b) * _b; \ + }) +#else +#define PA_ROUND_DOWN(a, b) (((a) / (b)) * (b)) +#endif + /* This type is not intended to be used in exported APIs! Use classic "int" there! */ #ifdef HAVE_STD_BOOL typedef _Bool pa_bool_t; 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); diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c index 46f049ef..8aa07f5e 100644 --- a/src/pulsecore/source.c +++ b/src/pulsecore/source.c @@ -181,7 +181,7 @@ pa_source* pa_source_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; @@ -219,7 +219,7 @@ pa_source* pa_source_new( s->n_corked = 0; s->monitor_of = NULL; - s->virtual_volume = data->volume; + s->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; @@ -751,31 +751,32 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) { } /* Called from main thread */ -void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save) { - pa_cvolume old_virtual_volume; - pa_bool_t virtual_volume_changed; +void pa_source_set_volume( + pa_source *s, + const pa_cvolume *volume, + pa_bool_t save) { + + pa_bool_t real_changed; pa_source_assert_ref(s); pa_assert_ctl_context(); pa_assert(PA_SOURCE_IS_LINKED(s->state)); - pa_assert(volume); pa_assert(pa_cvolume_valid(volume)); pa_assert(pa_cvolume_compatible(volume, &s->sample_spec)); - old_virtual_volume = s->virtual_volume; - s->virtual_volume = *volume; - virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume); - s->save_volume = (!virtual_volume_changed && s->save_volume) || save; + real_changed = !pa_cvolume_equal(volume, &s->volume); + s->volume = *volume; + s->save_volume = (!real_changed && s->save_volume) || save; if (s->set_volume) { pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); s->set_volume(s); } else - s->soft_volume = s->virtual_volume; + s->soft_volume = s->volume; pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_SET_VOLUME, NULL, 0, NULL) == 0); - if (virtual_volume_changed) + if (real_changed) pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } @@ -783,12 +784,16 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume) { pa_source_assert_ref(s); pa_assert_ctl_context(); - pa_assert(volume); + + if (!volume) + pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels); + else + s->soft_volume = *volume; if (PA_SOURCE_IS_LINKED(s->state)) pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_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 */ @@ -798,20 +803,22 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) { pa_assert(PA_SOURCE_IS_LINKED(s->state)); if (s->refresh_volume || force_refresh) { - pa_cvolume old_virtual_volume = s->virtual_volume; + pa_cvolume old_volume; + + old_volume = s->volume; if (s->get_volume) s->get_volume(s); pa_assert_se(pa_asyncmsgq_send(s->asyncmsgq, PA_MSGOBJECT(s), PA_SOURCE_MESSAGE_GET_VOLUME, NULL, 0, NULL) == 0); - if (!pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume)) { + if (!pa_cvolume_equal(&old_volume, &s->volume)) { s->save_volume = TRUE; pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); } } - return &s->virtual_volume; + return &s->volume; } /* Called from main thread */ @@ -822,10 +829,10 @@ void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) { /* The source implementor may call this if the volume changed to make sure everyone is notified */ - if (pa_cvolume_equal(&s->virtual_volume, new_volume)) + if (pa_cvolume_equal(&s->volume, new_volume)) return; - s->virtual_volume = *new_volume; + s->volume = *new_volume; s->save_volume = TRUE; pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index); diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h index 6f33de06..7b3e4953 100644 --- a/src/pulsecore/source.h +++ b/src/pulsecore/source.h @@ -79,7 +79,7 @@ struct pa_source { pa_volume_t base_volume; /* shall be constant */ unsigned n_volume_steps; /* shall be constant */ - pa_cvolume virtual_volume, soft_volume; + pa_cvolume volume, soft_volume; pa_bool_t muted:1; pa_bool_t refresh_volume:1; diff --git a/src/tests/voltest.c b/src/tests/voltest.c index 64aec5c6..551f7ecd 100644 --- a/src/tests/voltest.c +++ b/src/tests/voltest.c @@ -33,6 +33,8 @@ int main(int argc, char *argv[]) { pa_cvolume cv; float b; pa_channel_map map; + pa_volume_t md = 0; + unsigned mdn = 0; printf("Attenuation of sample 1 against 32767: %g dB\n", 20.0*log10(1.0/32767.0)); printf("Smallest possible attenutation > 0 applied to 32767: %li\n", lrint(32767.0*pa_sw_volume_to_linear(1))); @@ -85,16 +87,48 @@ int main(int argc, char *argv[]) { printf("After: volume: [%s]; balance: %2.1f (intended: %2.1f) %s\n", pa_cvolume_snprint(s, sizeof(s), &r), k, b, k < b-.05 || k > b+.5 ? "MISMATCH" : ""); } - for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 1) { + for (v = PA_VOLUME_MUTED; v <= PA_VOLUME_NORM*2; v += 51) { double l = pa_sw_volume_to_linear(v); pa_volume_t k = pa_sw_volume_from_linear(l); double db = pa_sw_volume_to_dB(v); pa_volume_t r = pa_sw_volume_from_dB(db); + pa_volume_t w; pa_assert(k == v); pa_assert(r == v); + + for (w = PA_VOLUME_MUTED; w < PA_VOLUME_NORM*2; w += 37) { + + double t = pa_sw_volume_to_linear(w); + double db2 = pa_sw_volume_to_dB(w); + pa_volume_t p, p1, p2; + double q, qq; + + p = pa_sw_volume_multiply(v, w); + qq = db + db2; + p2 = pa_sw_volume_from_dB(qq); + q = l*t; + p1 = pa_sw_volume_from_linear(q); + + if (p2 > p && p2 - p > md) + md = p2 - p; + if (p2 < p && p - p2 > md) + md = p - p2; + if (p1 > p && p1 - p > md) + md = p1 - p; + if (p1 < p && p - p1 > md) + md = p - p1; + + if (p1 != p || p2 != p) + mdn++; + } } + printf("max deviation: %lu n=%lu\n", (unsigned long) md, (unsigned long) mdn); + + pa_assert(md <= 1); + pa_assert(mdn <= 251); + return 0; } |