本文将实现一个使用OpenSL ES来播放assets目录下mp3歌曲的demo(实际推荐大家使用oboe库)。
Android NDK之高性能音频https://developer.android.google.cn/ndk/guides/audio/opensl/getting-started
Oboe is a C++ library that makes it easy to build high-performance audio apps on Android.https://github.com/google/oboe NDK 软件包中包括 Khronos Group 开发的 OpenSL ES™ 1.0.1 API 规范的 Android 专用实现。利用这个库,不论您是编写合成器、数字音频工作站、卡拉 OK 应用、游戏还是其他实时应用,都可以使用 C 或 C++ 实现高性能、低延迟的音频。
OpenSL ES™ 标准与 Android Java 框架中的 MediaPlayer
和 MediaRecorder
API 提供类似的音频功能。
OpenSL ES API 可以帮助您开发和提升应用的音频性能。
标准 OpenSL ES 头文件 <SLES/OpenSLES.h> 和 <SLES/OpenSLES_Platform.h> 允许音频输入和输出。<SLES/OpenSLES_Android.h> 和 <SLES/OpenSLES_AndroidConfiguration.h> 中提供了其他 Android 专用功能。
OpenSL ES编程简述https://developer.android.google.cn/ndk/guides/audio/opensl/opensl-prog-notes
PlayerX.c
#include <stdlib.h>
#include <assert.h>
#include <jni.h>
#include <string.h>#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>#include <sys/types.h>
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
该示例将包含4个简单的接口
- 引擎初始化(学习 SLObjectItf,SLEngineItf)
- 创建播放器(学习 SLPlayItf)
- 切换播放状态
- 回收资源
static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine = NULL;static SLObjectItf outputMixObject = NULL;
static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;JNIEXPORT void JNICALL
Java_tao_h_playerx_MainActivity_createEngine(JNIEnv *env, jclass clazz) {SLresult result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);assert(SL_RESULT_SUCCESS == result);(void) result;const SLInterfaceID ids[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean req[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;if (result == SL_RESULT_SUCCESS) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void) result;}
}
(void)result 只是用于忽略编译警告
创建基于FD的音频播放器
static SLObjectItf fdPlayerObject = NULL;
static SLPlayItf fdPlayerPlay = NULL;
static SLSeekItf fdPlayerSeek = NULL;JNIEXPORT jboolean JNICALL
Java_tao_h_playerx_MainActivity_createAssetAudioPlayer(JNIEnv *env, jclass clazz,jobject asset_manager, jstring file_name) {SLresult result;const char *utf8 = (*env)->GetStringUTFChars(env, file_name, NULL);assert(NULL != utf8);AAssetManager *mgr = AAssetManager_fromJava(env, asset_manager);assert(NULL != mgr);AAsset *asset = AAssetManager_open(mgr, utf8, AASSET_MODE_UNKNOWN);(*env)->ReleaseStringUTFChars(env, file_name, utf8);if (asset == NULL) {return JNI_FALSE;}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 = {&loc_fd, &format_mime};SLDataLocator_OutputMix loc_output_mix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};SLDataSink audioSink = {&loc_output_mix, NULL};const SLInterfaceID ids[3] = {SL_IID_SEEK, SL_IID_MUTESOLO, SL_IID_VOLUME};const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};result = (*engineEngine)->CreateAudioPlayer(engineEngine, &fdPlayerObject, &audioSrc,&audioSink, 3, ids, req);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*fdPlayerObject)->Realize(fdPlayerObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_PLAY, &fdPlayerPlay);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*fdPlayerObject)->GetInterface(fdPlayerObject, SL_IID_SEEK, &fdPlayerSeek);assert(SL_RESULT_SUCCESS == result);(void) result;result = (*fdPlayerSeek)->SetLoop(fdPlayerSeek, SL_BOOLEAN_TRUE, 0, SL_TIME_UNKNOWN);assert(SL_RESULT_SUCCESS == result);(void) result;return JNI_TRUE;
}
设置播放状态
JNIEXPORT void JNICALL
Java_tao_h_playerx_MainActivity_setPlayingAssetAudioPlayer(JNIEnv *env, jclass clazz,jboolean is_playing) {SLresult result;if (NULL != fdPlayerPlay) {result = (*fdPlayerPlay)->SetPlayState(fdPlayerPlay, is_playing ? SL_PLAYSTATE_PLAYING: SL_PLAYSTATE_PAUSED);assert(SL_RESULT_SUCCESS == result);(void) result;}
}
回收资源(务必按照与创建时相反的顺序进行资源释放)
JNIEXPORT void JNICALL
Java_tao_h_playerx_MainActivity_shutdown(JNIEnv *env, jclass clazz) {if (fdPlayerObject != NULL) {(*fdPlayerObject)->Destroy(fdPlayerObject);fdPlayerPlay = NULL;fdPlayerSeek = NULL;}if (outputMixObject != NULL) {(*outputMixObject)->Destroy(outputMixObject);outputMixObject = NULL;outputMixEnvironmentalReverb = NULL;}if (engineObject != NULL) {(*engineObject)->Destroy(engineObject);engineObject = NULL;engineEngine = NULL;}
}
创建简单的页面来进行播放,布局就不放上来了
public class MainActivity extends AppCompatActivity {static {System.loadLibrary("PlayerX");}private AssetManager mAssetManager;private boolean isPlayingAsset = false;public static native void createEngine();public static native boolean createAssetAudioPlayer(AssetManager assetManager, String fileName);public static native void setPlayingAssetAudioPlayer(boolean isPlaying);public static native void shutdown();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mAssetManager = getAssets();createEngine();findViewById(R.id.asset_song).setOnClickListener(new View.OnClickListener() {boolean created = false;@Overridepublic void onClick(View v) {if (!created) {created = createAssetAudioPlayer(mAssetManager, "Legend Of Heroes.mp3");}if (created) {isPlayingAsset = !isPlayingAsset;setPlayingAssetAudioPlayer(isPlayingAsset);}}});}@Overrideprotected void onDestroy() {shutdown();super.onDestroy();}
}
CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)project("PlayerX")set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall")add_library(PlayerXSHAREDPlayerX.c)target_link_libraries(PlayerXandroidlogOpenSLES)