C++程序  |  236行  |  9.55 KB

/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "jni_native.h"

#include <stdlib.h>

#include <android/log.h>

#include "loopback.h"

#define LOG_TAG "jni_native"

static int nativeEngineFromThreadType(int threadType) {
    switch (threadType) {
        case AUDIO_THREAD_TYPE_NATIVE_SLES: return NATIVE_ENGINE_SLES;
        case AUDIO_THREAD_TYPE_NATIVE_AAUDIO: return NATIVE_ENGINE_AAUDIO;
    }
    __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
            "unsupported thread type %d", threadType);
    return -1;
}

JNIEXPORT jobject JNICALL
Java_org_drrickorang_loopback_NativeAudioThread_nativeComputeDefaultSettings
(JNIEnv *env, jobject obj __unused, jint bytesPerFrame, jint threadType, jint performanceMode) {
    int engine = nativeEngineFromThreadType(threadType);
    if (engine == -1) return NULL;
    int samplingRate, playerBufferFrameCount, recorderBufferFrameCount;
    if (sEngines[engine].computeDefaultSettings(performanceMode, &samplingRate,
                    &playerBufferFrameCount, &recorderBufferFrameCount) == STATUS_SUCCESS) {
        jclass cls = (*env)->FindClass(env, "org/drrickorang/loopback/TestSettings");
        jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "(III)V");
        jobject testSettings = (*env)->NewObject(env, cls, methodID,
                samplingRate,
                playerBufferFrameCount * bytesPerFrame,
                recorderBufferFrameCount * bytesPerFrame);
        return testSettings;
    } else {
        return NULL;
    }
}

JNIEXPORT jlong JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeInit
  (JNIEnv *env, jobject obj __unused, jint threadType, jint samplingRate, jint frameCount,
   jint micSource, jint performanceMode,
   jint testType, jdouble frequency1, jobject byteBuffer, jshortArray loopbackTone,
   jint maxRecordedLateCallbacks, jint ignoreFirstFrames) {

    int engine = nativeEngineFromThreadType(threadType);
    if (engine == -1) return 0;

    native_engine_instance_t *pInstance =
            (native_engine_instance_t*) malloc(sizeof(native_engine_instance_t));
    if (pInstance == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,
                "failed to allocate a native engine instance");
        return 0;
    }
    void *pContext = NULL;

    char *byteBufferPtr = (*env)->GetDirectBufferAddress(env, byteBuffer);
    int byteBufferLength = (*env)->GetDirectBufferCapacity(env, byteBuffer);

    short *loopbackToneArray = (*env)->GetShortArrayElements(env, loopbackTone, 0);

    if (sEngines[engine].init(&pContext, samplingRate, frameCount, micSource,
                 performanceMode,
                 testType, frequency1, byteBufferPtr, byteBufferLength,
                 loopbackToneArray, maxRecordedLateCallbacks, ignoreFirstFrames) != STATUS_FAIL) {
        pInstance->context = pContext;
        pInstance->methods = &sEngines[engine];
        return (long) pInstance;
    }

    free(pInstance);
    return 0;
}


JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeProcessNext
(JNIEnv *env __unused, jobject obj __unused, jlong handle, jdoubleArray samplesArray,
jlong offset) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;

    long maxSamples = (*env)->GetArrayLength(env, samplesArray);
    double *pSamples = (*env)->GetDoubleArrayElements(env, samplesArray, 0);

    long availableSamples = maxSamples-offset;
    double *pCurrentSample = pSamples+offset;

    __android_log_print(ANDROID_LOG_INFO, LOG_TAG,
            "jni nativeProcessNext currentSample %p, availableSamples %ld ",
            pCurrentSample, availableSamples);

    int samplesRead = pInstance->methods->processNext(
            pInstance->context, pCurrentSample, availableSamples);
    return samplesRead;
}


JNIEXPORT jint JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeDestroy
  (JNIEnv *env __unused, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    int status = pInstance->methods->destroy(&pInstance->context);
    free(pInstance);
    return status;
}


JNIEXPORT jintArray JNICALL
        Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderBufferPeriod
  (JNIEnv *env, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    int* recorderBufferPeriod = pInstance->methods->getRecorderBufferPeriod(
            pInstance->context);

    // get the length = RANGE
    jintArray result = (*env)->NewIntArray(env, RANGE);
    (*env)->SetIntArrayRegion(env, result, 0, RANGE, recorderBufferPeriod);

    return result;
}


JNIEXPORT jint JNICALL
        Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderMaxBufferPeriod
  (JNIEnv *env __unused, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    int recorderMaxBufferPeriod = pInstance->methods->getRecorderMaxBufferPeriod(
            pInstance->context);

    return recorderMaxBufferPeriod;
}


JNIEXPORT jdouble JNICALL
        Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderVarianceBufferPeriod
        (JNIEnv *env __unused, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    int64_t result = pInstance->methods->getRecorderVarianceBufferPeriod(pInstance->context);
    // variance has units ns^2 so we have to square the conversion factor
    double scaled = (double) result / ((double) NANOS_PER_MILLI * (double) NANOS_PER_MILLI);
    return scaled;
}


JNIEXPORT jintArray
JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerBufferPeriod
  (JNIEnv *env __unused, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    int* playerBufferPeriod = pInstance->methods->getPlayerBufferPeriod(pInstance->context);

    jintArray result = (*env)->NewIntArray(env, RANGE);
    (*env)->SetIntArrayRegion(env, result, 0, RANGE, playerBufferPeriod);

    return result;
}


JNIEXPORT jint JNICALL
        Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerMaxBufferPeriod
  (JNIEnv *env __unused, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    int playerMaxBufferPeriod = pInstance->methods->getPlayerMaxBufferPeriod(pInstance->context);

    return playerMaxBufferPeriod;
}


JNIEXPORT jdouble JNICALL
Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerVarianceBufferPeriod
        (JNIEnv *env __unused, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    int64_t result = pInstance->methods->getPlayerVarianceBufferPeriod(pInstance->context);
    // variance has units ns^2 so we have to square the conversion factor
    double scaled = (double) result / ((double) NANOS_PER_MILLI * (double) NANOS_PER_MILLI);
    return scaled;
}


jobject getCallbackTimes(JNIEnv *env, callbackTimeStamps *callbacks, short expectedBufferPeriod){
    jintArray timeStamps = (*env)->NewIntArray(env, callbacks->index);
    (*env)->SetIntArrayRegion(env, timeStamps, 0, callbacks->index, callbacks->timeStampsMs);

    jshortArray callbackLengths = (*env)->NewShortArray(env, callbacks->index);
    (*env)->SetShortArrayRegion(env, callbackLengths, 0, callbacks->index,
                                callbacks->callbackDurations);

    jclass cls = (*env)->FindClass(env, "org/drrickorang/loopback/BufferCallbackTimes");
    jmethodID methodID = (*env)->GetMethodID(env, cls, "<init>", "([I[SZS)V");
    jobject callbackTimes=(*env)->NewObject(env,cls, methodID, timeStamps, callbackLengths,
                                            callbacks->exceededCapacity, expectedBufferPeriod);
    return callbackTimes;
}

JNIEXPORT jobject
JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetPlayerCallbackTimeStamps
        (JNIEnv *env, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    callbackTimeStamps *pTSs;
    int expectedBufferPeriod = pInstance->methods->getPlayerTimeStampsAndExpectedBufferPeriod(
            pInstance->context, &pTSs);
    return getCallbackTimes(env, pTSs, expectedBufferPeriod);
}

JNIEXPORT jobject
JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetRecorderCallbackTimeStamps
        (JNIEnv *env, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    callbackTimeStamps *pTSs;
    int expectedBufferPeriod = pInstance->methods->getRecorderTimeStampsAndExpectedBufferPeriod(
            pInstance->context, &pTSs);
    return getCallbackTimes(env, pTSs, expectedBufferPeriod);
}

JNIEXPORT jint
JNICALL Java_org_drrickorang_loopback_NativeAudioThread_nativeGetCaptureRank
        (JNIEnv *env __unused, jobject obj __unused, jlong handle) {
    native_engine_instance_t *pInstance = (native_engine_instance_t*) handle;
    return pInstance->methods->getCaptureRank(pInstance->context);
}