/** * 语音录音工具类 * 使用 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} 录音文件临时路径 */ 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} 识别结果 */ 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} 评测结果 */ 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()