最後の音声機能「uniappが音声再生機能を実現する」の実装に着手しましたが、まだ欠陥があり、問題は次のとおりです。
1.音声が再生されているときに、バックグラウンドで再生されている音楽を直接中断します音楽は再生後に自動的に再開されません
2. 音声再生 出力 (ヘッドフォン/スピーカー) は、さまざまなブランドの携帯電話の実現に依存し、制御することはできません。
これをもとにブロガーが徹底的に研究した結果、現在のAndroid端
最適化ソリューションにはiOS端末がありませんo(╥﹏╥)o
バックグラウンド
まず、以前のブログ「uniappで音声再生を実現し、システムの音声フォーカスをつかむ」で解決した、音声再生で音楽が途切れるという解決策があり、実装の詳細が明記されています。
オーディオの初期ロードのため、元のクラスではなくクラスをplus.audio.createPlayer
使用して実装されているため、問題3 が発生します。オーディオ出力チャネル (スピーカー/ハンドセット) の切り替えの問題android.media.MediaPlayer
プレーヤーを初期化する
関連する変数を最初に宣言する
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,远离为5
this.voicePlayer.pause();
if (distance !== 0) {
// 扬声器
this.audioManager.setSpeakerphoneOn(true);
this.audioManager.setMode(this.audioManager.MODE_NORMAL);
} else {
// 听筒
this.voicePlayer.seekTo(0); // 播放进度调回0
this.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;
}
分析のまとめ
上記のコード スニペットは、基本的に音声再生のさまざまな詳細を実装しており、この記事の冒頭で説明した 3 つの問題を解決しています.呼び出しは、this.playVoice('xxx')
音声ファイル パスを渡すだけで済みます.
問題 1 については、オーディオ フォーカス リクエストを使用してフォーカスしてギブアップし、オーディオを再生するときにフォーカスの持続時間を設定して、AUDIOFOCUS_GAIN_TRANSIENT
一時的なゲインまたはオーディオ フォーカス リクエストを示します。これは短時間続くことが予想されます。
問題 2 については、オーディオ管理クラスメソッドで出力デバイスgetDevices
を取得し、クラスと連携して出力デバイスの種類がヘッドセットかどうかを判断し、最後に問題 3 のソリューションと連携してヘッドセット/スピーカーの切り替え制御を実現する; 問題 3 に対して、オーディオ管理クラスのメソッドを使用して、オーディオ スイッチ スピーカー/イヤホンを制御します。これまでのところ、すべての問題が解決され、音声再生機能が完全に実現されました。引き続き頑張りましょう...GET_DEVICES_OUTPUTS
AudioDeviceInfo
setSpeakerphoneOn
setMode
Android端
参照リンクが添付されています: AudioManager クラス、MediaPlayer クラス、AudioAttributes クラス、AudioDeviceInfo クラス