164 lines
3.9 KiB
JavaScript
164 lines
3.9 KiB
JavaScript
/**
|
|
* 语音录音工具类
|
|
* 使用 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()
|