guoyu/fronted_uniapp/utils/speech-recorder.js

164 lines
3.9 KiB
JavaScript
Raw Normal View History

2025-12-07 00:11:06 +08:00
/**
* 语音录音工具类
* 使用 uni-app 原生录音 API
* 支持内网环境录音后上传到服务器进行识别
*/
class SpeechRecorder {
constructor() {
this.recorderManager = null
this.isRecording = false
this.tempFilePath = ''
}
/**
* 初始化录音管理器
*/
init() {
this.recorderManager = uni.getRecorderManager()
// 录音开始监听
this.recorderManager.onStart(() => {
console.log('[录音] 开始录音')
this.isRecording = true
})
// 录音结束监听
this.recorderManager.onStop((res) => {
console.log('[录音] 录音结束', res)
this.isRecording = false
this.tempFilePath = res.tempFilePath
})
// 录音错误监听
this.recorderManager.onError((err) => {
console.error('[录音] 录音错误', err)
this.isRecording = false
uni.showToast({
title: '录音失败:' + err.errMsg,
icon: 'none'
})
})
}
/**
* 开始录音
* @param {Object} options 录音配置
*/
start(options = {}) {
if (!this.recorderManager) {
this.init()
}
const defaultOptions = {
duration: 60000, // 最长录音时间(毫秒)
sampleRate: 16000, // 采样率
numberOfChannels: 1, // 声道数
encodeBitRate: 96000, // 编码码率
format: 'mp3' // 音频格式
}
const config = { ...defaultOptions, ...options }
this.recorderManager.start(config)
}
/**
* 停止录音
* @returns {Promise<string>} 录音文件临时路径
*/
stop() {
return new Promise((resolve, reject) => {
if (!this.isRecording) {
reject(new Error('未在录音中'))
return
}
this.recorderManager.onStop((res) => {
resolve(res.tempFilePath)
})
this.recorderManager.stop()
})
}
/**
* 上传录音到服务器进行识别
* @param {string} filePath 录音文件路径
* @param {Object} params 附加参数如题目文本等
* @returns {Promise<Object>} 识别结果
*/
uploadAndRecognize(filePath, params = {}) {
return new Promise((resolve, reject) => {
// 获取服务器配置
const config = require('./config.js').default
const serverUrl = config.API_BASE_URL
uni.uploadFile({
url: `${serverUrl}/api/speech/recognize`,
filePath: filePath,
name: 'audio',
formData: {
...params,
format: 'mp3',
sampleRate: 16000
},
success: (uploadRes) => {
if (uploadRes.statusCode === 200) {
try {
const result = JSON.parse(uploadRes.data)
if (result.code === 200) {
resolve(result.data)
} else {
reject(new Error(result.msg || '识别失败'))
}
} catch (e) {
reject(new Error('解析结果失败'))
}
} else {
reject(new Error('上传失败'))
}
},
fail: (err) => {
reject(err)
}
})
})
}
/**
* 完整的语音评测流程
* @param {string} referenceText 参考文本标准答案
* @param {number} duration 录音时长毫秒
* @returns {Promise<Object>} 评测结果
*/
async evaluate(referenceText, duration = 10000) {
try {
// 1. 开始录音
this.start({ duration })
// 2. 等待录音完成(用户手动停止或超时)
const filePath = await this.stop()
// 3. 上传并识别
const result = await this.uploadAndRecognize(filePath, {
referenceText: referenceText
})
return {
success: true,
...result
}
} catch (error) {
console.error('[语音评测] 失败', error)
return {
success: false,
error: error.message
}
}
}
}
// 导出单例
export default new SpeechRecorder()