views:

693

answers:

1

I was trying to write to an AudioTrack from a jni callback, and I get a signal 7 (SIGBUS), fault addr 00000000.

I have looked at the Wolf3D example for odroid and they seem to use a android.os.Handler to post a Runnable that will do an update in the correct thread context. I have also tried AttachCurrentThread, but I fail in this case also.

It works to play the sound when running from the constructor even if I wrap it in a thread and then post it to the handler. When I do the "same" via a callback from jni it fails. I assume I am braeaking some rules, but I haven't been able to figure out what they are. So far, I haven't found the answer here on SO.

So I wonder if anyone knows how this should be done.

EDIT: Answered below.

The following code is to illustrate the problem.

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
+2  A: 

There seems to have been a memory problem. Making mAudioTrack and mArr static solved it. I was sending the wrong object to the callback. See comment by fadden. I have removed the call to AttachCurrentThread since it did not make any difference in this case.

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);
}
icecream
That should be "env->CallVoidMethod(obj, audioCBID)". If your audioCB tries to do anything with the "this" pointer, it will fail badly. Making the data it needs "static" avoids having to dereference "this". (That's probably the source of your problems; I just read through it too quickly the first time around to spot it.)
fadden
Yes, you are correct. Thanks. I will edit the answer.
icecream