文章目录
一、动态注册广播接收者监听耳机插拔事件
二、jni 层的 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 源码快照 :