From e68df70aadaa767f1b91f808ed55833f6238258a Mon Sep 17 00:00:00 2001 From: evgeny Date: Thu, 15 Oct 2026 14:19:03 +0200 Subject: [PATCH] Extend audio probe with route roundtrip and 40 ms control buffer --- .../com/utun/audioprobe/ProbeActivity.java | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/tools/chatgui-android/audio-probe/src/main/java/com/utun/audioprobe/ProbeActivity.java b/tools/chatgui-android/audio-probe/src/main/java/com/utun/audioprobe/ProbeActivity.java index 56a02fb3..7bca04ad 100644 --- a/tools/chatgui-android/audio-probe/src/main/java/com/utun/audioprobe/ProbeActivity.java +++ b/tools/chatgui-android/audio-probe/src/main/java/com/utun/audioprobe/ProbeActivity.java @@ -87,9 +87,13 @@ public final class ProbeActivity extends Activity { int channels = getIntent().getIntExtra("channels", 0); if (api == null || api.equals("track")) trial("track-mono", 0, 1); if (api == null || api.equals("aaudio")) { + String sharing = getIntent().getStringExtra("sharing"); for (int c : new int[]{1, 2}) { if (channels != 0 && channels != c) continue; - for (int mode : new int[]{1, 2}) trial("aaudio-" + (mode == 1 ? "shared" : "exclusive") + "-ch" + c, mode, c); + for (int mode : new int[]{1, 2}) { + String name = mode == 1 ? "shared" : "exclusive"; + if (sharing == null || sharing.equals(name)) trial("aaudio-" + name + "-ch" + c, mode, c); + } } } show("probe complete captured=" + captured.get() + " captureError=" + captureError.get() + " cancelled=" + cancelled); @@ -156,7 +160,16 @@ public final class ProbeActivity extends Activity { route(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); measure(output, name + "/earpiece-existing"); } - if (!cancelled) try (Output output = open(mode, channels)) { measure(output, name + "/earpiece-new"); } + if (!cancelled) try (Output output = open(mode, channels)) { + measure(output, name + "/earpiece-new"); + if (getIntent().getBooleanExtra("roundtrip", false)) { + route(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); + measure(output, name + "/speaker-existing"); + } + } + if (!cancelled && getIntent().getBooleanExtra("roundtrip", false)) { + try (Output output = open(mode, channels)) { measure(output, name + "/speaker-reopened"); } + } } catch (RuntimeException e) { Log.e(TAG, "trial failed name=" + name, e); } } @@ -213,10 +226,17 @@ public final class ProbeActivity extends Activity { .setChannelMask(AudioFormat.CHANNEL_OUT_MONO).build()) .setBufferSizeInBytes(Math.max(min * 2, 3840)).setTransferMode(AudioTrack.MODE_STREAM) .setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY).build(); - int actual = track.setBufferSizeInFrames(960); - if (Build.VERSION.SDK_INT >= 31) track.setStartThresholdInFrames(actual); - Log.i(TAG, "track opened requested=1 performance=" + track.getPerformanceMode() + " buffer=" + actual); - track.play(); + try { + if (track.getState() != AudioTrack.STATE_INITIALIZED) throw new IllegalStateException("track not initialized"); + int bufferMs = getIntent().getIntExtra("buffer_ms", 20); + if (bufferMs != 20 && bufferMs != 40) throw new IllegalArgumentException("buffer_ms must be 20 or 40"); + int actual = track.setBufferSizeInFrames(48 * bufferMs); + if (actual <= 0) throw new IllegalStateException("track buffer result=" + actual); + if (Build.VERSION.SDK_INT >= 31 && track.setStartThresholdInFrames(actual) <= 0) + throw new IllegalStateException("track start threshold rejected"); + Log.i(TAG, "track opened requested=1 performance=" + track.getPerformanceMode() + " buffer=" + actual); + track.play(); + } catch (RuntimeException e) { track.release(); throw e; } writer = new Thread(() -> { short[] silence = new short[960]; while (running && !cancelled) {