承接上回语音功能的实现《uniapp实现语音播放功能》,仍存在有瑕疵,问题如下:
1. 语音播放时,会直接打断后台正在播放的音乐,播放完毕无自动恢复音乐
2. 语音播放的输出(耳机/扬声器)全凭各种品牌手机的实现,不可控

基于此,博主又深入研究了一下,目前实现了Android端的优化方案,暂无iOS端 o(╥﹏╥)o

背景

首先,语音播放打断音乐的解决方案,在上一篇博客《uniapp实现音频播放抢占系统音频焦点》中已解决,实现的细节均已清晰指明。
由于音频的初始化加载,由原来的plus.audio.createPlayer转为使用android.media.MediaPlayer类来实现,引出问题3. 音频输出频道(扬声器/听筒)的切换问题

初始化播放器

先声明相关变量

data() {return {voicePlayer: null, // 语音播放器watchProximity: null, // 设备距离监听器sessionPlayMode: 0, // 语音播放模式 0扬声器 1听筒wakeLock: null, // Android端唤醒锁audioManager: null, // Android音频管理器audioFocus: false // 音频聚焦状态}
},
onLoad() {plus.android.importClass('android.media.AudioManager');
}

初始化播放器

async initPlayer(src) {return new Promise((resolve) => {let path = plus.io.convertLocalFileSystemURL(src); // 本地文件路径转为系统路径// 用于判断文件是否存在plus.io.resolveLocalFileSystemURL(path,() => {console.log('路径', path);let MediaPlayer = plus.android.importClass('android.media.MediaPlayer');let AudioAttributes = plus.android.importClass('android.media.AudioAttributes');this.voicePlayer = new MediaPlayer();let completionCB = plus.android.implements('android.media.MediaPlayer$OnCompletionListener', {onCompletion: () => {this.voicePlayEnded(); // 语音播放完毕}});this.voicePlayer.setOnCompletionListener(completionCB);this.voicePlayer.setDataSource(path);this.voicePlayer.setAudioAttributes(AudioAttributes.CONTENT_TYPE_SPEECH); // 设置类型为语音resolve(true);},(err) => {// 文件获取失败console.log('文件获取失败,请重新获取', err);// 释放唤醒锁if (this.wakeLock) {this.wakeLock.release();this.wakeLock = null;}// 销毁正在监听设备距离的监听器if (this.watchProximity) {plus.proximity.clearWatch(this.watchProximity);this.watchProximity = null;}resolve(false);});});
}

播放语音

async playVoice(src) {console.log('播放地址', src);if (!(await this.initPlayer(src))) {return;}let main = plus.android.runtimeMainActivity();let Context = plus.android.importClass('android.content.Context');this.audioManager = main.getSystemService(Context.AUDIO_SERVICE);// 判断是否有音乐播放,若存在音乐播放,把音频聚焦到语音上let isMusicActive = this.audioManager.isMusicActive();if (isMusicActive) {this.audioFocus = true;// 请求音频焦点,暂停音乐播放,待语音播放完毕后恢复this.audioManager.requestAudioFocus(null, this.audioManager.STREAM_MUSIC, this.audioManager.AUDIOFOCUS_GAIN_TRANSIENT);}// 设置播放模式let headsetStatus = false;let AudioDeviceInfo = plus.android.importClass('android.media.AudioDeviceInfo');let audioDevices = this.audioManager.getDevices(this.audioManager.GET_DEVICES_OUTPUTS);// 判断设备类型for (let deviceInfo of audioDevices) {let status = plus.android.invoke(deviceInfo, 'getType');if (status == AudioDeviceInfo.TYPE_WIRED_HEADPHONES ||status == AudioDeviceInfo.TYPE_WIRED_HEADSET ||status == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP ||status == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {headsetStatus = true;}}if (headsetStatus) {// 有耳机this.audioManager.setSpeakerphoneOn(false);this.audioManager.setMode(this.audioManager.MODE_NORMAL);this.voicePlayer.setAudioStreamType(this.audioManager.STREAM_MUSIC);} else {// 无耳机if (!this.sessionPlayMode) {// 扬声器this.audioManager.setSpeakerphoneOn(true);this.audioManager.setMode(this.audioManager.MODE_NORMAL);this.voicePlayer.setAudioStreamType(this.audioManager.STREAM_MUSIC);// 扬声器模式下,需要监听距离对声道进行实时修改this.watchProximity = plus.proximity.watchProximity((distance) => {// Android端接近为0,远离为5this.voicePlayer.pause();if (distance !== 0) {// 扬声器this.audioManager.setSpeakerphoneOn(true);this.audioManager.setMode(this.audioManager.MODE_NORMAL);} else {// 听筒this.voicePlayer.seekTo(0); // 播放进度调回0this.audioManager.setSpeakerphoneOn(false);this.audioManager.setMode(this.audioManager.MODE_IN_COMMUNICATION);}this.voicePlayer.start();});} else {// 听筒this.audioManager.setSpeakerphoneOn(false);this.audioManager.setMode(this.audioManager.MODE_IN_COMMUNICATION);this.voicePlayer.setAudioStreamType(this.audioManager.STREAM_VOICE_CALL);}// Android端需要设置唤醒模式才能在接近传感器激活时关闭屏幕let PowerManager = plus.android.importClass('android.os.PowerManager');let pm = main.getSystemService(Context.POWER_SERVICE);// 32代表PROXIMITY_SCREEN_OFF_WAKE_LOCK,唤醒锁定电平:当接近传感器激活时关闭屏幕let wakeStatus = pm.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK);// 系统支持该唤醒模式if (wakeStatus) {this.wakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, 'TAG');this.wakeLock.acquire();}}this.voicePlayer.prepare();this.voicePlayer.start();
}

语音播放完毕

voicePlayEnded() {// 销毁正在监听设备距离的监听器if (this.watchProximity) {plus.proximity.clearWatch(this.watchProximity);this.watchProximity = null;}// 释放唤醒锁if (this.wakeLock) {this.wakeLock.release();this.wakeLock = null;}// 放弃系统音频焦点if (this.audioFocus) {this.audioManager.setMode(this.audioManager.MODE_NORMAL);this.audioManager.abandonAudioFocus(null);this.audioFocus = false;}// 释放资源this.voicePlayer.release();this.voicePlayer = null;
}

分析总结

上述代码片段基本实现了语音播放的各种细节,以及解决文章开头的三个问题,调用仅需要this.playVoice('xxx')传入语音文件路径即可。
针对问题1,使用音频焦点的请求聚焦及放弃,在播放音频时聚焦设置其持续时间为AUDIOFOCUS_GAIN_TRANSIENT,用于指示临时增益或音频焦点的请求,预计会持续很短的时间;
针对问题2,使用音频管理类的getDevices方法获取GET_DEVICES_OUTPUTS输出设备,配合AudioDeviceInfo类,判断输出设备的类型是否为耳机,最后配合问题3的解决方案即可实现耳机/扬声器的切换控制;
针对问题3,使用音频管理类的setSpeakerphoneOnsetMode方法来控制音频切换扬声器/听筒。
至此问题已全部解决,完美实现Android端的语音播放功能,继续加油吧…

附上参考链接:AudioManager类、MediaPlayer类、AudioAttributes类、AudioDeviceInfo类

uniapp语音播放Android端细节进阶实现相关推荐

  1. 基于蓝牙适配器的PC与Android端通讯

    Demo 首先,直接给Demo,对于只想使用的朋友,直接下载使用即可.Demo其实也是从网上爬来的,之后做了各种调试和修改. 原有Demo代码下载,可见地址. 修改后Demo效果如下.效果不太清晰,见 ...

  2. Android音视频学习系列(七) — 从0~1开发一款Android端播放器(支持多协议网络拉流本地文件)

    系列文章 Android音视频学习系列(一) - JNI从入门到精通 Android音视频学习系列(二) - 交叉编译动态库.静态库的入门 Android音视频学习系列(三) - Shell脚本入门 ...

  3. android 自定义推流器,Android直播实现 Android端推流、播放

    最近想实现一个Android直播,但是对于这方面的资料都比较零碎,一开始是打算用ffmpeg来实现编码推流,在搜集资料期间,找到了几个强大的开源库,直接避免了jni的代码,集成后只用少量的java代码 ...

  4. android 推流地址可以多人用,Android直播实现(一)Android端推流、播放

    Android直播实现(一)Android端推流.播放 最近想实现一个Android直播,但是对于这方面的资料都比较零碎,一开始是打算用ffmpeg来实现编码推流,在搜集资料期间,找到了几个强大的开源 ...

  5. uni-app 实现语音播放实现思路和代码

    #uni-app 实现语音播放功能demo == 思路 1== 从消息列表中筛选出单独的语音列表, 在消息列表中添加字段,语音列表的index和消息列表中的index对应 语音列表添加标识,区分播放状 ...

  6. android语音动画,Android逐帧动画的简单使用-语音播放效果的实现

    逐帧动画(Frame-By-Frame Animation)原理很简单,即快速切换不同的图片,形成动画效果.Android中逐帧动画的实现方式也很简单,下面以语音播放效果为例说明. 1. 首先准备图片 ...

  7. android 语音提醒,文字转语音播放

    android本身具有文字转语音播放的TTS,一些第三方的sdk,像百度语音,科大讯飞语音等等都提供了文字转语音播放的功能,但这些第三方sdk大都收费,还挺贵的. 这里推荐两种方法:1.android ...

  8. Android端本地音乐播放器(一)---前言

    前言: 2018时的记录:大概一周多以前(现在是2018.11.26   15:24)android平台开发的课程结束了,要写大作业,最后决定写这个音乐播放器,因为老师在课堂上讲的例子也是这个,前面的 ...

  9. Android 语音播放Media Player

    原文地址: https://developer.android.com/guide/topics/media/mediaplayer.html#viacontentresolver 语音播放 因为实习 ...

最新文章

  1. 2013-7-12学习笔记
  2. 2020 China Collegiate Programming Contest Changchun F - Strange Memory(dsu on tree + 位运算小技巧)
  3. nagios 3.2安装详解(一)
  4. 【linux家常菜】redhat 6.5 安装yum
  5. 调用webservice超时问题的解决
  6. 沃尔沃汽车与高德合作:首款纯电动汽车XC40 RECHARGE采用高德方案
  7. DL_C1_week4_2(build a deep neural network 2)
  8. java开源代码生成器_人人开源之代码生成器(renren-generator)
  9. 步进伺服控制程序 用三菱plc和威纶触摸屏编写
  10. 优秀员工评审表 模板
  11. 联想服务器AR系列,目前为止最好的AR游戏设备竟然出自联想
  12. 使用Arduino控制TB6600步进电机驱动器教程(更新于 2022.03.19)
  13. arcmap坐标点生成线和面(更正版)
  14. 统计学习方法P104 L(w,b,a)计算过程
  15. 人力资源知识图谱搭建及应用
  16. JAVA的AWT组件概述
  17. 小兵大乱斗服务器维修吗,这个游戏有点意思《小兵大乱斗》最新评测来袭
  18. 斐波拉契数列前100项求和c语言,C++斐波那契数列前100项详细数据
  19. listview-requsetdata
  20. WebBrowserのIEバージョンを最新にする。

热门文章

  1. 如何确定gcc是否支持c11,c14,c17
  2. 测试绝地求生显卡使用率软件,多分辨率《绝地求生:大逃杀》显卡性能测试!...
  3. 排除Windows XP无法启动故障
  4. google svn 服务器使用(免费SVN服务器)_陕南赤子_新浪博客
  5. leetcode 网站
  6. 【QT】Qt 之字体设置(QFont)详解
  7. 金仓数据库Kingbase ES如何进行增量备份与恢复
  8. 好用的Mac窗口管理器:Rectangle for Mac
  9. sql 两张表对比多出的字段值
  10. python手机号定位_python实现手机号归属地相关信息查询