Как зарегистрировать функцию обратного вызова на Android NDK с помощью OpenSL ES - PullRequest
0 голосов
/ 01 октября 2018

В моем проекте Android я использую OpenSL ES для воспроизведения аудиофайлов.Я хотел бы иметь возможность обрабатывать аудио на лету, извлекая аудиосэмплы, обрабатывая их и перенаправляя их на аудиовыход.

Вот что я пробовал до сих пор:

// create the engine and output mix objects
void Java_com_ywl5320_openslaudio_MainActivity_createEngine(JNIEnv* env, jclass clazz, int newsamplerate, int newbuffersize)
{
    SLresult result;
    audioBuffer = calloc(buffersize, sizeof(int16_t));
    buffersize = newbuffersize;
    samplerate = newsamplerate;

    // create engine
    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // realize the engine
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // get the engine interface, which is needed in order to create other objects
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // create output mix, with environmental reverb specified as a non-required interface
    const SLInterfaceID ids[] = {};
    const SLboolean req[] = {};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, ids, req);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // realize the output mix
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;
}

// this callback handler is called every time a buffer finishes playing
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
    if (--nextCount > 0 && NULL != audioBuffer && 0 != nextSize)
    {
        // audio processing...

        // enqueue another buffer
        SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audioBuffer, nextSize);
        assert(SL_RESULT_SUCCESS == result);
        (void)result;
    }
}

// create asset audio player
jboolean Java_com_ywl5320_openslaudio_MainActivity_createAssetAudioPlayer(JNIEnv* env, jclass clazz, jobject assetManager, jstring filename)
{
    SLresult result;

    // convert Java string to UTF-8
    const char *utf8 = (*env)->GetStringUTFChars(env, filename, NULL);
    assert(NULL != utf8);

    // use asset manager to open asset by filename
    AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);
    assert(NULL != mgr);
    AAsset* asset = AAssetManager_open(mgr, utf8, AASSET_MODE_UNKNOWN);

    // release the Java string and UTF-8
    (*env)->ReleaseStringUTFChars(env, filename, utf8);

    // the asset might not be found
    if (NULL == asset) {
        return JNI_FALSE;
    }

    // open asset as file descriptor
    off_t start, length;
    int fd = AAsset_openFileDescriptor(asset, &start, &length);
    assert(0 <= fd);
    AAsset_close(asset);

    // configure audio source
    SLDataLocator_AndroidFD loc_fd = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
    SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
    SLDataSource audioSrc = {&loc_fd, &format_mime};

    // configure audio sink
    SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
    SLDataSink audioSnk = {&loc_outmix, NULL};

    // create audio player
    const SLInterfaceID ids[3] = {SL_IID_PLAY, SL_IID_BUFFERQUEUE, SL_IID_VOLUME};
    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &fdPlayerObject, &audioSrc, &audioSnk, 3, ids, req);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // realize the player
    result = (*fdPlayerObject)->Realize(fdPlayerObject, SL_BOOLEAN_FALSE);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // get the play interface
    result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_PLAY, &fdPlayerPlay);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // get the buffer queue interface
    result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_BUFFERQUEUE, &bqPlayerBufferQueue);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // register callback on the buffer queue
    result = (*bqPlayerBufferQueue)->RegisterCallback(utf8, bqPlayerCallback, NULL);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    // get the volume interface
    result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_VOLUME, &fdPlayerVolume);
    assert(SL_RESULT_SUCCESS == result);
    (void)result;

    return JNI_TRUE;
}

К сожалению, происходит сбой при попытке создать аудиоплеер со следующей ошибкой:

10-04 10:46:10.809 20531-20531/com.ywl5320.openslaudio E/libOpenSLES: can't require SL_IID_BUFFERQUEUE or SL_IID_ANDROIDSIMPLEBUFFERQUEUE with a non-buffer queue data sink
10-04 10:46:10.809 20531-20531/com.ywl5320.openslaudio W/libOpenSLES: Leaving Engine::CreateAudioPlayer (SL_RESULT_FEATURE_UNSUPPORTED)
10-04 10:46:10.809 20531-20531/com.ywl5320.openslaudio A/libc: E:\OpenSLAudio\app\src\main\cpp\opensl_audio.c:260: jboolean Java_com_ywl5320_openslaudio_MainActivity_createAssetAudioPlayer(JNIEnv *, jclass, jobject, jstring): assertion "SL_RESULT_SUCCESS == result" failed
10-04 10:46:10.810 20531-20531/com.ywl5320.openslaudio A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 20531 (320.openslaudio)

    [ 10-04 10:46:10.811   441:  441 W/         ]
    debuggerd: handling request: pid=20531 uid=10230 gid=10230 tid=20531

Похоже, функция (*engineEngine)->CreateAudioPlayer() не работает.

Я также пыталсяследуйте этому учебнику (часть 2 здесь ), но мне пока не удалось заставить его работать, так как я новичок в OpenSL ES.

Поэтому, если кто-то знает, как зарегистрировать функцию обратного вызова, чтобы я мог обработать аудиофрагмент аудиофайла на лету, мне будет интересно.

Спасибо за вашу помощь.

...