OpenSLES(Open Sound Library for Embedded Systems)
無授權(quán)費、跨平臺腥寇、針對嵌入式系統(tǒng)精心優(yōu)化的硬件音頻加速API成翩。它為嵌入式移動多媒體設備上的本地應用程序開發(fā)者提供標準化, 高性能,低響應時間的音頻功能實現(xiàn)方法,并實現(xiàn)軟/硬件音頻性能的直接跨平臺部署赦役,降低執(zhí)行難度麻敌,促進高級音頻市場的發(fā)展。在
Android
里面ndk->platforms-> android-xx -> arch-xx ->usr->lib
目錄里面包含了ndk
內(nèi)置的so
掂摔,可以看到支持了libOpenSLES.so
术羔。在
github
上googlesamples/android-ndk可以看到ndk庫的sample
,里面native-audio
目錄就是OpenSLES
得sample
。
-
OpenSLES
播放主要步驟如下:
- 創(chuàng)建接口對象
- 設置混音器
- 創(chuàng)建播放器(錄音器)
- 設置緩沖隊列和回調(diào)函數(shù)
- 設置播放狀態(tài)
- 啟動回調(diào)函數(shù)
- 銷毀
4.1 創(chuàng)建接口對象
// 引擎接口
SLObjectItf engineObject = NULL;
SLEngineItf engineEngine = NULL;
// 創(chuàng)建引擎對象
slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
4.2 設置混音器
//混音器
SLObjectItf outputMixObject = NULL;
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
(*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
(void)result;
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
(void)result;
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);
if (SL_RESULT_SUCCESS == result) {
result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties( outputMixEnvironmentalReverb, &reverbSettings);
(void)result;
}
SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
4.3 創(chuàng)建播放器
//pcm
SLObjectItf pcmPlayerObject = NULL;
SLPlayItf pcmPlayerPlay = NULL;
SLVolumeItf pcmPlayerVolume = NULL;
SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
SLDataFormat_PCM pcm={
SL_DATAFORMAT_PCM,//播放pcm格式的數(shù)據(jù)
2,//2個聲道(立體聲)
SL_SAMPLINGRATE_44_1,//44100hz的頻率
SL_PCMSAMPLEFORMAT_FIXED_16,//位數(shù) 16位
SL_PCMSAMPLEFORMAT_FIXED_16,//和位數(shù)一致就行
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立體聲(前左前右)
SL_BYTEORDER_LITTLEENDIAN//結(jié)束標志
};
SLDataSource slDataSource = {&android_queue, &pcm};
SLDataSink audioSnk = {&outputMix, NULL};
// SL_IID_BUFFERQUEUE:緩沖 SL_IID_VOLUME:音量 SL_IID_PLAYBACKRATE:微調(diào)功能 防止卡頓 微調(diào)功能 SL_IID_MUTESOLO:聲道切換
const SLInterfaceID ids[4] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_PLAYBACKRATE, SL_IID_MUTESOLO};
const SLboolean req[4] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 3, ids, req);
// 初始化播放器
(*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);
//得到接口后調(diào)用 獲取Player接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
4.4 設置緩沖隊列和回調(diào)函數(shù)
//緩沖器隊列接口
SLAndroidSimpleBufferQueueItf pcmBufferQueue;
void *buffer;
uint8_t *out_buffer;
void getPcmData(void **pcm){
while(!feof(pcmFile)) {
int size = static_cast<int>(fread(out_buffer,1,44100 * 2 * 2,pcmFile));
if(out_buffer == NULL) {
LOGI("%s %d", "read end",size);
break;
} else{
LOGI("%s %d", "reading",size);
}
*pcm = out_buffer;
break;
}
}
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context){
//assert(NULL == context);
getPcmData(&buffer);
// for streaming playback, replace this test by logic to find and fill the next buffer
if (NULL != buffer) {
SLresult result;
// enqueue another buffer
result = (*pcmBufferQueue)->Enqueue(pcmBufferQueue, buffer, 44100 * 2 * 2);
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
}
}
// 創(chuàng)建緩沖區(qū)和回調(diào)函數(shù)
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
//緩沖接口回調(diào)
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);
//獲取音量接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);
4.5 設置播放狀態(tài)
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
4.6 啟動回調(diào)函數(shù)
// 主動調(diào)用回調(diào)函數(shù)開始工作
pcmBufferCallBack(pcmBufferQueue, NULL);
4.7 銷毀
if (pcmPlayerObject != NULL) {
(*pcmPlayerObject)->Destroy(pcmPlayerObject);
pcmPlayerObject = NULL;
pcmPlayerPlay = NULL;
pcmBufferQueue = NULL;
pcmPlayerVolume = NULL;
}
if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL;
outputMixEnvironmentalReverb = NULL;
}
if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
engineEngine = NULL;
}
示例代碼如下:
#include <jni.h>
#include <string>
extern "C"
{
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
}
#include <android/log.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"zzw",FORMAT,##__VA_ARGS__);
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"zzw",FORMAT,##__VA_ARGS__);
// 引擎接口
SLObjectItf engineObject = NULL;
SLEngineItf engineEngine = NULL;
//混音器
SLObjectItf outputMixObject = NULL;
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
//pcm
SLObjectItf pcmPlayerObject = NULL;
SLPlayItf pcmPlayerPlay = NULL;
SLVolumeItf pcmPlayerVolume = NULL;
//緩沖器隊列接口
SLAndroidSimpleBufferQueueItf pcmBufferQueue;
FILE *pcmFile;
void *buffer;
uint8_t *out_buffer;
void getPcmData(void **pcm)
{
while(!feof(pcmFile))
{
int size = static_cast<int>(fread(out_buffer,1,44100 * 2 * 2,pcmFile));
if(out_buffer == NULL)
{
LOGI("%s %d", "read end",size);
break;
} else{
LOGI("%s %d", "reading",size);
}
*pcm = out_buffer;
break;
}
}
void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void * context)
{
//assert(NULL == context);
getPcmData(&buffer);
// for streaming playback, replace this test by logic to find and fill the next buffer
if (NULL != buffer) {
SLresult result;
// enqueue another buffer
result = (*pcmBufferQueue)->Enqueue(pcmBufferQueue, buffer, 44100 * 2 * 2);
// the most likely other result is SL_RESULT_BUFFER_INSUFFICIENT,
// which for this code example would indicate a programming error
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_zzw_androidopenslaudio_MainActivity_palypcm(JNIEnv *env, jobject instance,
jstring url_) {
const char *url = env->GetStringUTFChars(url_, 0);
// TODO
//讀取pcm文件
pcmFile = fopen(url, "r");
if(pcmFile == NULL)
{
LOGE("%s", "fopen file error");
return;
}
out_buffer = (uint8_t *) malloc(44100 * 2 * 2);
SLresult result;
//第一步------------------------------------------
// 創(chuàng)建引擎對象
slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
//第二步-------------------------------------------
// 創(chuàng)建混音器
const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
(void)result;
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
(void)result;
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, &outputMixEnvironmentalReverb);
if (SL_RESULT_SUCCESS == result) {
result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
outputMixEnvironmentalReverb, &reverbSettings);
(void)result;
}
SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
// 第三步--------------------------------------------
// 創(chuàng)建播放器
SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
SLDataFormat_PCM pcm={
SL_DATAFORMAT_PCM,//播放pcm格式的數(shù)據(jù)
2,//2個聲道(立體聲)
SL_SAMPLINGRATE_44_1,//44100hz的頻率
SL_PCMSAMPLEFORMAT_FIXED_16,//位數(shù) 16位
SL_PCMSAMPLEFORMAT_FIXED_16,//和位數(shù)一致就行
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立體聲(前左前右)
SL_BYTEORDER_LITTLEENDIAN//結(jié)束標志
};
SLDataSource slDataSource = {&android_queue, &pcm};
SLDataSink audioSnk = {&outputMix, NULL};
// SL_IID_BUFFERQUEUE:緩沖 SL_IID_VOLUME:音量 SL_IID_PLAYBACKRATE:微調(diào)功能 防止卡頓 微調(diào)功能 SL_IID_MUTESOLO:聲道切換
const SLInterfaceID ids[4] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_PLAYBACKRATE, SL_IID_MUTESOLO};
const SLboolean req[4] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource, &audioSnk, 3, ids, req);
// 初始化播放器
(*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);
//得到接口后調(diào)用 獲取Player接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
//第四步---------------------------------------
// 創(chuàng)建緩沖區(qū)和回調(diào)函數(shù)
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
//緩沖接口回調(diào)
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);
//獲取音量接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);
//第五步----------------------------------------
// 設置播放狀態(tài)
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
//第六步----------------------------------------
// 主動調(diào)用回調(diào)函數(shù)開始工作
pcmBufferCallBack(pcmBufferQueue, NULL);
env->ReleaseStringUTFChars(url_, url);
}
- 暫停乙漓、繼續(xù)级历、停止
使用播放控制接口SLPlayItf
//暫停
if (pcmPlayerPlay != NULL) {
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
}
//繼續(xù)
if (pcmPlayerPlay != NULL) {
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
}
//停止
if (pcmPlayerPlay != NULL) {
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
}
- 音量控制
使用音量控制接口SLVolumeItf
//初始化
(*pcmPlayerObject)->GetInterface(pcmPlayerObject,SL_IID_VOLUME,&pcmPlayerVolume);
//設置音量
(*pcmPlayerVolume)->SetVolumeLevel(pcmPlayerVolume, (100 - percent) * -50);
可用示例
void WlAudio::setVolume(int percent) {
volumePercent = percent;
if(pcmVolumePlay != NULL)
{
if(percent > 30)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -20);
}
else if(percent > 25)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -22);
}
else if(percent > 20)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -25);
}
else if(percent > 15)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -28);
}
else if(percent > 10)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -30);
}
else if(percent > 5)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -34);
}
else if(percent > 3)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -37);
}
else if(percent > 0)
{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -40);
}
else{
(*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -100);
}
}
}
- 聲道控制
采用聲道控制接口SLMuteSoloItf
接口
SLMuteSoloItf pcmMutePlay = NULL;
//初始化
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &pcmMutePlay);
// 設置聲道:
(*pcmPlayPlayerMuteSolo)->SetChannelMute(
pcmPlayPlayerMuteSolo,
1, //0右聲道1左聲道
false //聲道是否開啟
);
有效示例:
SLMuteSoloItf pcmMutePlay = NULL;
//初始化
...
void WlAudio::setMute(int mute) {
this->mute = mute;
if(pcmMutePlay != NULL)
{
if(mute == 0)//right
{
(*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, false);
(*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, true);
}
else if(mute == 1)//left
{
(*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, true);
(*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, false);
}
else if(mute == 2)//center
{
(*pcmMutePlay)->SetChannelMute(pcmMutePlay, 1, false);
(*pcmMutePlay)->SetChannelMute(pcmMutePlay, 0, false);
}
}
}
- 錄音
有效示例:
#include <jni.h>
#include <string>
#include "AndroidLog.h"
#include "RecordBuffer.h"
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
bool finish = false;
FILE *recodeFile = NULL;
const static int RECORDER_BUFFER_SIZE = 4096;
SLObjectItf engineObject = NULL;
SLEngineItf engineItf = NULL;
SLObjectItf recordObj = NULL;
SLRecordItf recordItf = NULL;
SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
RecordBuffer *recordBuffer = NULL;
// this callback handler is called every time a buffer finishes recording
void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
fwrite(recordBuffer->getNowBuffer(), 1, RECORDER_BUFFER_SIZE * sizeof(short), recodeFile);
if (finish) {
LOGE("錄制完成");
//設置停止
(*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_STOPPED);
fclose(recodeFile);
//釋放資源
(*recordObj)->Destroy(recordObj);
recordObj = NULL;
recordItf = NULL;
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
engineItf = NULL;
delete (recordBuffer);
} else {
LOGE("正在錄制");
// 入隊
(*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer->getRecordBuffer(),
RECORDER_BUFFER_SIZE * sizeof(short));
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_openslesrecoder_MainActivity_startRecord(JNIEnv *env, jobject instance,
jstring path_) {
const char *path = env->GetStringUTFChars(path_, 0);
finish = false;
recodeFile = fopen(path, "w+");
//1. 創(chuàng)建引擎對象
slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
//2. 實現(xiàn)引擎對象
(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
//3. 獲取引擎接口
(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineItf);
// configure audio source
SLDataLocator_IODevice loc_dev = {SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSrc = {&loc_dev, NULL};
// configure audio sink
SLDataLocator_AndroidSimpleBufferQueue loc_bq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, //PCM格式
2,//立體聲
SL_SAMPLINGRATE_44_1,//44100HZ
SL_PCMSAMPLEFORMAT_FIXED_16,//
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, //左右聲道
SL_BYTEORDER_LITTLEENDIAN};//小尾端
SLDataSink audioSnk = {&loc_bq, &format_pcm};
// (requires the RECORD_AUDIO permission)
const SLInterfaceID id[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
//4. 配置獲取錄音的引擎對象
(*engineItf)->CreateAudioRecorder(engineItf, &recordObj, &audioSrc,
&audioSnk, 1, id, req);
//5. 實現(xiàn)錄音的引擎對象
// realize the audio recorder
(*recordObj)->Realize(recordObj, SL_BOOLEAN_FALSE);
//6. 獲取錄音的引擎接口
//get the record interface
(*recordObj)->GetInterface(recordObj, SL_IID_RECORD, &recordItf);
//7. 獲取緩沖隊列接口
//get the buffer queue interface
(*recordObj)->GetInterface(recordObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&recorderBufferQueue);
//8. 設置錄音回掉
(*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback,
NULL);
recordBuffer = new RecordBuffer(RECORDER_BUFFER_SIZE);
//9. 入隊
(*recorderBufferQueue)->Enqueue(recorderBufferQueue, recordBuffer->getRecordBuffer(),
RECORDER_BUFFER_SIZE * sizeof(short));
//10. 設置狀態(tài)開啟錄音
(*recordItf)->SetRecordState(recordItf, SL_RECORDSTATE_RECORDING);
env->ReleaseStringUTFChars(path_, path);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_openslesrecoder_MainActivity_stopRecord(JNIEnv *env, jobject instance) {
finish = true;
}
RecordBuffer.cpp:
#include "RecordBuffer.h"
RecordBuffer::RecordBuffer(int bufferSize) {
buffer = new short *[2];
for (int i = 0; i < 2; i++) {
buffer[i] = new short[bufferSize];
}
}
short *RecordBuffer::getRecordBuffer() {
index++;
if (index > 1) {
index = 0;
}
return buffer[index];
}
RecordBuffer::~RecordBuffer() {
for (int i = 0; i < 2; i++) {
delete buffer[i];
}
delete buffer;
}
short *RecordBuffer::getNowBuffer() {
return buffer[index];
}
RecordBuffer.h
class RecordBuffer {
public:
short **buffer;
int index = 0;
public:
RecordBuffer(int bufferSize);
~RecordBuffer();
short *getRecordBuffer();
short * getNowBuffer();
};