Add a PipeWire audio backend and stereo capture/playback
Capture and playback now go through AudioCapture/AudioPlayback interfaces with two implementations: PipeWire, used for the system default and for every pw: device when a session is reachable, and Java Sound for the raw ALSA devices and other platforms. The PipeWire binding is FFM-based (PipeWireLibrary, PipeWireSession, PipeWireStream, SpaPod) and enumerates the graph's real devices, so each stream shows up separately in volume mixers. Lines are opened with a negotiated channel count instead of a fixed mono format: OPUS_MUSIC transmits the stereo capture as-is, OPUS_VOICE keeps transmitting the mono downmix so the pre-processing chain and VAD still see a single channel. Java Sound enumeration asks only for "a mixer that can capture/play" so the PipeWire and PulseAudio ALSA plugins are no longer hidden by their narrow format lists. The Java Sound classes are renamed to Desktop* to match what they now are, and JUnit plus a SpaPod round-trip test are wired into the build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
package com.ts3client.audio.desktop.pipewire;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HexFormat;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* Pins {@link SpaPod} to the bytes libpipewire itself produces.
|
||||
*
|
||||
* <p>The expected values are the output of {@code spa_format_audio_raw_build()} (pipewire
|
||||
* 1.4, {@code SPA_AUDIO_FORMAT_S16_LE} at 48 kHz), captured from a C program linked
|
||||
* against the real library. Since the POD layout is what the server parses, a mismatch
|
||||
* here is a stream that will fail to negotiate.
|
||||
*/
|
||||
class SpaPodTest {
|
||||
|
||||
private static final String MONO_48K =
|
||||
"a00000000f000000030004000300000001000000000000000400000003000000"
|
||||
+ "0100000000000000020000000000000004000000030000000100000000000000"
|
||||
+ "0100010000000000040000000300000003010000000000000300010000000000"
|
||||
+ "040000000400000080bb00000000000004000100000000000400000004000000"
|
||||
+ "010000000000000005000100000000000c0000000d0000000400000003000000"
|
||||
+ "0200000000000000";
|
||||
|
||||
private static final String STEREO_48K =
|
||||
"a00000000f000000030004000300000001000000000000000400000003000000"
|
||||
+ "0100000000000000020000000000000004000000030000000100000000000000"
|
||||
+ "0100010000000000040000000300000003010000000000000300010000000000"
|
||||
+ "040000000400000080bb00000000000004000100000000000400000004000000"
|
||||
+ "02000000000000000500010000000000100000000d0000000400000003000000"
|
||||
+ "0300000004000000";
|
||||
|
||||
@Test
|
||||
void buildsTheFormatLibpipewireExpects() {
|
||||
assertEquals(MONO_48K, hex(SpaPod.audioFormatBytes(48_000, 1)));
|
||||
assertEquals(STEREO_48K, hex(SpaPod.audioFormatBytes(48_000, 2)));
|
||||
}
|
||||
|
||||
private static String hex(byte[] bytes) {
|
||||
return HexFormat.of().formatHex(bytes);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user