Я хотел бы получить байтовый буфер из аудиоресурса с помощью объекта OpenSL ES FileDescriptor, чтобы я мог неоднократно ставить его в очередь в SimpleBufferQueue вместо использования интерфейсов SL для воспроизведения/остановки/поиска файла.
Есть три основные причины, по которым я хотел бы напрямую управлять байтами выборки:
- OpenSL использует слой AudioTrack для воспроизведения/остановки/и т. д. для объектов Player. Это не только приводит к нежелательным накладным расходам, но также содержит несколько ошибок, а быстрые запуски/остановки плеера вызывают множество проблем.
- Мне нужно манипулировать байтовым буфером напрямую для пользовательских эффектов DSP.
- Клипы, которые я собираюсь воспроизвести, небольшие, и все они могут быть загружены в память, чтобы избежать накладных расходов на файловый ввод-вывод. Кроме того, постановка в очередь моих собственных буферов позволит мне уменьшить задержку, записывая 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 файлового дескриптора. Вместо этого я хотел бы постоянно хранить его в массиве байтов и помещать его в очередь на выходе, когда захочу.