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 创建接口对象
1 |
|
4.2 设置混音器
1 |
|
4.3 创建播放器
1 |
|
4.4 设置缓冲队列和回调函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//缓冲器队列接口
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 设置播放状态
1 | (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING); |
4.6 启动回调函数1
2 // 主动调用回调函数开始工作
pcmBufferCallBack(pcmBufferQueue, NULL);
4.7 销毁1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
}
示例代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
extern "C"
{
}
// 引擎接口
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
1 | //暂停 |
- 音量控制
使用音量控制接口SLVolumeItf
1 | //初始化 |
可用示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41void 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
接口
1 | SLMuteSoloItf pcmMutePlay = NULL; |
有效示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 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);
}
}
}
- 录音
有效示例:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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:
1 |
|
RecordBuffer.h
1 | class RecordBuffer { |