OpenSL ES 是?Khronos Group 為嵌入式系統(tǒng)開發(fā)的調(diào)優(yōu)的免版權(quán)費、跨平臺、硬件加速的音頻 API 規(guī)范。Android 提供了這一 API 規(guī)范的 Android 專有實現(xiàn),并允許應(yīng)用程序通過 NDK 調(diào)用這些 API。Android 應(yīng)用可以借助這一 API,使用 C 或 C++ 實現(xiàn)高性能、低延遲的音頻。OpenSL ES? 標(biāo)準(zhǔn)與 Android Java 框架中的?MediaPlayer?和?MediaRecorderAPI 提供類似的音頻功能。OpenSL ES 提供 C 語言接口和 C++ 綁定,使我們可以從使用任意一種語言編寫的代碼中調(diào)用 API。
OpenSL ES 的使用
與在 Android 應(yīng)用中使用其它的第三方 C/C++ 庫類似,我們需要把 OpenSL ES 添加進(jìn)我們的應(yīng)用中,并編寫調(diào)用 OpenSL ES API 的代碼。把 OpenSL ES 添加進(jìn)我們的應(yīng)用,具體而言指添加相關(guān)的頭文件,并配置我們的動態(tài)鏈接庫的編譯選項以正確的鏈接 OpenSL ES 庫。
Google 的 android ndk-samples 中有許多 NDK 相關(guān)的示例代碼,其中包含有 OpenSL ES 相關(guān)的示例代碼 audio-echo?和?native-audio。
這里通過一段基于 audio-echo 的實現(xiàn)了 Android 平臺的耳返功能的代碼看一下 OpenSL ES API 的基本使用方法。在這段示例代碼中,啟動聲音錄制設(shè)備錄制聲音,錄得的聲音數(shù)據(jù)被放進(jìn)一個中間緩沖區(qū),中間緩沖區(qū)中的數(shù)據(jù)隨后又被送進(jìn)播放設(shè)備播放出來。
配置 OpenSL ES recorder 從錄音設(shè)備拿到音頻數(shù)據(jù),放入緩沖區(qū),這些數(shù)據(jù)再由緩沖區(qū)對象通過回調(diào)傳給外界。Android OpenSL ES 中用 SLAndroidSimpleBufferQueueItf 對象描述緩沖區(qū),這個緩沖區(qū)本身不包含用于存放音頻數(shù)據(jù)的內(nèi)存,而是需要外部分配內(nèi)存,并將分配好的內(nèi)存給到緩沖區(qū)對象。
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *rec) {(static_cast<AudioRecorder *>(rec))->ProcessSLCallback(bq);
}void AudioRecorder::ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq) {
#ifdef ENABLE_LOGrecLog_->logTime();
#endifassert(bq == recBufQueueItf_);sample_buf *dataBuf = NULL;devShadowQueue_->front(&dataBuf);devShadowQueue_->pop();dataBuf->size_ = dataBuf->cap_; // device only calls us when it is really// fullcallback_(ctx_, ENGINE_SERVICE_MSG_RECORDED_AUDIO_AVAILABLE, dataBuf);recQueue_->push(dataBuf);sample_buf *freeBuf;while (freeQueue_->front(&freeBuf) && devShadowQueue_->push(freeBuf)) {freeQueue_->pop();SLresult result = (*bq)->Enqueue(bq, freeBuf->buf_, freeBuf->cap_);SLASSERT(result);}++audioBufCount;// should leave the device to sleep to save power if no buffersif (devShadowQueue_->size() == 0) {LOGI("devShadowQueue size is 0, stop recording");(*recItf_)->SetRecordState(recItf_, SL_RECORDSTATE_STOPPED);}
}
由于 OpenSL ES 總是在緩沖區(qū)滿時調(diào)用回調(diào),則配置的緩沖區(qū)大小對錄制的延遲及回調(diào)調(diào)用的間隔有著重大影響。
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *ctx) {(static_cast<AudioPlayer *>(ctx))->ProcessSLCallback(bq);
}
void AudioPlayer::ProcessSLCallback(SLAndroidSimpleBufferQueueItf bq) {LOGI("AudioPlayer::ProcessSLCallback");
#ifdef ENABLE_LOGlogFile_->logTime();
#endifstd::lock_guard<std::mutex> lock(stopMutex_);// retrieve the finished device buf and put onto the free queue// so recorder could re-use itsample_buf *buf;if (!devShadowQueue_->front(&buf)) {/** This should not happen: we got a callback,* but we have no buffer in deviceShadowedQueue* we lost buffers this way...(ERROR)*/if (callback_) {uint32_t count;callback_(ctx_, ENGINE_SERVICE_MSG_RETRIEVE_DUMP_BUFS, &count);}return;}devShadowQueue_->pop();if (buf != &silentBuf_) {buf->size_ = 0;freeQueue_->push(buf);if (!playQueue_->front(&buf)) {
#ifdef ENABLE_LOGlogFile_->log("%s", "====Warning: running out of the Audio buffers");
#endifreturn;}devShadowQueue_->push(buf);(*bq)->Enqueue(bq, buf->buf_, buf->size_);playQueue_->pop();return;}if (playQueue_->size() < PLAY_KICKSTART_BUFFER_COUNT) {(*bq)->Enqueue(bq, buf->buf_, buf->size_);devShadowQueue_->push(&silentBuf_);return;}assert(PLAY_KICKSTART_BUFFER_COUNT <=(DEVICE_SHADOW_BUFFER_QUEUE_LEN - devShadowQueue_->size()));for (int32_t idx = 0; idx < PLAY_KICKSTART_BUFFER_COUNT; idx++) {playQueue_->front(&buf);playQueue_->pop();devShadowQueue_->push(buf);(*bq)->Enqueue(bq, buf->buf_, buf->size_);}
}
在錄制回調(diào)中,將錄制的數(shù)據(jù)傳給外界,在播放的回調(diào)中,則將外部數(shù)據(jù)拷貝給 OpenSL ES 使用。
銷毀 Player
AudioPlayer::~AudioPlayer() {std::lock_guard<std::mutex> lock(stopMutex_);// destroy buffer queue src.audio player object, and invalidate all associated// interfacesif (playerObjectItf_ != NULL) {(*playerObjectItf_)->Destroy(playerObjectItf_);}// Consume all non-completed src.audio bufferssample_buf *buf = NULL;while (devShadowQueue_->front(&buf)) {buf->size_ = 0;devShadowQueue_->pop();if(buf != &silentBuf_) {freeQueue_->push(buf);}}delete devShadowQueue_;while (playQueue_->front(&buf)) {buf->size_ = 0;playQueue_->pop();freeQueue_->push(buf);}// destroy output mix object, and invalidate all associated interfacesif (outputMixObjectItf_) {(*outputMixObjectItf_)->Destroy(outputMixObjectItf_);}delete[] silentBuf_.buf_;
}