【Android 高性能音频】Oboe 音频流打开后 耳机 / 音箱 插拔事件处理 ( 动态注册广播接收者监听耳机插拔事件 | 重新打开 Oboe 音频流 )

简介: 【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 = 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 源码快照 :


目录
相关文章
|
1月前
|
数据库 Android开发 开发者
构建高性能微服务架构:从理论到实践构建高效Android应用:探究Kotlin协程的优势
【2月更文挑战第16天】 在当今快速迭代和竞争激烈的软件市场中,微服务架构以其灵活性、可扩展性和独立部署能力而受到企业的青睐。本文将深入探讨如何构建一个高性能的微服务系统,涵盖从理论基础到具体实现的各个方面。我们将重点讨论服务拆分策略、通信机制、数据一致性以及性能优化等关键主题,为读者提供一个清晰、实用的指南,以便在复杂多变的业务环境中构建和维护健壮的微服务体系结构。 【2月更文挑战第16天】 在移动开发领域,性能优化和流畅的用户体验是至关重要的。随着技术的不断进步,Kotlin作为一种现代编程语言,在Android开发中被广泛采用,尤其是其协程特性为异步编程带来了革命性的改进。本文旨在深入
239 5
|
6月前
|
XML Android开发 数据格式
Android -- Fragment动态注册
Android -- Fragment动态注册
31 0
|
8月前
|
Java API Android开发
Android 静态注册广播接收者和动态注册广播接收者(Android8.0之前和之后)
Android 静态注册广播接收者和动态注册广播接收者(Android8.0之前和之后)
200 0
|
8月前
|
存储 BI 定位技术
高德Android高性能高稳定性代码覆盖率技术实践
高德这套全新的方案,简洁而不简单,巧妙地实现了无Hack采集,在保证高稳定性和不侵入源码的前提下,优雅地实现了生产环境代码覆盖率的高性能采集,已经过高德地图多版本验证,是一套成熟、稳定且高效的方案。
高德Android高性能高稳定性代码覆盖率技术实践
|
8月前
|
XML Android开发 数据格式
Android 基于监听的事件处理机制详解
Android 基于监听的事件处理机制详解
58 0
|
9月前
|
Java Android开发 C++
[Android JNI] --- 静态注册和动态注册实现java和native相互调用
[Android JNI] --- 静态注册和动态注册实现java和native相互调用
109 0
|
10月前
|
XML Java Android开发
Android 实现按钮监听的几种方式
Android 实现按钮监听的几种方式
|
10月前
|
Android开发
Android自定义支持滑动监听View
Android自定义支持滑动监听View
333 0
|
10月前
|
存储 Android开发 开发者
关于安卓媒体资源变动监听(ContentResolver)应用
关于安卓媒体资源变动监听(ContentResolver)应用
177 0
[RK3568 Android11]AudioTrack音频流数据传输
[RK3568 Android11]AudioTrack音频流数据传输
93 0
[RK3568 Android11]AudioTrack音频流数据传输