AAudio 是作为 OpenSL ES 库的轻量级原生 Android 替代项而开发。 与 OpenSL ES 相比,AAudio API 不仅较小,而且容易使用。
AAudio 是在 Android O 版本中引入的全新 Android C API。
因此 API 是专为需要低延迟的高性能音频应用而设计。 应用通过读取并将数据写入流来与 AAudio 进行通信。
GitHub :AAudio Android demo地址http:// https://github.com/googlesamples/android-audio-high-performance/tree/master/aaudio
使用Android系统底层的OpenSL ES或者AAudio都可以实现一个高性能的音频程序,尤其是AAudio更是简单易用,性能上,功能上都更佳,但是AAudio 是在 Android O 版本中才引入的全新 Android C API,在以前的系统版本中只能使用OpenSL ES。
那么我们需要做的是在新版本系统中使用AAudio在不支持AAudio的系统版本中使用OpenSL ES,两套 API 同时使用。
不要怕困难,因为这是一个即面向未来,又兼顾现在的优秀方案。不要害怕这有多么困难,Google已经帮我们实现了——Oboe。
OpenSL ES 谷歌使用文档介绍如下:注意:开发者应考虑使用开源 Oboe 库,这个库可在 GitHub 上获得。Oboe 是一个 C++ 封装容器,提供与 AAudio 非常相似的 API。Oboe 在 AAudio 可用时对其进行调用,并在 AAudio 不可用时回退到 OpenSL ES。
OpenSL ES™ API 规范的 Android 专用实现。利用这个库,不论您是编写合成器、数字音频工作站、卡拉 OK 应用、游戏还是其他实时应用,都可以使用 C 或 C++ 实现高性能、低延迟的音频。
OpenSL ES 与 Android Java 框架中的 MediaPlayer 和 MediaRecorder API 提供类似的音频功能。OpenSL ES 提供 C 语言接口和 C++ 绑定,让您可以从使用任意一种语言编写的代码中调用 API。
OpenSL ES API 可以帮助您开发和提升应用的音频性能。
如何使用oboe?
官方指导文档在这里,可以通过两种方式使用oboe,这里是把源码拉下来编译到项目。
1、下载oboe库->https://github.com/google/oboeOboe GitHub 主页 : GitHub/Oboe
① 简单使用 : Getting Started
② Oboe 全指南 : Full Guide To Oboe
③ Oboe API 参考 : API reference
④ Android 音频框架发展 : Android audio history
OpenSL 的生命周期如下:
为 OpenSL ES 播放器设置 采样率 与 采样缓冲区参数 :
( 1 ) 在 Java 层获取采样率与采样缓冲区大小 :
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1){ AudioManager myAudioMgr = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // 获取采样率 String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); int defaultSampleRate = Integer.parseInt(sampleRateStr); // 获取采样缓冲区 String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_frameS_PER_BUFFER); int defaultframesPerBurst = Integer.parseInt(framesPerBurstStr);// 调用 Native 方法传入本地层 native_setDefaultStreamValues(defaultSampleRate, defaultframesPerBurst);}
( 2 ) 在 C++ 代码中设置 OpenSL ES 的参数 :
JNIEXPORT void JNICALLJava_com_google_sample_oboe_hellooboe_MainActivity_native_1setDefaultStreamValues(JNIEnv *env, jclass type, jint sampleRate, jint framesPerBurst) { oboe::DefaultStreamValues::SampleRate = (int32_t) sampleRate; oboe::DefaultStreamValues::framesPerBurst = (int32_t) framesPerBurst;}
上述的采样率 和 缓冲区大小是用于 设置 Android 设备内置音频设备 的 , 如 内置扬声器 , 听筒 ;
外置设备 , 如耳机 , 蓝牙音箱 需要设置更大的缓冲区 ;
Oboe 音频流
Oboe 的主要作用是在 Android 应用 和 Android 设备中的 音频 输入 / 输出 设备 之间 操作移动音频数据 ;
Android 应用 输入 / 输出 数据 方案 :
通过使用 回调函数 实现直接从 音频流 中 读取 / 写出 数据
音频数据读写调用 , 可以是 阻塞的 ( 同步 ) , 也可以是 非阻塞的 ( 异步 ) ;
阻塞 : 调用后 , 阻塞等待回应 , 回应收到后 , 才往下执行 ;非阻塞 : 调用后 , 不等待回应 , 直接向后执行后续代码 ;
音频流根据如下属性定义 :
音频流方向 : 音频设备作为 数据源 或 流数据接收器 ( 数据目的地 ) ;共享模式 : 独占模式 / 共享模式 ; 独占模式 下音频流独占该设备 , 其它音频流不允许访问该设备 , 性能高 ; 共享模式 , 多个音频流可以同时访问该设备 , 性能低 ;采样格式 : 音频流数据的采样格式 ;
Oboe 音频设备
音频设备与音频流对应关系 : 每个 Oboe 音频流都需要关联一个单独的音频设备 ; 注意对应关系 , 一个音频设备可以关联多个音频流 , 但是 一个音频流只能关联一个音频设备 ;
音频设备作用 : 音频设备是一个硬件接口或者虚拟端口 , 一般作为 连续的数字音频数据流的 源端 或 目的端 ; 音频设备作为 数据源 或 流数据接收器 ( 数据目的地 ) ;
音频设备举例 : Android 设备的 内置麦克风 , 扬声器 , 电话听筒 , 或外接的耳机 , 蓝牙音箱 等 ;
获取音频设备 : Android 6.0 Marshmallow( API Level 23 ) 及以上的版本 , 可以通过调用 AudioManager 的 getDevices() 方法 , 获取当前的可用音频设备 , 该方法会返回设备的类型和信息 ;
音频设备 ID : 每个音频设备都有一个 唯一的 ID 标识 , 使用该标识 , 可以实现将 音频流 与 指定的 音频设备进行绑定 ; 多数情况下 , 用户不需要自己设置音频设备 , Oboe 会自动选择主设备 , 推荐让 Oboe 自动选择 , 不要进行手动干预 ;
音频流方向 : 音频设备 可以 决定该音频流是 输入流 还是 输出流
输入流 : 麦克风 , 采集音频数据 ; 设备 -> 内存 ;输出流 : 扬声器 , 播放音频数据 ; 内存 -> 设备 ;
打开 Oboe 音频流时 , 系统会检查音频流方向 , 如果你设置的是麦克风 , 但是音频流方向设置成了输出方向 , 那么打开音频流操作就会出错 ;
使用总结:
第一步:导入oboe头文件
第二步:创建builder并设置参数
第三步:设置回调接口
第四步:播放音频
第五步:关闭音频流
下面提供一下Oboe使用实例:
录制器:
setupCommonStreamParameters( oboe::AudioStreamBuilder *builder) { builder->setAudioApi(mAudioApi) ->setSharingMode(oboe::SharingMode::Exclusive) ->setPerformanceMode(oboe::PerformanceMode::LowLatency); return builder; setupRecordingStreamParameters( oboe::AudioStreamBuilder *builder ,bool isCallBack) { builder->setCallback(this) ->setDirection(oboe::Direction::Input) return setupCommonStreamParameters(builder); oboe::AudioStream *mRecordingStream = nullptr; oboe::AudioStreamBuilder builder; setupRecordingStreamParameters(&builder); oboe::Result result = builder.openStream(&mRecordingStream); if (result == oboe::Result::OK && mRecordingStream) { oboe::Result result = mRecordingStream->requestStart(); //开始录制。 }
播放器:
setupCommonStreamParameters( oboe::AudioStreamBuilder *builder) { builder->setAudioApi(mAudioApi) ->setSharingMode(oboe::SharingMode::Exclusive) ->setPerformanceMode(oboe::PerformanceMode::LowLatency); return builder; setupPlayStreamParameters( oboe::AudioStreamBuilder *builder ,bool isCallBack) { builder->setCallback(this) ->setDirection(oboe::Direction::output) return setupCommonStreamParameters(builder); oboe::AudioStream *mPlayStream = nullptr; oboe::AudioStreamBuilder builder; setupPlayStreamParameters(&builder); oboe::Result result = builder.openStream(&mPlayStream); if (result == oboe::Result::OK && mPlayStream) { oboe::Result result = mPlayStream->requestStart(); //开始播放。 }oboe::DataCallbackResult LiveEffectEngine::onAudioReady( oboe::AudioStream *oboeStream, void *audioData, int32_t numframes) { render(audioData,numframes); //audioData放入数据即可播放。 return oboe::DataCallbackResult::Continue; }