OpenSLES播放、录制、声道切换、音量控制
2018-06-25 本文已影响0人
曾大稳丶
-
OpenSLES(Open Sound Library for Embedded Systems)
无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供标准化, 高性能,低响应时间的音频功能实现方法,并实现软/硬件音频性能的直接跨平台部署,降低执行难度,促进高级音频市场的发展。 -
在
Android
里面ndk->platforms-> android-xx -> arch-xx ->usr->lib
目录里面包含了ndk
内置的so
,可以看到支持了libOpenSLES.so
。 -
在
github
上googlesamples/android-ndk可以看到ndk库的sample
,里面native-audio
目录就是OpenSLES
得sample
。
-
OpenSLES
播放主要步骤如下:
- 创建接口对象
- 设置混音器
- 创建播放器(录音器)
- 设置缓冲队列和回调函数
- 设置播放状态
- 启动回调函数
- 销毁
4.1 创建接口对象
// 引擎接口
SLObjectItf engineObject = NULL;
SLEngineItf engineEngine = NULL;
// 创建引擎对象
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 创建播放器
//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格式的数据
2,//2个声道(立体声)
SL_SAMPLINGRATE_44_1,//44100hz的频率
SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)
SL_BYTEORDER_LITTLEENDIAN//结束标志
};
SLDataSource slDataSource = {&android_queue, &pcm};
SLDataSink audioSnk = {&outputMix, NULL};
// SL_IID_BUFFERQUEUE:缓冲 SL_IID_VOLUME:音量 SL_IID_PLAYBACKRATE:微调功能 防止卡顿 微调功能 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);
//得到接口后调用 获取Player接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
4.4 设置缓冲队列和回调函数
//缓冲器队列接口
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
}
}
// 创建缓冲区和回调函数
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
//缓冲接口回调
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);
//获取音量接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);
4.5 设置播放状态
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
4.6 启动回调函数
// 主动调用回调函数开始工作
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;
//第一步------------------------------------------
// 创建引擎对象
slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
(*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
(*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
//第二步-------------------------------------------
// 创建混音器
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};
// 第三步--------------------------------------------
// 创建播放器
SLDataLocator_AndroidSimpleBufferQueue android_queue={SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,2};
SLDataFormat_PCM pcm={
SL_DATAFORMAT_PCM,//播放pcm格式的数据
2,//2个声道(立体声)
SL_SAMPLINGRATE_44_1,//44100hz的频率
SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声(前左前右)
SL_BYTEORDER_LITTLEENDIAN//结束标志
};
SLDataSource slDataSource = {&android_queue, &pcm};
SLDataSink audioSnk = {&outputMix, NULL};
// SL_IID_BUFFERQUEUE:缓冲 SL_IID_VOLUME:音量 SL_IID_PLAYBACKRATE:微调功能 防止卡顿 微调功能 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);
//得到接口后调用 获取Player接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
//第四步---------------------------------------
// 创建缓冲区和回调函数
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
//缓冲接口回调
(*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, NULL);
//获取音量接口
(*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmPlayerVolume);
//第五步----------------------------------------
// 设置播放状态
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
//第六步----------------------------------------
// 主动调用回调函数开始工作
pcmBufferCallBack(pcmBufferQueue, NULL);
env->ReleaseStringUTFChars(url_, url);
}
- 暂停、继续、停止
使用播放控制接口SLPlayItf
//暂停
if (pcmPlayerPlay != NULL) {
(*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
}
//继续
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. 创建引擎对象
slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
//2. 实现引擎对象
(*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. 实现录音的引擎对象
// 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. 设置状态开启录音
(*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();
};