是否可以直接从OpenSL ES中的audio资产(Android)获取字节缓冲区?

我想从使用OpenSL ES FileDescriptor对象的audio资产中获得一个字节缓冲区,所以我可以将它重复排入SimpleBufferQueue,而不是使用SL接口来播放/停止/查找文件。

有三个主要原因,我想直接pipe理示例字节:

  1. OpenSL使用AudioTrack层来播放/停止播放器对象等。 这不仅会引入不必要的开销,而且还会有一些错误,玩家的快速启动/停止会带来很多问题。
  2. 我需要直接操作字节缓冲区来定制DSP效果。
  3. 我要播放的片段很小,可以全部加载到内存中以避免文件I / O开销。 另外,排队我自己的缓冲区将允许我通过写入0到输出接收器来减less延迟,并且当他们正在播放时简单地切换到采样字节,而不是停止,暂停和播放audio轨道。

好吧,理由完成 – 这是我已经尝试过 – 我有一个示例结构,其中本质上包含一个input和输出轨道,和一个字节数组来保存样本。 input是我的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)); 

但是,这会导致我的应用程序冻结和closures。 这里不对劲。 另外 ,我宁愿不要每次都从文件描述符的BufferQueue中获取样本。 相反,我想永久存储在一个字节数组,并Enqueue到输出,只要我喜欢。

解码到PCM是API级别14和更高。

当您创build解码器播放器时,您需要将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数据。

您还需要注册一个callback处理程序,并在PCM数据准备就绪时调用解码器队列。 callback处理程序应该处理PCM数据,重新入列现在的缓冲区,然后返回。 应用程序负责跟踪已解码的缓冲区; callback参数列表不包括足够的信息来指示哪个缓冲区被填充或哪个缓冲区接下来排队。

解码到PCM支持暂停和初始查找。 音量控制,效果,循环和回放速率不受支持。

阅读从Android的OpenSL ES 解码audio到PCM更多细节。