zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

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

Android事件注册高性能 动态 打开 重新 监听
2023-06-13 09:17:50 时间

文章目录

基于 【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

代码示例 :