diff --git a/android/app/src/main/java/com/wzp/audio/AudioPipeline.kt b/android/app/src/main/java/com/wzp/audio/AudioPipeline.kt index 9223e50..841194f 100644 --- a/android/app/src/main/java/com/wzp/audio/AudioPipeline.kt +++ b/android/app/src/main/java/com/wzp/audio/AudioPipeline.kt @@ -60,6 +60,16 @@ class AudioPipeline(private val context: Context) { var debugRecording: Boolean = true private var captureThread: Thread? = null private var playoutThread: Thread? = null + + // DirectByteBuffers for zero-copy JNI audio transfer. + // Allocated as class fields (NOT locals) because ART's JIT OSR + // can null local variables when it replaces the stack frame mid-loop. + // These survive OSR because they're on the heap. + private val captureDirectBuf: ByteBuffer = + ByteBuffer.allocateDirect(FRAME_SAMPLES * 2).order(ByteOrder.LITTLE_ENDIAN) + private val playoutDirectBuf: ByteBuffer = + ByteBuffer.allocateDirect(FRAME_SAMPLES * 2).order(ByteOrder.LITTLE_ENDIAN) + /** Latch counted down by each audio thread after exiting its loop. * stop() does NOT wait on this — teardown waits via awaitDrain(). */ private var drainLatch: CountDownLatch? = null @@ -224,7 +234,10 @@ class AudioPipeline(private val context: Context) { val read = recorder.read(pcm, 0, FRAME_SAMPLES) if (read > 0) { applyGain(pcm, read, captureGainDb) - engine.writeAudio(pcm) + // Zero-copy write via DirectByteBuffer (class field, survives JIT OSR) + captureDirectBuf.clear() + captureDirectBuf.asShortBuffer().put(pcm, 0, read) + engine.writeAudioDirect(captureDirectBuf, read) // Debug: write raw PCM + RMS if (pcmOut != null) { @@ -303,8 +316,12 @@ class AudioPipeline(private val context: Context) { } try { while (running) { - val read = engine.readAudio(pcm) + // Zero-copy read via DirectByteBuffer (class field, survives JIT OSR) + playoutDirectBuf.clear() + val read = engine.readAudioDirect(playoutDirectBuf, FRAME_SAMPLES) if (read >= FRAME_SAMPLES) { + playoutDirectBuf.rewind() + playoutDirectBuf.asShortBuffer().get(pcm, 0, read) applyGain(pcm, read, playoutGainDb) track.write(pcm, 0, read)