1

I am developing a low-level Android library which needs to process audio signals in the JNI to save processing cost. Since I might need to refer to the same audio buffer multiple times, so I decide to keep a pointer of a structure to wrap these audio buffer in C (so multiple C functions can access the same audio buffer). I mainly use the idea borrowed from here and here.

However, things are not working as expected. My program crash after another function try to access the memory allocated by the previous jni calls.

Here is the JNI example to show this problem:

struct AddAudioRet{
    int chCnt;
    int traceCnt;
    int sampleCnt;
    float ***data; // data[chIdx][traceIdx][sampleIdx]; -> reverse order of the Matlab data structure
};
extern "C" jlong Java_XXX_XXX_addAudioSamples(JNIEnv *env, jobject obj, jbyteArray audioToAdd) {
    // some processing codes
    AddAudioRet *ret;
    ret = (AddAudioRet *)malloc(sizeof(AddAudioRet));
    ret->chCnt = ps->recordChCnt; // 2
    ret->traceCnt = repeatToProcess; // 3
    ret->sampleCnt = as->signalSize; // 2400
    jlong retLong = (jlong)ret;
    mylog("retLong (jlong) = %ld", retLong);
    AddAudioRet *temp = (AddAudioRet *)retLong;
    mylog("temp's chCnt %d, traceCnt %d, sampleCnt = %d", temp->chCnt, temp->traceCnt, temp->sampleCnt);
    return retLong; // return the memory address of the ret structure
}

extern "C" void Java_XXX_XXX_debugDumpAddAudioRet(JNIEnv *env, jobject obj, jlong addAudioRet) {
    debug("addAudioRetLong = %ld", addAudioRet);
    debug("ret's chCnt %d, traceCnt %d, sampleCnt = %d", r->chCnt, r->traceCnt, r->sampleCnt);
}

In Android, I call the JNI functions like this:

public native int addAudioSamples(byte[] audioToAdd);
public native void debugDumpAddAudioRet(long addAudioRet);
int testJNI(byte[] data) {
    long ret = addAudioSamples(data);
    debugDumpAddAudioRet(ret);
}

Results:

retLong (jlong) = 547383410656
temp's chCnt 2, traceCnt 3, sampleCnt = 2400
// *** dumped by the debug check ***
addAudioRetLong = 1922564064
ret's chCnt 55646750, traceCnt 82374663, sampleCnt = 1831675530

I know the problem is the type conversion between the memory address and jlong since the memory address outputs are not identical. However, I don't know how it happens, if the conversion is not allowed/legal, I should get the error when I (trivially) dump the "temp" variable, right?

Yu-Chih
  • 345
  • 4
  • 13
  • 1
    Possibly a size problem. Pointers are 64 bits, but long tends to only be 32 bit- generally you need to use a long long to store a pointer. – Gabe Sechan Sep 25 '17 at 21:51
  • Hi Gabe, I guess it is the size problem as well, but I did print the sizeof jlong (= 8 bytes) and I am certain that java long is > 8-byte as well (e.g., long testLong = 547383410656L will be ok). – Yu-Chih Sep 26 '17 at 01:39
  • By any chance, is it possible that an allocated memory address (`malloc` or `new`), gets re-located by some sort of compaction or defragmentation? I know this is absurd, but i'm just clearing some doubts – daparic Dec 28 '20 at 14:51

1 Answers1

0

I found the problem. My native function signature is wrong....

Wrong:

public native int addAudioSamples(byte[] audioToAdd);

Correct:

public native long addAudioSamples(byte[] audioToAdd);

Feel so surprised that Java seems pretty smart to help me do some kind of function overloading automatically.

Yu-Chih
  • 345
  • 4
  • 13
  • Actually, Java will not catch you if you declare `native int addAudioSamples(String audioToAdd)` for same `extern "C" jlong Java_XXX_XXX_addAudioSamples(JNIEnv *env, jobject obj, jbyteArray audioToAdd)`. There is a way to declare overloaded native methods, though: https://stackoverflow.com/a/45024076/192373. – Alex Cohn Sep 26 '17 at 10:12