Можно ли получить байтовый буфер непосредственно из аудиоресурса в OpenSL ES (для Android)?

Я хотел бы получить байтовый буфер из аудиоресурса с помощью объекта OpenSL ES FileDescriptor, чтобы я мог неоднократно ставить его в очередь в SimpleBufferQueue вместо использования интерфейсов SL для воспроизведения/остановки/поиска файла.

Есть три основные причины, по которым я хотел бы напрямую управлять байтами выборки:

  1. OpenSL использует слой AudioTrack для воспроизведения/остановки/и т. д. для объектов Player. Это не только приводит к нежелательным накладным расходам, но также содержит несколько ошибок, а быстрые запуски/остановки плеера вызывают множество проблем.
  2. Мне нужно манипулировать байтовым буфером напрямую для пользовательских эффектов DSP.
  3. Клипы, которые я собираюсь воспроизвести, небольшие, и все они могут быть загружены в память, чтобы избежать накладных расходов на файловый ввод-вывод. Кроме того, постановка в очередь моих собственных буферов позволит мне уменьшить задержку, записывая 0 в выходной приемник и просто переключаясь на байты выборки во время их воспроизведения, а не ОСТАНАВЛИВАЯСЬ, ПРИОСТАНОВЛЯЯ и ВОСПРОИЗВЕДЯ AudioTrack.

Итак, обоснования завершены - вот что я пробовал - у меня есть структура Sample, которая содержит, по сути, входную и выходную дорожки, а также массив байтов для хранения образцов. Ввод — мой проигрыватель FileDescriptor, а вывод — объект SimpleBufferQueue. Вот моя структура:

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;

после инициализации файлового проигрывателя fdPlayerObject и выделения памяти для моего байтового буфера с помощью

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

Я получаю интерфейс BufferQueue с помощью

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

Затем я создаю экземпляр проигрывателя вывода:

// 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);

Когда я хочу воспроизвести образец, я использую:

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));

Однако это приводит к зависанию и закрытию моего приложения. Что-то здесь не так. Кроме того, я бы предпочел не каждый раз получать образцы из BufferQueue файлового дескриптора. Вместо этого я хотел бы постоянно хранить его в массиве байтов и помещать его в очередь на выходе, когда захочу.


person khiner    schedule 27.05.2012    source источник
comment
Здравствуйте, khiner, поможет ли это вам прочитать .wav-файлы из папки с активами в байтовом или коротком массиве Java, а затем обработать это дальше?   -  person G_J    schedule 30.06.2013
comment
Не уверен, насколько я могу помочь, но просто для протокола - вы используете NDK? Поэтому ваш код на C++?   -  person Erhannis    schedule 04.09.2013
comment
Да, я использую NDK, и на самом деле это чистый C. Этот вопрос уже затянулся. Я добился огромного прогресса в этом и добавлю достойный ответ.   -  person khiner    schedule 05.09.2013
comment
@khiner, пожалуйста, было бы очень интересно услышать, что сработало для вас   -  person Baggers    schedule 08.10.2013


Ответы (1)


Декодирование в PCM доступно на уровне API 14 и выше.

Когда вы создаете проигрыватель декодера, вам нужно установить простую буферную очередь Android в качестве приемника данных:

// 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);

Для очереди декодера вам нужно поставить набор пустых буферов в очередь простых буферов Android, которая будет заполнена данными PCM.

Также вам необходимо зарегистрировать обработчик обратного вызова в очереди декодера, который будет вызываться, когда данные PCM будут готовы. Обработчик обратного вызова должен обработать данные PCM, повторно поставить в очередь пустой буфер и затем вернуться. Приложение отвечает за отслеживание декодированных буферов; список параметров обратного вызова не содержит достаточной информации, чтобы указать, какой буфер был заполнен или какой буфер поставить в очередь следующим.

Декодирование в PCM поддерживает паузу и начальный поиск. Регулятор громкости, эффекты, зацикливание и скорость воспроизведения не поддерживаются.

Прочтите Декодирование аудио в PCM из OpenSL ES для Android для получения дополнительной информации.

person PSyton    schedule 18.09.2013