Pregunta ¿Es posible obtener un búfer de bytes directamente desde un activo de audio en OpenSL ES (para Android)?


Me gustaría obtener un búfer de bytes de un recurso de audio utilizando el objeto OpenSL ES FileDescriptor, de modo que pueda enrutarlo repetidamente a un SimpleBufferQueue, en lugar de utilizar las interfaces SL para reproducir / detener / buscar el archivo.

Hay tres razones principales por las que me gustaría administrar los bytes de muestra directamente:

  1. OpenSL utiliza una capa de AudioTrack para reproducir / detener / etc para los objetos del jugador. Esto no solo introduce una sobrecarga no deseada, sino que también tiene varios errores, y los inicios / paradas rápidas del jugador causan muchos problemas.
  2. Necesito manipular el buffer de byte directamente para efectos DSP personalizados.
  3. Los clips que voy a reproducir son pequeños y pueden cargarse en la memoria para evitar la sobrecarga de E / S de archivos. Además, poner en cola mis propios búferes me permitirá reducir la latencia escribiendo 0 en el receptor de salida y simplemente cambiando a bytes de muestra cuando están jugando, en lugar de DETENER, PAUSAR y JUGAR el AudioTrack.

De acuerdo, entonces las justificaciones se completaron - esto es lo que intenté - Tengo un Muestra struct que contiene, esencialmente, una pista de entrada y salida, y una matriz de bytes para contener las muestras. La entrada es mi reproductor FileDescriptor, y el resultado es un objeto SimpleBufferQueue. Aquí está mi estructura:

typedef struct Sample_ {
    // buffer to hold all samples
    short *buffer;      
    int totalSamples;

    SLObjectItf fdPlayerObject;
    // file descriptor player interfaces
    SLPlayItf fdPlayerPlay;
    SLSeekItf fdPlayerSeek;
    SLMuteSoloItf fdPlayerMuteSolo;
    SLVolumeItf fdPlayerVolume;
    SLAndroidSimpleBufferQueueItf fdBufferQueue;

    SLObjectItf outputPlayerObject; 
    SLPlayItf outputPlayerPlay; 
    // output buffer interfaces
    SLAndroidSimpleBufferQueueItf outputBufferQueue;        
} Sample;

después de inicializar un reproductor de archivos fdPlayerObjecty la memoria malloc -ing para mi buffer byte con

sample->buffer = malloc(sizeof(short)*sample->totalSamples);

Estoy obteniendo su interfaz BufferQueue con

// get the buffer queue interface
result = (*(sample->fdPlayerObject))->GetInterface(sample->fdPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(sample->fdBufferQueue));

Entonces instanciar un reproductor de salida:

// create audio player for output buffer queue
const SLInterfaceID ids1[] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(sample->outputPlayerObject), &outputAudioSrc, &audioSnk,
                                               1, ids1, req1);

// realize the output player
result = (*(sample->outputPlayerObject))->Realize(sample->outputPlayerObject, SL_BOOLEAN_FALSE);
assert(result == SL_RESULT_SUCCESS);

// get the play interface
result = (*(sample->outputPlayerObject))->GetInterface(sample->outputPlayerObject, SL_IID_PLAY, &(sample->outputPlayerPlay));
assert(result == SL_RESULT_SUCCESS);

// get the buffer queue interface for output
result = (*(sample->outputPlayerObject))->GetInterface(sample->outputPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
                                                   &(sample->outputBufferQueue));
assert(result == SL_RESULT_SUCCESS);    

  // set the player's state to playing
result = (*(sample->outputPlayerPlay))->SetPlayState(sample->outputPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(result == SL_RESULT_SUCCESS);

Cuando quiero reproducir la muestra, estoy usando:

Sample *sample = &samples[sampleNum];
// THIS WORKS FOR SIMPLY PLAYING THE SAMPLE, BUT I WANT THE BUFFER DIRECTLY 
//    if (sample->fdPlayerPlay != NULL) {
//        // set the player's state to playing
//        (*(sample->fdPlayerPlay))->SetPlayState(sample->fdPlayerPlay, SL_PLAYSTATE_PLAYING);
//    }

// fill buffer with the samples from the file descriptor
(*(sample->fdBufferQueue))->Enqueue(sample->fdBufferQueue, sample->buffer,sample->totalSamples*sizeof(short));
// write the buffer to the outputBufferQueue, which is already playing
(*(sample->outputBufferQueue))->Enqueue(sample->outputBufferQueue, sample->buffer, sample->totalSamples*sizeof(short));

Sin embargo, esto hace que mi aplicación se congele y se apague. Algo está mal aquí. también, Preferiría no obtener las muestras del BufferQueue del Descriptor de archivos cada vez. En cambio, me gustaría almacenarlo permanentemente en una matriz de bytes, y poner eso a la salida cuando quiera.


32
2018-05-27 01:18


origen


Respuestas:


La decodificación a PCM está disponible en API nivel 14 y superior.

Cuando crea un decodificador, debe configurar la cola de buffer simple de Android como receptor de datos:

// For init use something like this:
SLDataLocator_AndroidFD locatorIn = {SL_DATALOCATOR_ANDROIDFD, decriptor, start, length};
SLDataFormat_MIME dataFormat = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
SLDataSource audioSrc = {&locatorIn, &dataFormat};

SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataSink audioSnk = { &loc_bq, NULL };

const SLInterfaceID ids[2] = {SL_IID_PLAY, SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[2] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

SLresult result = (*engineEngine)->CreateAudioPlayer(engineEngine, &(sample->fdPlayerObject), &outputAudioSrc, &audioSnk, 2, ids1, req1);

Para la cola del decodificador, debe poner en cola un conjunto de búferes vacíos en la cola de búfer simple de Android, que se rellenará con datos PCM.

También necesita registrar un manejador de devolución de llamada con la cola del decodificador a la que se llamará cuando los datos PCM estén listos. El controlador de devolución de llamada debe procesar los datos PCM, volver a poner en cola el búfer ahora vacío y luego regresar. La aplicación es responsable de realizar un seguimiento de los búferes decodificados; la lista de parámetros de devolución de llamada no incluye información suficiente para indicar qué memoria intermedia se rellenó o qué memoria intermedia colocar en secuencia siguiente.

Decode to PCM admite pausa y búsqueda inicial. El control de volumen, los efectos, el bucle y la velocidad de reproducción no son compatibles.

Leer Decodificar audio a PCM de OpenSL ES para Android para más detalles.


5
2017-09-18 04:22