Browse Source

Probe AAudio nonblocking writes alongside callback output

master
evgeny 3 days ago
parent
commit
9d422dd798
  1. 10
      tools/chatgui-android/audio-probe/src/main/cpp/probe.cpp
  2. 30
      tools/chatgui-android/audio-probe/src/main/java/com/utun/audioprobe/ProbeActivity.java

10
tools/chatgui-android/audio-probe/src/main/cpp/probe.cpp

@ -54,7 +54,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_utun_audioprobe_ProbeActivity_closeNa
}
extern "C" JNIEXPORT jboolean JNICALL Java_com_utun_audioprobe_ProbeActivity_openNative(
JNIEnv*, jclass, jboolean exclusive, jint channels) {
JNIEnv*, jclass, jboolean exclusive, jint channels, jboolean callback) {
if (probe.stream || (channels != 1 && channels != 2)) {
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "probe invalid open stream=%p channels=%d", probe.stream, channels);
return JNI_FALSE;
@ -70,7 +70,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_utun_audioprobe_ProbeActivity_ope
AAudioStreamBuilder_setChannelCount(builder, channels);
AAudioStreamBuilder_setSharingMode(builder, exclusive ? AAUDIO_SHARING_MODE_EXCLUSIVE : AAUDIO_SHARING_MODE_SHARED);
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
AAudioStreamBuilder_setDataCallback(builder, data_callback, &probe);
if (callback) AAudioStreamBuilder_setDataCallback(builder, data_callback, &probe);
AAudioStreamBuilder_setErrorCallback(builder, error_callback, &probe);
aaudio_result_t opened = AAudioStreamBuilder_openStream(builder, &probe.stream);
check_result("delete builder", AAudioStreamBuilder_delete(builder));
@ -99,6 +99,12 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_utun_audioprobe_ProbeActivity_ope
return JNI_TRUE;
}
extern "C" JNIEXPORT jint JNICALL Java_com_utun_audioprobe_ProbeActivity_writeNative(JNIEnv* env, jclass, jshortArray input) {
int16_t samples[960]{};
env->GetShortArrayRegion(input, 0, 960, samples);
return AAudioStream_write(probe.stream, samples, 960 / probe.channels, 0);
}
/* Timestamp опрашивается вне realtime callback. Java одинаково агрегирует AAudio и AudioTrack. */
extern "C" JNIEXPORT void JNICALL Java_com_utun_audioprobe_ProbeActivity_sampleNative(JNIEnv* env, jclass, jlongArray output) {
if (!probe.stream || env->GetArrayLength(output) != 10) {

30
tools/chatgui-android/audio-probe/src/main/java/com/utun/audioprobe/ProbeActivity.java

@ -24,7 +24,8 @@ import java.util.concurrent.atomic.AtomicLong;
public final class ProbeActivity extends Activity {
private static final String TAG = "AudioProbe";
static { System.loadLibrary("call_audio_probe"); }
private static native boolean openNative(boolean exclusive, int channels);
private static native boolean openNative(boolean exclusive, int channels, boolean callback);
private static native int writeNative(short[] input);
private static native void sampleNative(long[] out);
private static native void closeNative();
private volatile boolean cancelled;
@ -143,10 +144,31 @@ public final class ProbeActivity extends Activity {
private Output open(int mode, int channels) {
if (mode == 0) return new TrackOutput();
if (!openNative(mode == 2, channels)) throw new IllegalStateException("AAudio open rejected");
boolean callback = getIntent().getBooleanExtra("callback", true);
if (!openNative(mode == 2, channels, callback)) throw new IllegalStateException("AAudio open rejected");
AtomicInteger writeError = new AtomicInteger();
java.util.concurrent.atomic.AtomicBoolean writing = new java.util.concurrent.atomic.AtomicBoolean(!callback);
Thread writer = new Thread(() -> {
short[] silence = new short[960];
try {
while (writing.get() && !cancelled) {
int n = writeNative(silence);
if (n < 0) { writeError.set(n); Log.e(TAG, "AAudio write=" + n); break; }
if (n == 0) Thread.sleep(2);
}
} catch (InterruptedException e) { Thread.currentThread().interrupt(); }
}, "audio-probe-aaudio-write");
if (!callback) writer.start();
return new Output() {
public void sample(long[] out) { sampleNative(out); }
public void close() { closeNative(); }
public void sample(long[] out) { sampleNative(out); if (writeError.get() != 0) out[7] = writeError.get(); }
public void close() {
writing.set(false);
if (!callback) {
writer.interrupt();
try { writer.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); Log.e(TAG, "AAudio join", e); }
}
closeNative();
}
};
}

Loading…
Cancel
Save