Open a new line when a speaker's playback line dies

Both desktop backends dropped writes to a line whose device had gone
away, and the per-speaker playout ignored any failure, so that speaker
stayed silent for the rest of the session. A write to a dead line now
throws; the playout drops that line and the speaker's next packet
opens a fresh one. Sound effects and the microphone test's loopback
reopen their line the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-25 10:48:22 +00:00
parent 6234ec6d97
commit 12ca585d90
4 changed files with 20 additions and 6 deletions

View File

@@ -15,7 +15,12 @@ public interface AudioPlayback extends AutoCloseable {
/** Begins playing whatever is written from now on. */
void start();
/** Blocks until the audio has been queued for playback. */
/**
* Blocks until the audio has been queued for playback.
*
* @throws IllegalStateException once the line is gone for good (its device went away,
* say), so the caller can open another
*/
void write(byte[] buffer, int offset, int length);
/** Blocks until everything already written has been played out. */

View File

@@ -158,7 +158,12 @@ final class PerSpeakerPlayout implements Playout {
Pcm16.encode(speaker.pcm, samples, speaker.out);
try {
speaker.line.write(speaker.out, 0, samples * 2);
} catch (RuntimeException ignored) {
} catch (RuntimeException e) {
// The line died (a device went away, say). Drop it rather than keep writing into it:
// the speaker's next packet starts a burst on a fresh one.
speakers.remove(speaker.stream, speaker);
speaker.close();
speaker.stream.stop();
}
}