行是知之始,知是行之成。这篇文章主要讲述Android 音视频深入 十三 OpenSL ES 制作音乐播放器,能暂停和调整音量(附源码下载)相关的知识,希望能为你提供帮助。
项目地址
https://github.com/979451341/OpenSLAudio
OpenSL ES 是基于NDK也就是c语言的底层开发音频的公开API,通过使用它能够做到标准化, 高性能,低响应时间的音频功能实现方法。
这次是使用OpenSL ES来做一个音乐播放器,它能够播放m4a、mp3文件,并能够暂停和调整音量
播放音乐需要做一些步骤
1.创建声音引擎
首先创建声音引擎的对象接口
result = slCreateEngine(&
engineObject, 0, NULL, 0, NULL, NULL);
然后实现它
result = (*engineObject)->
Realize(engineObject, SL_BOOLEAN_FALSE);
从声音引擎的对象中抓取声音引擎
result = (*engineObject)->
GetInterface(engineObject, SL_IID_ENGINE, &
engineEngine);
创建"
输出混音器"
result = (*engineEngine)->
CreateOutputMix(engineEngine, &
outputMixObject, 1, ids, req);
【Android 音视频深入 十三 OpenSL ES 制作音乐播放器,能暂停和调整音量(附源码下载)】实现输出混合音
result = (*outputMixObject)->
Realize(outputMixObject, SL_BOOLEAN_FALSE);
2.创建声音播放器
创建和实现播放器
// realize the player
result = (*bqPlayerObject)->
Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// get the play interface
result = (*bqPlayerObject)->
GetInterface(bqPlayerObject, SL_IID_PLAY, &
bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result);
(void)result;
3.设置播放缓冲
数据格式配置
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_8,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_CENTER, SL_BYTEORDER_LITTLEENDIAN};
数据定位器 就是定位要播放声音数据的存放位置,分为4种:内存位置,输入/输出设备位置,缓冲区队列位置,和midi缓冲区队列位置。
数据定位器配置
SLDataLocator_androidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
得到了缓存队列接口,并注册
// get the buffer queue interface
result = (*bqPlayerObject)->
GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&
bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// register callback on the buffer queue
result = (*bqPlayerBufferQueue)->
RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
assert(SL_RESULT_SUCCESS == result);
(void)result;
4.获得其他接口用来控制播放
得到声音特效接口
// get the effect send interface
result = (*bqPlayerObject)->
GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,
&
bqPlayerEffectSend);
assert(SL_RESULT_SUCCESS == result);
(void)result;
得到音量接口
// get the volume interface
result = (*bqPlayerObject)->
GetInterface(bqPlayerObject, SL_IID_VOLUME, &
bqPlayerVolume);
assert(SL_RESULT_SUCCESS == result);
(void)result;
// set the player‘s state to playing
result = (*bqPlayerPlay)->
SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result);
(void)result;
5.提供播放数据
打开音乐文件
// convert java string to UTF-8
const char *utf8 = (*env)->
GetStringUTFChars(env, filename, NULL);
assert(NULL != utf8);
// use asset manager to open asset by filename
AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);
assert(NULL != mgr);
AAsset* asset = AAssetManager_open(mgr, utf8, AASSET_MODE_UNKNOWN);
// release the Java string and UTF-8
(*env)->
ReleaseStringUTFChars(env, filename, utf8);
// the asset might not be found
if (NULL == asset) {
return JNI_FALSE;
}// open asset as file descriptor
off_t start, length;
int fd = AAsset_openFileDescriptor(asset, &
start, &
length);
assert(0 <
= fd);
AAsset_close(asset);
设置播放数据
SLDataLocator_AndroidFD loc_fd = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
SLDataFormat_MIME format_mime = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED};
SLDataSource audiosrc = https://www.songbingjia.com/android/{&
loc_fd, &
format_mime};
6.播放音乐
播放音乐只需要通过播放接口改变播放状态就可以了,暂停也是,停止也是,但是暂停必须之前的播放缓存做了才行,否则那暂停就相当于停止了
result = (*fdPlayerPlay)->
SetPlayState(fdPlayerPlay, isPlaying ?
SL_PLAYSTATE_PLAYING : SL_PLAYSTATE_PAUSED);
7.调解音量
SLVolumeItf getVolume()
{
if (fdPlayerVolume != NULL)
return fdPlayerVolume;
else
return bqPlayerVolume;
}
void Java_com_ywl5320_openslaudio_MainActivity_setVolumeAudioPlayer(JNIEnv env, jclass clazz,
jint millibel)
{
SLresult result;
SLVolumeItf volumeItf = getVolume();
if (NULL != volumeItf) {
result = (volumeItf)-> SetVolumeLevel(volumeItf, millibel);
assert(SL_RESULT_SUCCESS == result);
(void)result;
}
}
8.释放资源
关闭app时释放占用资源
void Java_com_ywl5320_openslaudio_MainActivity_shutdown(JNIEnv* env, jclass clazz)
{
// destroy buffer queue audio player object, and invalidate all associated interfaces
if (bqPlayerObject != NULL) {
(*bqPlayerObject)->
Destroy(bqPlayerObject);
bqPlayerObject = NULL;
bqPlayerPlay = NULL;
bqPlayerBufferQueue = NULL;
bqPlayerEffectSend = NULL;
bqPlayerMuteSolo = NULL;
bqPlayerVolume = NULL;
}// destroy file descriptor audio player object, and invalidate all associated interfaces
if (fdPlayerObject != NULL) {
(*fdPlayerObject)->
Destroy(fdPlayerObject);
fdPlayerObject = NULL;
fdPlayerPlay = NULL;
fdPlayerSeek = NULL;
fdPlayerMuteSolo = NULL;
fdPlayerVolume = NULL;
}// destroy output mix object, and invalidate all associated interfaces
if (outputMixObject != NULL) {
(*outputMixObject)->
Destroy(outputMixObject);
outputMixObject = NULL;
outputMixEnvironmentalReverb = NULL;
}// destroy engine object, and invalidate all associated interfaces
if (engineObject != NULL) {
(*engineObject)->
Destroy(engineObject);
engineObject = NULL;
engineEngine = NULL;
}
}
参考文章
http://blog.csdn.net/u013898698/article/details/72822595
http://blog.csdn.net/ywl5320/article/details/78503768
推荐阅读
- Android内存优化1 了解java GC 垃圾回收机制1
- 44.mapping下的数据结构
- Android push推送消息到达成功率优化
- Android中Parcel的分析以及使用
- C4C Cloud Application Studio做ABSL开发的一些性能方面的最佳实践
- 前端转安卓后的经历
- Erlang 中application 导出接口
- @RequestMapping
- Oracle Apps DBA R12.2 Syllabus