zhgdyunapp/utils/voiceIntercom.js
2025-09-11 18:14:57 +08:00

517 lines
14 KiB
JavaScript

/**
* 语音对讲工具类
* 支持录音、播放、权限管理等功能
* 适用于uniapp项目
*/
class VoiceIntercom {
constructor() {
this.recorderManager = null;
this.innerAudioContext = null;
this.isRecording = false;
this.isPlaying = false;
this.recordPath = '';
this.audioList = []; // 存储录音文件列表
this.maxRecordTime = 60000; // 最大录音时长60秒
this.recordOptions = {
duration: 60000,
sampleRate: 16000,
numberOfChannels: 1,
encodeBitRate: 96000,
format: 'mp3'
};
}
/**
* 初始化录音管理器
*/
initRecorder() {
if (!this.recorderManager) {
this.recorderManager = uni.getRecorderManager();
this.setupRecorderEvents();
}
return this.recorderManager;
}
/**
* 初始化音频播放器
*/
initPlayer() {
if (!this.innerAudioContext) {
this.innerAudioContext = uni.createInnerAudioContext();
this.setupPlayerEvents();
}
return this.innerAudioContext;
}
/**
* 设置录音事件监听
*/
setupRecorderEvents() {
this.recorderManager.onStart(() => {
console.log('录音开始');
this.isRecording = true;
this.onRecordStart && this.onRecordStart();
});
this.recorderManager.onStop((res) => {
console.log('录音结束', res);
this.isRecording = false;
this.recordPath = res.tempFilePath;
this.audioList.push({
path: res.tempFilePath,
duration: res.duration,
fileSize: res.fileSize,
timestamp: Date.now()
});
this.onRecordStop && this.onRecordStop(res);
});
this.recorderManager.onError((err) => {
console.error('录音错误', err);
this.isRecording = false;
this.onRecordError && this.onRecordError(err);
});
this.recorderManager.onFrameRecorded((res) => {
// 实时录音数据,可用于实时传输
this.onFrameRecorded && this.onFrameRecorded(res);
});
}
/**
* 设置播放器事件监听
*/
setupPlayerEvents() {
this.innerAudioContext.onPlay(() => {
console.log('开始播放');
this.isPlaying = true;
this.onPlayStart && this.onPlayStart();
});
this.innerAudioContext.onEnded(() => {
console.log('播放结束');
this.isPlaying = false;
this.onPlayEnd && this.onPlayEnd();
});
this.innerAudioContext.onError((err) => {
console.error('播放错误', err);
this.isPlaying = false;
this.onPlayError && this.onPlayError(err);
});
this.innerAudioContext.onTimeUpdate(() => {
this.onTimeUpdate && this.onTimeUpdate({
currentTime: this.innerAudioContext.currentTime,
duration: this.innerAudioContext.duration
});
});
}
/**
* 检查录音权限
*/
async checkRecordPermission() {
return new Promise((resolve, reject) => {
// #ifdef H5
this.checkH5RecordPermission().then(resolve).catch(reject)
// #endif
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO
this.checkMiniProgramRecordPermission().then(resolve).catch(reject)
// #endif
// #ifdef APP-PLUS
this.checkAppRecordPermission().then(resolve).catch(reject)
// #endif
})
}
/**
* 申请录音权限
*/
async requestRecordPermission() {
return new Promise((resolve, reject) => {
// #ifdef H5
this.requestH5RecordPermission().then(resolve).catch(reject)
// #endif
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO
this.requestMiniProgramRecordPermission().then(resolve).catch(reject)
// #endif
// #ifdef APP-PLUS
this.requestAppRecordPermission().then(resolve).catch(reject)
// #endif
})
}
/**
* H5平台检查录音权限
*/
async checkH5RecordPermission() {
return new Promise((resolve, reject) => {
// #ifdef H5
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia({ audio: true })
.then(() => {
resolve(true)
})
.catch((error) => {
console.error('H5录音权限检查失败:', error)
reject(error)
})
} else {
reject(new Error('浏览器不支持录音功能'))
}
// #endif
// #ifndef H5
resolve(true)
// #endif
})
}
/**
* H5平台申请录音权限
*/
async requestH5RecordPermission() {
return this.checkH5RecordPermission()
}
/**
* 小程序平台检查录音权限
*/
async checkMiniProgramRecordPermission() {
return new Promise((resolve, reject) => {
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO
uni.getSetting({
success: (res) => {
if (res.authSetting['scope.record'] === true) {
resolve(true)
} else if (res.authSetting['scope.record'] === false) {
reject(new Error('用户拒绝了录音权限'))
} else {
// 未询问过权限
resolve(false)
}
},
fail: (err) => {
reject(err)
}
})
// #endif
// #ifndef MP-WEIXIN && !MP-ALIPAY && !MP-BAIDU && !MP-TOUTIAO
resolve(true)
// #endif
})
}
/**
* 小程序平台申请录音权限
*/
async requestMiniProgramRecordPermission() {
return new Promise((resolve, reject) => {
// #ifdef MP-WEIXIN || MP-ALIPAY || MP-BAIDU || MP-TOUTIAO
uni.authorize({
scope: 'scope.record',
success: () => {
resolve(true)
},
fail: (err) => {
if (err.errMsg.includes('auth deny')) {
// 用户拒绝,引导到设置页面
this.showPermissionModal('录音权限', '需要录音权限才能使用语音对讲功能')
.then(() => {
uni.openSetting({
success: (res) => {
if (res.authSetting['scope.record']) {
resolve(true)
} else {
reject(new Error('用户拒绝录音权限'))
}
},
fail: reject
})
})
.catch(reject)
} else {
reject(err)
}
}
})
// #endif
// #ifndef MP-WEIXIN && !MP-ALIPAY && !MP-BAIDU && !MP-TOUTIAO
resolve(true)
// #endif
})
}
/**
* App平台检查录音权限
*/
async checkAppRecordPermission() {
return new Promise((resolve, reject) => {
// #ifdef APP-PLUS
plus.android.requestPermissions(
['android.permission.RECORD_AUDIO'],
(result) => {
const granted = result.granted && result.granted.length > 0
resolve(granted)
},
(error) => {
reject(error)
}
)
// #endif
// #ifndef APP-PLUS
resolve(true)
// #endif
})
}
/**
* App平台申请录音权限
*/
async requestAppRecordPermission() {
return this.checkAppRecordPermission()
}
/**
* 显示权限申请弹窗
*/
showPermissionModal(permissionName, description) {
return new Promise((resolve, reject) => {
uni.showModal({
title: '权限申请',
content: `需要${permissionName}才能使用相关功能。${description}`,
confirmText: '去设置',
cancelText: '取消',
success: (res) => {
if (res.confirm) {
resolve()
} else {
reject(new Error('用户取消权限申请'))
}
}
})
})
}
/**
* 开始录音
*/
async startRecord(options = {}) {
try {
// 检查权限
const hasPermission = await this.checkRecordPermission();
// 如果没有权限,尝试申请
if (!hasPermission) {
await this.requestRecordPermission();
}
// 初始化录音管理器
this.initRecorder();
// 合并录音配置
const recordOptions = {...this.recordOptions, ...options };
// 开始录音
this.recorderManager.start(recordOptions);
return true;
} catch (error) {
console.error('开始录音失败', error);
throw error;
}
}
/**
* 停止录音
*/
stopRecord() {
if (this.recorderManager && this.isRecording) {
this.recorderManager.stop();
return true;
}
return false;
}
/**
* 播放录音
*/
async playRecord(audioPath = null) {
try {
const path = audioPath || this.recordPath;
if (!path) {
throw new Error('没有可播放的录音文件');
}
// 初始化播放器
this.initPlayer();
// 停止当前播放
if (this.isPlaying) {
this.stopPlay();
}
// 设置音频源并播放
this.innerAudioContext.src = path;
this.innerAudioContext.play();
return true;
} catch (error) {
console.error('播放录音失败', error);
throw error;
}
}
/**
* 停止播放
*/
stopPlay() {
if (this.innerAudioContext && this.isPlaying) {
this.innerAudioContext.stop();
this.isPlaying = false;
return true;
}
return false;
}
/**
* 暂停播放
*/
pausePlay() {
if (this.innerAudioContext && this.isPlaying) {
this.innerAudioContext.pause();
return true;
}
return false;
}
/**
* 恢复播放
*/
resumePlay() {
if (this.innerAudioContext && !this.isPlaying) {
this.innerAudioContext.play();
return true;
}
return false;
}
/**
* 设置播放进度
*/
seekPlay(position) {
if (this.innerAudioContext) {
this.innerAudioContext.seek(position);
return true;
}
return false;
}
/**
* 获取录音列表
*/
getAudioList() {
return this.audioList;
}
/**
* 删除录音文件
*/
deleteAudio(index) {
if (index >= 0 && index < this.audioList.length) {
const audio = this.audioList[index];
// 删除文件
uni.getFileSystemManager().unlink({
filePath: audio.path,
success: () => {
console.log('文件删除成功');
},
fail: (err) => {
console.error('文件删除失败', err);
}
});
// 从列表中移除
this.audioList.splice(index, 1);
return true;
}
return false;
}
/**
* 清空所有录音
*/
clearAllAudio() {
this.audioList.forEach(audio => {
uni.getFileSystemManager().unlink({
filePath: audio.path,
fail: (err) => {
console.error('文件删除失败', err);
}
});
});
this.audioList = [];
this.recordPath = '';
}
/**
* 上传录音文件
*/
async uploadAudio(audioPath, uploadUrl, formData = {}) {
return new Promise((resolve, reject) => {
uni.uploadFile({
url: uploadUrl,
filePath: audioPath,
name: 'audio',
formData: formData,
success: (res) => {
resolve(res);
},
fail: (err) => {
reject(err);
}
});
});
}
/**
* 获取录音状态
*/
getRecordStatus() {
return {
isRecording: this.isRecording,
isPlaying: this.isPlaying,
recordPath: this.recordPath,
audioCount: this.audioList.length
};
}
/**
* 销毁实例
*/
destroy() {
if (this.recorderManager) {
this.recorderManager.stop();
this.recorderManager = null;
}
if (this.innerAudioContext) {
this.innerAudioContext.destroy();
this.innerAudioContext = null;
}
this.isRecording = false;
this.isPlaying = false;
}
}
// 创建单例实例
const voiceIntercom = new VoiceIntercom();
export default voiceIntercom;