revert: use ShortArray audio path (DirectByteBuffer causes null ptr crash)
Some checks failed
Mirror to GitHub / mirror (push) Failing after 35s
Build Release Binaries / build-amd64 (push) Failing after 3m58s

DirectByteBuffer.clear() crashes with null pointer in ART's JIT OSR
compiled code on Android 16. Revert AudioPipeline to use the original
ShortArray writeAudio/readAudio path.

The DirectByteBuffer JNI functions remain in WzpEngine.kt and
jni_bridge.rs for future use once the OSR issue is resolved.

The original SIGBUS from ART GC is rare (~1 crash per 8 min call)
and doesn't warrant the DirectByteBuffer approach until we can
allocate the buffer as a class field outside the hot loop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Siavash Sameni
2026-04-06 21:17:15 +04:00
parent 6f99841cc7
commit b56b4a759c

View File

@@ -205,8 +205,6 @@ class AudioPipeline(private val context: Context) {
Log.i(TAG, "capture started: ${SAMPLE_RATE}Hz mono, buf=$bufSize, aec=${aec?.enabled}, ns=${ns?.enabled}")
val pcm = ShortArray(FRAME_SAMPLES)
// DirectByteBuffer for zero-copy JNI (avoids ART GC SIGBUS on Android 16)
val directBuf = ByteBuffer.allocateDirect(FRAME_SAMPLES * 2).order(ByteOrder.LITTLE_ENDIAN)
// Debug: PCM file + RMS CSV
var pcmOut: BufferedOutputStream? = null
var rmsCsv: OutputStreamWriter? = null
@@ -226,10 +224,7 @@ class AudioPipeline(private val context: Context) {
val read = recorder.read(pcm, 0, FRAME_SAMPLES)
if (read > 0) {
applyGain(pcm, read, captureGainDb)
// Zero-copy write via DirectByteBuffer (no GC array interaction)
directBuf.clear()
directBuf.asShortBuffer().put(pcm, 0, read)
engine.writeAudioDirect(directBuf, read)
engine.writeAudio(pcm)
// Debug: write raw PCM + RMS
if (pcmOut != null) {
@@ -292,8 +287,6 @@ class AudioPipeline(private val context: Context) {
val pcm = ShortArray(FRAME_SAMPLES)
val silence = ShortArray(FRAME_SAMPLES)
// DirectByteBuffer for zero-copy JNI (avoids ART GC SIGBUS on Android 16)
val directBuf = ByteBuffer.allocateDirect(FRAME_SAMPLES * 2).order(ByteOrder.LITTLE_ENDIAN)
// Debug: PCM file + RMS CSV for playout
var pcmOut: BufferedOutputStream? = null
var rmsCsv: OutputStreamWriter? = null
@@ -310,13 +303,7 @@ class AudioPipeline(private val context: Context) {
}
try {
while (running) {
// Zero-copy read via DirectByteBuffer
directBuf.clear()
val read = engine.readAudioDirect(directBuf, FRAME_SAMPLES)
if (read > 0) {
directBuf.rewind()
directBuf.asShortBuffer().get(pcm, 0, read)
}
val read = engine.readAudio(pcm)
if (read >= FRAME_SAMPLES) {
applyGain(pcm, read, playoutGainDb)
track.write(pcm, 0, read)