From 030717f627971b6fcdb4f5583f18e959a1c80dac Mon Sep 17 00:00:00 2001 From: Filipe Moreira Date: Tue, 29 Sep 2026 09:42:16 -0300 Subject: [PATCH] fix(audio): a failed output switch no longer rolls back the session With SoundVolumeView a failed /SetDefault was ignored and the session went on. The Core Audio backend throws instead, and ConsoleEngine.Start does not catch it: an error in SetOutput made MainViewModel roll the whole start back (Engine.Stop) after the screens had already been switched. The two polling paths (auto-switch and pending target) called SetOutput unguarded too. TrySetOutput logs the error in Portuguese and lets the session continue; the switch is only recorded (AudioDeviceId, LastAudioSwitchName) when it worked. The session menu's manual output choice still reports its error to the user. Co-Authored-By: Claude Sonnet 5.5 --- docs/TESTING.md | 1 + src/ConsoleMode/Services/ConsoleEngine.cs | 32 ++++++++++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/TESTING.md b/docs/TESTING.md index ba03f50..64d7b9b 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -121,6 +121,7 @@ Pré-condições: 1.6 instalada por cima da 1.5, com a configuração da 1.5 (sa - [ ] Ajustes → Saída de áudio lista as mesmas saídas que na 1.5, com os mesmos nomes, e as desativadas aparecem com o sufixo "[Desabilitado]". Resultado: ______ - [ ] A saída que já estava escolhida com o SoundVolumeView continua selecionada depois de atualizar da 1.5 (o ID salvo é o mesmo). Resultado: ______ - [ ] Entrar no modo console com uma saída fixa (ex.: HDMI da TV): o som passa para ela, inclusive em apps de chamada (papel "comunicações"). O log mostra `Áudio: saída padrão = …`. Resultado: ______ +- [ ] Se a troca de saída falhar (ex.: o dispositivo some no meio), a sessão continua e o log mostra `Áudio: não foi possível trocar para …`; a mesa não é desfeita. Resultado: ______ - [ ] "A que aparecer ao conectar (TV)" com a TV começando **desligada**: quando a TV liga, o som vai para o HDMI dela. Resultado: ______ - [ ] Uma saída **desabilitada** em Configurações → Som: escolhê-la liga a saída e o som vai para ela. Resultado: ______ - [ ] Menu da sessão (Select + Y): o volume inicial é o do Windows; ◀/▶ muda de 5 em 5 e tira o mudo; A alterna o mudo. Resultado: ______ diff --git a/src/ConsoleMode/Services/ConsoleEngine.cs b/src/ConsoleMode/Services/ConsoleEngine.cs index 18cdd0a..21574b3 100644 --- a/src/ConsoleMode/Services/ConsoleEngine.cs +++ b/src/ConsoleMode/Services/ConsoleEngine.cs @@ -138,7 +138,7 @@ public void Start(AppConfig config, MonitorInfo? focusInfo, Func d.FriendlyId == config.AudioDeviceId); if (target is { IsActive: true }) { - Audio.SetOutput(config.AudioDeviceId); + TrySetOutput(config.AudioDeviceId); CompleteAudioWatch(); } else @@ -465,9 +465,11 @@ private void UpdateAutoSwitch() var pick = Audio.PickNewDevice(newly, State.AudioDeviceHint, focus); if (pick is null) return; - Audio.SetOutput(pick.FriendlyId); - State.AudioDeviceId = pick.FriendlyId; - State.LastAudioSwitchName = pick.Name; + if (TrySetOutput(pick.FriendlyId)) + { + State.AudioDeviceId = pick.FriendlyId; + State.LastAudioSwitchName = pick.Name; + } CompleteAudioWatch(); } @@ -477,8 +479,26 @@ private void UpdatePendingTarget() var devices = Audio.GetDevices(true); var target = devices.FirstOrDefault(d => d.FriendlyId == State.AudioDeviceId); if (target is not { IsActive: true }) return; - Audio.SetOutput(target.FriendlyId); - State.LastAudioSwitchName = target.Name; + if (TrySetOutput(target.FriendlyId)) State.LastAudioSwitchName = target.Name; CompleteAudioWatch(); } + + /// + /// Switching the output is best effort, as it was with SoundVolumeView (its failures were ignored): + /// a Core Audio error is logged and the session goes on, instead of rolling back the whole start + /// after the screens were already switched. + /// + private bool TrySetOutput(string friendlyId) + { + try + { + Audio.SetOutput(friendlyId); + return true; + } + catch (Exception ex) + { + AppLog.Write($"Áudio: não foi possível trocar para {friendlyId}: {ex.Message}"); + return false; + } + } }