uniapp实现语音播放功能

10 篇文章 1 订阅

需求点

  1. 语音播放不跟随系统铃声模式。
  2. 语音播放支持扬声器/听筒播放。
  3. 扬声器模式下播放检测到距离接近需要息屏(防误触)并实时转换为听筒播放,距离远离需亮屏并转回扬声器播放。
  4. 听筒模式下播放检测距离变化不需要切换播放声道,但距离接近需要息屏(防误触),距离远离需要亮屏。

分析需求

首先明确一点,语音播放支持扬声器/听筒播放,那就只能使用plus.audio.createPlayer来实现,而切换播放声道,可以使用plus.audio.createPlayer创建出来实例的setRoute()方法。
由3.4点需求,明确在播放语音时需要添加距离监听器,这里可以使用plus.proximity.watchProximity来实时监听设备接近距离。
最后一个接近息屏,远离亮屏的需求,先把前面的做了,再看看如何实现吧。

实现

this.playMode:播放声道(扬声器 0,听筒 1)
this.playState:play回调中播放参数

  1. 距离监听器
this.watchProximity = plus.proximity.watchProximity((distance) => {
	/**
	 * iOS端接近为0,远离为Infinity
	 * Android端接近为0,远离为5
	 */
	if (this.playMode === 0) {
		// 扬声器模式下,需要对声道进行实时修改
		this.voicePlayer.pause();
		if (distance !== 0) {
			// 扬声器
			this.voicePlayer.setRoute(0);
		} else {
			// 听筒
			this.voicePlayer.setRoute(1);
		}
		this.voicePlayer.resume();
	}
});
  1. 初始化播放器
this.voicePlayer = plus.audio.createPlayer({ src: 'xxx' });
this.voicePlayer.setRoute(this.playMode);
// 监听自然播放完成
this.voicePlayer.addEventListener('ended', () => {
	// 销毁正在监听设备距离的监听器
	if (this.watchProximity) {
		plus.proximity.clearWatch(this.watchProximity);
		this.watchProximity = null;
	}
	console.log('播放完毕了');
});
// 监听音频可以开始播放事件
this.voicePlayer.addEventListener('play', () => {
	// 首次播放时会执行两次回调
	if (this.playState !== 2) {
		// plus.audio.ROUTE_SPEAKER:扬声器 0
		// plus.audio.ROUTE_EARPIECE:听筒 1
		this.voicePlayer.setRoute(this.playMode);
		this.playState++;
	}
});
// 监听音频播放错误事件
this.voicePlayer.addEventListener('error', (err) => {
	console.log('报错err', err);
	// 销毁正在监听设备距离的监听器
	if (this.watchProximity) {
		plus.proximity.clearWatch(this.watchProximity);
		this.watchProximity = null;
	}
});
  1. 播放
// 在正确的地方调用播放
this.voicePlayer.play();

播放后,发现语音在iOS端,是默认存在接近息屏,远离亮屏的现象(默认行为);但是Android端在播放的时候,始终是亮屏的,看来Android端还需要自己实现。
播放时,初始播放模式为扬声器模式,接近切换为听筒,远离切换回扬声器,在iOS端是能完美表现的,切换无延迟;Android端切换是存在延迟的,好在延迟不影响播放。
播放时,初始播放模式为听筒模式,Android端第一次播放音频会存在几秒钟的卡顿延迟,且该卡顿是占播放时间的,也就是说第一次播放时不完整的,目前也没有什么比较好的方案解决(暂时搁置)。

Android端实现播放时接近息屏,远离亮屏

该如何下手呢?博主查阅了plus几乎所有的API,都没有能找到相关的内容,看来又只能上Android官网找了。
直接上Android官网找跟电源相关的API,发现了PowerManager,在其中找到了一个电平模式PROXIMITY_SCREEN_OFF_WAKE_LOCK,具体如下:唤醒锁定电平
这不就是我想要的答案吗?事不宜迟,说干就干!

由于PowerManager电源管理类是属于全局唯一的,故使用也不能像播放音频那样随随便便new一个实例出来,他需要借助Android的主实例对象plus.android.runtimeMainActivity()来调用获取。
this.platform: 当前手机平台
this.wakeLock:唤醒锁

// Android端需要设置唤醒模式才能在接近传感器激活时关闭屏幕,iOS端自带了
if (this.platform === 'android') {
	let main = plus.android.runtimeMainActivity();
	let Context = plus.android.importClass('android.content.Context');
	let PowerManager = plus.android.importClass('android.os.PowerManager');
	let pm = main.getSystemService(Context.POWER_SERVICE);
	// 32代表PROXIMITY_SCREEN_OFF_WAKE_LOCK,唤醒锁定电平:当接近传感器激活时关闭屏幕
	let status = pm.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK);
	// 系统支持该唤醒模式
	if (status) {
		this.wakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, 'TAG');
		this.wakeLock.acquire();
	}
}

在刚刚初始化播放器时添加的监听事件中,增加对唤醒模式资源的释放

// 监听自然播放完成
this.voicePlayer.addEventListener('ended', () => {
	// 销毁正在监听设备距离的监听器
	if (this.watchProximity) {
		plus.proximity.clearWatch(this.watchProximity);
		this.watchProximity = null;
	}
	// 释放唤醒锁
	if (this.wakeLock) {
		this.wakeLock.release();
		this.wakeLock = null;
	}
	console.log('播放完毕了');
});
// 监听音频播放错误事件
this.voicePlayer.addEventListener('error', (err) => {
	console.log('报错err', err);
	// 释放唤醒锁
	if (this.wakeLock) {
		this.wakeLock.release();
		this.wakeLock = null;
	}
	// 销毁正在监听设备距离的监听器
	if (this.watchProximity) {
		plus.proximity.clearWatch(this.watchProximity);
		this.watchProximity = null;
	}
});

至此,所有的需求都圆满完成了!

总结

随着对uniapp的深入了解,越来越多功能点的实现,开发人员不止需要把视角放在前端,还需要转战到Android、iOS官方查阅相关的API文档才能实现。
按照这个趋势,博主认为凡是手机系统底层实现了的功能,但uniapp未实现的,都可以通过plus.android或者plus.ios通过调用相关功能类来实现,这泥潭算是越陷越深了。
总而言之,Keep learning…

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值