Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 90d0b9e1 authored by Eric Laurent's avatar Eric Laurent Committed by Narayan Kamath
Browse files

fix AudioTrack and AudioRecord JNI

AudioTrack and AudioRecord JNI should not cast
jshortArray to jbyteArray. This appeared to work with Dalvik
but causes data corruption with ART.

(cherry picked from commit 9d02848e902d04417df616354db3a18c03e639b7)

Change-Id: Ie36624d3ea06042373c64edced4b5e30b7b1ee86
parent 104da1e4
Loading
Loading
Loading
Loading
+39 −6
Original line number Diff line number Diff line
@@ -399,13 +399,46 @@ static jint android_media_AudioRecord_readInShortArray(JNIEnv *env, jobject thi
                                                        jshortArray javaAudioData,
                                                        jint offsetInShorts, jint sizeInShorts) {

    jint read = android_media_AudioRecord_readInByteArray(env, thiz,
                                                        (jbyteArray) javaAudioData,
                                                        offsetInShorts*2, sizeInShorts*2);
    if (read > 0) {
        read /= 2;
    jshort* recordBuff = NULL;
    // get the audio recorder from which we'll read new audio samples
    sp<AudioRecord> lpRecorder = getAudioRecord(env, thiz);
    if (lpRecorder == NULL) {
        ALOGE("Unable to retrieve AudioRecord object, can't record");
        return 0;
    }
    return read;

    if (!javaAudioData) {
        ALOGE("Invalid Java array to store recorded audio, can't record");
        return 0;
    }

    // get the pointer to where we'll record the audio
    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
    // a way that it becomes much more efficient. When doing so, we will have to prevent the
    // AudioSystem callback to be called while in critical section (in case of media server
    // process crash for instance)
    recordBuff = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);

    if (recordBuff == NULL) {
        ALOGE("Error retrieving destination for recorded audio data, can't record");
        return 0;
    }

    // read the new audio data from the native AudioRecord object
    const size_t recorderBuffSize = lpRecorder->frameCount()*lpRecorder->frameSize();
    const size_t sizeInBytes = sizeInShorts * sizeof(short);
    ssize_t readSize = lpRecorder->read(recordBuff + offsetInShorts * sizeof(short),
                                        sizeInBytes > recorderBuffSize ?
                                            recorderBuffSize : sizeInBytes);

    env->ReleaseShortArrayElements(javaAudioData, recordBuff, 0);

    if (readSize < 0) {
        readSize = AUDIORECORD_ERROR_INVALID_OPERATION;
    } else {
        readSize /= sizeof(short);
    }
    return (jint) readSize;
}

// ----------------------------------------------------------------------------
+31 −5
Original line number Diff line number Diff line
@@ -595,13 +595,39 @@ static jint android_media_AudioTrack_native_write_short(JNIEnv *env, jobject th
                                                  jshortArray javaAudioData,
                                                  jint offsetInShorts, jint sizeInShorts,
                                                  jint javaAudioFormat) {
    jint written = android_media_AudioTrack_native_write_byte(env, thiz,
                                                 (jbyteArray) javaAudioData,
                                                 offsetInShorts*2, sizeInShorts*2,
                                                 javaAudioFormat);
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    if (lpTrack == NULL) {
        jniThrowException(env, "java/lang/IllegalStateException",
            "Unable to retrieve AudioTrack pointer for write()");
        return 0;
    }

    // get the pointer for the audio data from the java array
    // NOTE: We may use GetPrimitiveArrayCritical() when the JNI implementation changes in such
    // a way that it becomes much more efficient. When doing so, we will have to prevent the
    // AudioSystem callback to be called while in critical section (in case of media server
    // process crash for instance)
    jshort* cAudioData = NULL;
    if (javaAudioData) {
        cAudioData = (jshort *)env->GetShortArrayElements(javaAudioData, NULL);
        if (cAudioData == NULL) {
            ALOGE("Error retrieving source of audio data to play, can't play");
            return 0; // out of memory or no data to load
        }
    } else {
        ALOGE("NULL java array of audio data to play, can't play");
        return 0;
    }
    jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
                                offsetInShorts * sizeof(short), sizeInShorts * sizeof(short));
    env->ReleaseShortArrayElements(javaAudioData, cAudioData, 0);

    if (written > 0) {
        written /= 2;
        written /= sizeof(short);
    }
    //ALOGV("write wrote %d (tried %d) shorts in the native AudioTrack with offset %d",
    //     (int)written, (int)(sizeInShorts), (int)offsetInShorts);

    return written;
}