diff --git a/src/outputs.c b/src/outputs.c index 940f67c6..12c045a1 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -532,9 +532,6 @@ vol_adjust(void) for (device = outputs_device_list; device; device = device->next) { - if (!OUTPUTS_DEVICE_DISPLAY_SELECTED(device) && (device->volume > outputs_master_volume)) - device->volume = outputs_master_volume; - device->relvol = vol_to_rel(device->volume, outputs_master_volume); } @@ -815,12 +812,23 @@ outputs_device_remove(struct output_device *remove) } void -outputs_device_select(struct output_device *device) +outputs_device_select(struct output_device *device, int max_volume) { device->selected = 1; device->prevent_playback = 0; device->busy = 0; + // The purpose of this is to cap the volume for a newly selected device. It is + // used by the player to avoid this scenario: + // 1 Play on two speakers, say Kitchen (100) and Office (75), master is 100 + // 2 Disable Office, reduce master to 25, Kitchen is now 25, Office is still 75 + // 3 Turn on Office, it now blasts at 75 + // We could avoid this by reducing the unselected Office in step 2, but that + // leads to issue #1077, where volumes of unselected devices go to 0 (e.g. by + // reducing master to 0 and then increasing again -> unselected stays at 0). + if (max_volume >= 0 && device->volume > max_volume) + device->volume = max_volume; + vol_adjust(); } diff --git a/src/outputs.h b/src/outputs.h index 2142b8fd..3946c960 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -291,7 +291,7 @@ void outputs_device_remove(struct output_device *remove); void -outputs_device_select(struct output_device *device); +outputs_device_select(struct output_device *device, int max_volume); void outputs_device_deselect(struct output_device *device); diff --git a/src/player.c b/src/player.c index 5d9cb445..9488982b 100644 --- a/src/player.c +++ b/src/player.c @@ -2091,7 +2091,7 @@ playback_start_item(void *arg, int *retval) if (*retval >= 0) { DPRINTF(E_LOG, L_PLAYER, "Autoselected %s device '%s'\n", device->type_name, device->name); - outputs_device_select(device); + outputs_device_select(device, -1); } } @@ -2561,6 +2561,7 @@ speaker_set(void *arg, int *retval) struct speaker_set_param *speaker_set_param = arg; struct output_device *device; uint64_t *ids; + int max_volume; int nspk; int i; @@ -2575,6 +2576,11 @@ speaker_set(void *arg, int *retval) DPRINTF(E_DBG, L_PLAYER, "Speaker set: %d speakers\n", nspk); + // Save the current master volume before we start selecting/unselecting, as + // that will affect master volume. See comment in outputs_device_select() for + // why we want to provide a max_volume. + max_volume = (player_state != PLAY_STOPPED) ? outputs_volume_get() : -1; + for (device = outputs_list(); device; device = device->next) { for (i = 1; i <= nspk; i++) @@ -2584,7 +2590,7 @@ speaker_set(void *arg, int *retval) } if (i <= nspk) - outputs_device_select(device); + outputs_device_select(device, max_volume); else outputs_device_deselect(device); } @@ -2602,6 +2608,7 @@ speaker_enable(void *arg, int *retval) { uint64_t *id = arg; struct output_device *device; + int max_volume; *retval = -1; @@ -2611,7 +2618,9 @@ speaker_enable(void *arg, int *retval) DPRINTF(E_DBG, L_PLAYER, "Speaker enable: '%s' (id=%" PRIu64 ")\n", device->name, *id); - outputs_device_select(device); + max_volume = (player_state != PLAY_STOPPED) ? outputs_volume_get() : -1; + + outputs_device_select(device, max_volume); *retval = outputs_device_start(device, device_activate_cb, PLAYER_ONLY_PROBE);