【Android 高性能音频】Oboe 音频流打开后 耳机 / 音箱 插拔事件处理 ( 动态注册广播接收者监听耳机插拔事件 | 重新打开 Oboe 音频流 )
文章目錄
- 一、動態注冊廣播接收者監聽耳機插拔事件
- 二、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 = falsereturn}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.oboedemoimport 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 = falseoverride 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 = falsereturn}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(): Stringcompanion 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::DataCallbackResultonAudioReady(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 音频流 )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Android 高性能音频】Oboe
- 下一篇: 【Android 安装包优化】使用 li