Como faço para lidar com chamadas para o áudio da JNI sem travar?
-
23-09-2019 - |
Pergunta
Eu estava tentando escrever para uma fita de um retorno de chamada JNI e recebo um sinal 7 (SIGBUS), falha adicional 00000000.
Eu olhei para o exemplo Wolf3d para Odroid e eles parecem usar um Android.OS.Handler para publicar um executável que fará uma atualização no contexto de encadeamento correto. Também tentei o AnextCurrentThread, mas também falhei neste caso.
Ele funciona para reproduzir o som ao fugir do construtor, mesmo que eu o envolva em um thread e depois o publique no manipulador. Quando eu faço o "mesmo" por meio de um retorno de chamada do JNI, ele falha. Suponho que estou fazendo algumas regras, mas não consegui descobrir o que são. Até agora, não encontrei a resposta aqui em So.
Então, eu me pergunto se alguém sabe como isso deve ser feito.
EDIT: Respondido abaixo.
O código a seguir é ilustrar o problema.
Java:
package com.example.jniaudiotrack;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
public class JniAudioTrackActivity extends Activity {
AudioTrack mAudioTrack;
byte[] mArr;
public static final Handler mHandler = new Handler();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mArr = new byte[2048];
for (int i = 0; i < 2048; i++) {
mArr[i] = (byte) (Math.sin(i) * 128);
}
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
11025,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_8BIT,
2048,
AudioTrack.MODE_STREAM);
mAudioTrack.play();
new Thread(new Runnable() {
public void run() {
mHandler.post(new Runnable() {
public void run() {
mAudioTrack.write(mArr, 0, 2048);
Log.i(TAG, "*** Handler from constructor ***");
}
});
}
}).start();
new Thread(new Runnable() {
public void run() {
audioFunc();
}
}).start();
}
public native void audioFunc();
@SuppressWarnings("unused")
private void audioCB() {
mHandler.post(new Runnable() {
public void run() {
mAudioTrack.write(mArr, 0, 2048);
Log.i(TAG, "*** audioCB called ***");
}
});
}
private static final String TAG = "JniAudioTrackActivity";
static {
System.loadLibrary("jni_audiotrack");
}
}
CPP:
#include <jni.h>
extern "C" {
JNIEXPORT void Java_com_example_jniaudiotrack_JniAudioTrackActivity_audioFunc(JNIEnv* env, jobject obj);
}
JNIEXPORT void Java_com_example_jniaudiotrack_JniAudioTrackActivity_audioFunc(JNIEnv* env, jobject obj)
{
JNIEnv* jniEnv;
JavaVM* vm;
env->GetJavaVM(&vm);
vm->AttachCurrentThread(&jniEnv, 0);
jclass cls = env->GetObjectClass(obj);
jmethodID audioCBID = env->GetMethodID(cls, "audioCB", "()V");
if (!audioCBID) {
return;
}
env->CallVoidMethod(cls, audioCBID);
}
Trace Snippet:
I/DEBUG ( 1653): pid: 9811, tid: 9811 >>> com.example.jniaudiotrack <<<
I/DEBUG ( 1653): signal 7 (SIGBUS), fault addr 00000000
I/DEBUG ( 1653): r0 00000800 r1 00000026 r2 00000001 r3 00000000
I/DEBUG ( 1653): r4 42385726 r5 41049e54 r6 bee25570 r7 ad00e540
I/DEBUG ( 1653): r8 000040f8 r9 41048200 10 41049e44 fp 00000000
I/DEBUG ( 1653): ip 000000f8 sp bee25530 lr ad02dbb5 pc ad012358 cpsr 20000010
I/DEBUG ( 1653): #00 pc 00012358 /system/lib/libdvm.so
Solução
Parece ter havido um problema de memória. Fazer o Maudiotrack e o Marr estáticos resolveram. Eu estava enviando o objeto errado para o retorno de chamada. Veja o comentário de Fadden. Eu removi a chamada para anexar o READ, pois ela não fez nenhuma diferença neste caso.
Java:
package com.example.jniaudiotrack;
import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
public class JniAudioTrackActivity extends Activity {
public AudioTrack mAudioTrack;
public byte[] mArr;
public static Handler mHandler = new Handler();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mArr = new byte[2048];
for (int i = 0; i < 2048; i++) {
mArr[i] = (byte) (Math.sin(i) * 128);
}
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
11025,
AudioFormat.CHANNEL_CONFIGURATION_MONO,
AudioFormat.ENCODING_PCM_8BIT,
2048,
AudioTrack.MODE_STREAM);
mAudioTrack.play();
new Thread(new Runnable() {
public void run() {
audioFunc();
}
}).start();
}
public native void audioFunc();
@SuppressWarnings("unused")
private void audioCB() {
mHandler.post(new Runnable() {
public void run() {
mAudioTrack.write(mArr, 0, 2048);
Log.i(TAG, "*** audioCB called ***");
}
});
}
private static final String TAG = "JniAudioTrackActivity";
static {
System.loadLibrary("jni_audiotrack");
}
}
CPP:
#include <jni.h>
extern "C" {
JNIEXPORT void Java_com_example_jniaudiotrack_JniAudioTrackActivity_audioFunc(JNIEnv* env, jobject obj);
}
JNIEXPORT void Java_com_example_jniaudiotrack_JniAudioTrackActivity_audioFunc(JNIEnv* env, jobject obj)
{
jclass cls = env->GetObjectClass(obj);
jmethodID audioCBID = env->GetMethodID(cls, "audioCB", "()V");
if (!audioCBID) {
return;
}
env->CallVoidMethod(obj, audioCBID);
}