我想使用 OpenSL ES FileDescriptor 对象从音频资产中获取字节缓冲区,因此我可以将其反复排入 SimpleBufferQueue,而不是使用 SL 接口来播放/停止/查找文件。
我想直接管理样本字节有三个主要原因:
- OpenSL 使用 AudioTrack 层来播放/停止/等播放器对象。这不仅引入了不必要的开销,而且还存在一些错误,并且播放器的快速启动/停止会导致很多问题。
- 我需要直接为自定义 DSP 效果操作字节缓冲区。
- 我要播放的剪辑很小,都可以加载到内存中以避免文件 I/O 开销。另外,将我自己的缓冲区排入队列将允许我通过将 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 中获取样本。相反,我想将它永久存储在一个字节数组中,并在我喜欢的时候将它排入输出。