Android 高性能音频Oboe 音频流打开后 耳机 / 音箱 插拔事件处理 ( 动态注册广播接收者监听耳机插拔事件 | 重新打开 Oboe 音频流 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 高性能音频Oboe 音频流打开后 耳机 / 音箱 插拔事件处理 ( 动态注册广播接收者监听耳机插拔事件 | 重新打开 Oboe 音频流 )相关的知识,希望对你有一定的参考价值。
基于 【Android 高性能音频】Oboe 开发流程 ( Oboe 完整代码示例 ) 博客中的示例 , 为该示例添加耳机插拔监听 , 监测到耳机插拔后 , 重新打开 Oboe 音频流 ;
一、动态注册广播接收者监听耳机插拔事件
耳机插拔监听 , 需要监听 android.intent.action.HEADSET_PLUG
广播事件 ;
注意不能使用静态注册的广播接收者监听该事件 , 只能使用代码中动态注册的广播接收者进行监听 ;
还有一点特别注意 , 在 Resume 时 , 也会激活一次耳机插拔事件 , 相当于初始化事件 , 这里屏蔽 Resume 后的第一次耳机插拔事件 , 需要设置标志位 ;
广播接收者代码示例 :
/**
* 广播接收者
* 监听耳机插拔事件
*/
val mHeadsetPlugReceiver: BroadcastReceiver = object : BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent) {
if (intent.hasExtra("state")) {
// resume 第一次忽略耳机插拔事件
if(isResumeIgnore){
isResumeIgnore = false
return
}
if (intent.getIntExtra("state", 0) == 0) {
stringFromJNI()
Toast.makeText(context,
"耳机拔出", Toast.LENGTH_SHORT).show()
} else if (intent.getIntExtra("state", 0) == 1) {
stringFromJNI()
Toast.makeText(context,
"耳机插入", Toast.LENGTH_SHORT).show()
}
}
}
}
注册广播接收者 :
val filter = IntentFilter()
filter.addAction("android.intent.action.HEADSET_PLUG")
registerReceiver(mHeadsetPlugReceiver, filter)
完整代码示例 :
package kim.hsl.oboedemo
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
/**
* 每次 Resume 第一次忽略
*/
var isResumeIgnore = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 创建 Oboe 音频流并发音
sample_text.text = stringFromJNI()
val filter = IntentFilter()
filter.addAction("android.intent.action.HEADSET_PLUG")
registerReceiver(mHeadsetPlugReceiver, filter)
}
override fun onResume() {
super.onResume()
isResumeIgnore = true
}
override fun onPause() {
super.onPause()
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(mHeadsetPlugReceiver)
}
/**
* 广播接收者
* 监听耳机插拔事件
*/
val mHeadsetPlugReceiver: BroadcastReceiver = object : BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent) {
if (intent.hasExtra("state")) {
// resume 第一次忽略耳机插拔事件
if(isResumeIgnore){
isResumeIgnore = false
return
}
if (intent.getIntExtra("state", 0) == 0) {
stringFromJNI()
Toast.makeText(context,
"耳机拔出", Toast.LENGTH_SHORT).show()
} else if (intent.getIntExtra("state", 0) == 1) {
stringFromJNI()
Toast.makeText(context,
"耳机插入", Toast.LENGTH_SHORT).show()
}
}
}
}
/**
* 重新打开 Oboe 音频流
*/
external fun stringFromJNI(): String
companion object {
init {
System.loadLibrary("native-lib")
}
}
}
二、jni 层的 Oboe 播放器代码 ( 重新打开 Oboe 音频流 )
JNI 层代码没有进行修改 ;
Oboe 音频流变量声明为全局变量 , 如果插入耳机 , 再次调用 Java_kim_hsl_oboedemo_MainActivity_stringFromJNI 方法 , 即可重新打开 Oboe 音频流 , 打开时的设备是默认的设备 , 即当前插入的耳机/音箱 ;
// 声明 Oboe 音频流
oboe::ManagedStream managedStream = oboe::ManagedStream();
如果拔出耳机 , 再次调用 Java_kim_hsl_oboedemo_MainActivity_stringFromJNI 方法 , 即可重新打开 Oboe 音频流 , 打开时的设备是默认的设备 , 即手机本身自带的扬声器 ;
完整 C++ 代码示例 :
#include <jni.h>
#include <string>
#include <oboe/Oboe.h>
#include "logging_macros.h"
// 这部分变量是采样相关的 , 与 Oboe 操作无关
// 声道个数 , 2 代表立体声
static int constexpr kChannelCount = 2;
static int constexpr kSampleRate = 48000;
// Wave params, these could be instance variables in order to modify at runtime
static float constexpr kAmplitude = 0.5f;
// 频率
static float constexpr kFrequency = 440;
// PI 圆周率
static float constexpr kPI = M_PI;
// 2 PI 两倍圆周率
static float constexpr kTwoPi = kPI * 2;
// 每次累加的采样值
static double constexpr mPhaseIncrement = kFrequency * kTwoPi / (double) kSampleRate;
// 追踪当前波形位置
float mPhase = 0.0;
// Oboe 音频流回调类
class MyCallback : public oboe::AudiostreamCallback {
public:
oboe::DataCallbackResult
onAudioReady(oboe::AudioStream *audioStream, void *audioData, int32_t numFrames) {
// 需要生成 AudioFormat::Float 类型数据 , 该缓冲区类型也是该类型
// 生产者需要检查该格式
// oboe::AudioStream *audioStream 已经转换为适当的类型
// 获取音频数据缓冲区
auto *floatData = static_cast<float *>(audioData);
// 生成正弦波数据
for (int i = 0; i < numFrames; ++i) {
float sampleValue = kAmplitude * sinf(mPhase);
for (int j = 0; j < kChannelCount; j++) {
floatData[i * kChannelCount + j] = sampleValue;
}
mPhase += mPhaseIncrement;
if (mPhase >= kTwoPi) mPhase -= kTwoPi;
}
LOGI("回调 onAudioReady");
return oboe::DataCallbackResult::Continue;
}
};
// 创建 MyCallback 对象
MyCallback myCallback = MyCallback();
// 声明 Oboe 音频流
oboe::ManagedStream managedStream = oboe::ManagedStream();
extern "C" JNIEXPORT jstring JNICALL
Java_kim_hsl_oboedemo_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
// 1. 音频流构建器
oboe::AudioStreamBuilder builder = oboe::AudioStreamBuilder();
// 设置音频流方向
builder.setDirection(oboe::Direction::Output);
// 设置性能优先级
builder.setPerformanceMode(oboe::PerformanceMode::LowLatency);
// 设置共享模式 , 独占
builder.setSharingMode(oboe::SharingMode::Exclusive);
// 设置音频采样格式
builder.setFormat(oboe::AudioFormat::Float);
// 设置声道数 , 单声道/立体声
builder.setChannelCount(oboe::ChannelCount::Stereo);
// 设置采样率
builder.setSampleRate(48000);
// 设置回调对象 , 注意要设置 AudioStreamCallback * 指针类型
builder.setCallback(&myCallback);
// 2. 通过 AudioStreamBuilder 打开 Oboe 音频流
oboe::Result result = builder.openManagedStream(managedStream);
LOGI("openManagedStream result : %s", oboe::convertToText(result));
// 3. 开始播放
result = managedStream->requestStart();
LOGI("requestStart result : %s", oboe::convertToText(result));
// 返回数据到
std::string hello = "Oboe Test " + std::to_string(static_cast<int>(oboe::PerformanceMode::LowLatency)) + " Result : " + oboe::convertToText(result);
return env->NewStringUTF(hello.c_str());
}
三、相关资料
Oboe GitHub 主页 : GitHub/Oboe
-
① 简单使用 : Getting Started
-
② Oboe 全指南 : Full Guide To Oboe
-
③ Oboe API 参考 : API reference
-
④ Android 音频框架发展 : Android audio history
Oboe API 参考 :
-
API 索引 : https://google.github.io/oboe/reference/namespaceoboe.html
-
Oboe 音频流创建器 : https://google.github.io/oboe/reference/classoboe_1_1_audio_stream_builder.html
-
Oboe 音频流 : https://google.github.io/oboe/reference/classoboe_1_1_audio_stream.html
-
Oboe 音频流回调接口 : https://google.github.io/oboe/reference/classoboe_1_1_audio_stream_callback.html
代码示例 :
-
GitHub 地址 : https://github.com/han1202012/OboeDemo
-
CSDN 源码快照 :
以上是关于Android 高性能音频Oboe 音频流打开后 耳机 / 音箱 插拔事件处理 ( 动态注册广播接收者监听耳机插拔事件 | 重新打开 Oboe 音频流 )的主要内容,如果未能解决你的问题,请参考以下文章
Smule 借助 Oboe 音频库提升用户体验,助力音乐创作 | Android 开发者故事
Smule 借助 Oboe 音频库提升用户体验,助力音乐创作 | Android 开发者故事
错误记录Oboe / AAudio 播放器报错 ( onEventFromServer - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared )(代码片