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:
@@ -15,7 +15,12 @@ public interface AudioPlayback extends AutoCloseable {
|
|||||||
/** Begins playing whatever is written from now on. */
|
/** Begins playing whatever is written from now on. */
|
||||||
void start();
|
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);
|
void write(byte[] buffer, int offset, int length);
|
||||||
|
|
||||||
/** Blocks until everything already written has been played out. */
|
/** Blocks until everything already written has been played out. */
|
||||||
|
|||||||
@@ -158,7 +158,12 @@ final class PerSpeakerPlayout implements Playout {
|
|||||||
Pcm16.encode(speaker.pcm, samples, speaker.out);
|
Pcm16.encode(speaker.pcm, samples, speaker.out);
|
||||||
try {
|
try {
|
||||||
speaker.line.write(speaker.out, 0, samples * 2);
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,11 +25,13 @@ final class JavaSoundPlayback implements AudioPlayback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] buffer, int offset, int length) {
|
public void write(byte[] buffer, int offset, int length) {
|
||||||
|
int written;
|
||||||
try {
|
try {
|
||||||
line.write(buffer, offset, length);
|
written = line.write(buffer, offset, length);
|
||||||
} catch (Exception ignored) {
|
} catch (RuntimeException e) {
|
||||||
// The line went away (device unplugged); the caller sees silence.
|
throw new IllegalStateException("The playback line failed", e);
|
||||||
}
|
}
|
||||||
|
if (written < length && !line.isOpen()) throw new IllegalStateException("The playback line closed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ final class PipeWirePlayback extends PipeWireStream implements AudioPlayback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(byte[] buffer, int offset, int length) {
|
public void write(byte[] buffer, int offset, int length) {
|
||||||
ring.write(buffer, offset, length);
|
if (ring.write(buffer, offset, length) < length && ring.isClosed()) {
|
||||||
|
throw new IllegalStateException("The PipeWire stream ended");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user