Browse Source

Probe AAudio nonblocking writes alongside callback output

master
evgeny 4 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( 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)) { if (probe.stream || (channels != 1 && channels != 2)) {
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "probe invalid open stream=%p channels=%d", probe.stream, channels); DEBUG_ERROR(DEBUG_CATEGORY_CALL, "probe invalid open stream=%p channels=%d", probe.stream, channels);
return JNI_FALSE; return JNI_FALSE;
@ -70,7 +70,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_utun_audioprobe_ProbeActivity_ope
AAudioStreamBuilder_setChannelCount(builder, channels); AAudioStreamBuilder_setChannelCount(builder, channels);
AAudioStreamBuilder_setSharingMode(builder, exclusive ? AAUDIO_SHARING_MODE_EXCLUSIVE : AAUDIO_SHARING_MODE_SHARED); AAudioStreamBuilder_setSharingMode(builder, exclusive ? AAUDIO_SHARING_MODE_EXCLUSIVE : AAUDIO_SHARING_MODE_SHARED);
AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); 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); AAudioStreamBuilder_setErrorCallback(builder, error_callback, &probe);
aaudio_result_t opened = AAudioStreamBuilder_openStream(builder, &probe.stream); aaudio_result_t opened = AAudioStreamBuilder_openStream(builder, &probe.stream);
check_result("delete builder", AAudioStreamBuilder_delete(builder)); 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; 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. */ /* Timestamp опрашивается вне realtime callback. Java одинаково агрегирует AAudio и AudioTrack. */
extern "C" JNIEXPORT void JNICALL Java_com_utun_audioprobe_ProbeActivity_sampleNative(JNIEnv* env, jclass, jlongArray output) { extern "C" JNIEXPORT void JNICALL Java_com_utun_audioprobe_ProbeActivity_sampleNative(JNIEnv* env, jclass, jlongArray output) {
if (!probe.stream || env->GetArrayLength(output) != 10) { 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 { public final class ProbeActivity extends Activity {
private static final String TAG = "AudioProbe"; private static final String TAG = "AudioProbe";
static { System.loadLibrary("call_audio_probe"); } 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 sampleNative(long[] out);
private static native void closeNative(); private static native void closeNative();
private volatile boolean cancelled; private volatile boolean cancelled;
@ -143,10 +144,31 @@ public final class ProbeActivity extends Activity {
private Output open(int mode, int channels) { private Output open(int mode, int channels) {
if (mode == 0) return new TrackOutput(); 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() { return new Output() {
public void sample(long[] out) { sampleNative(out); } public void sample(long[] out) { sampleNative(out); if (writeError.get() != 0) out[7] = writeError.get(); }
public void close() { closeNative(); } 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