diff --git a/tools/chatgui-android/audio-probe/src/main/cpp/probe.cpp b/tools/chatgui-android/audio-probe/src/main/cpp/probe.cpp index f2f5ac65..2e4b07bb 100644 --- a/tools/chatgui-android/audio-probe/src/main/cpp/probe.cpp +++ b/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) { 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 7bca04ad..aac7b103 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 @@ -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(); + } }; }