"use strict"; const common_vendor = require("../../common/vendor.js"); const recorderManager = common_vendor.index.getRecorderManager(); const _sfc_main = { name: "VoiceCheckModal", props: { visible: { type: Boolean, default: false } }, data() { return { isRecording: false, waveData: Array(30).fill(20), recordingTime: 0, statusMessage: '点击"开始录制"按钮朗读文字', silenceCounter: 0, recordingTimer: null, volumeLevel: 0, animationTimer: null, lastVolume: 0, hasPermission: false, // 添加录音权限状态 noSoundTimeout: null, // 添加无声音超时计时器 recordingStarted: false, // 添加录音开始标志 audioPath: null, // 添加录音文件路径 platform: "", // 添加平台标识 isRetrying: false // 添加重试标记 }; }, methods: { // 检查录音权限 - 跨平台兼容版本 async checkPermission() { const systemInfo = common_vendor.index.getSystemInfoSync(); const platform = systemInfo.platform; try { if (platform === "ios") { const res = await new Promise((resolve, reject) => { common_vendor.index.getSetting({ success: (result) => { if (result.authSetting["scope.record"]) { resolve(true); } else { common_vendor.index.authorize({ scope: "scope.record", success: () => resolve(true), fail: (err) => reject(err) }); } }, fail: (err) => reject(err) }); }); this.hasPermission = true; return true; } else { const res = await common_vendor.index.authorize({ scope: "scope.record" }); this.hasPermission = true; return true; } } catch (error) { console.error("权限请求失败:", error); common_vendor.index.showModal({ title: "提示", content: "需要录音权限才能进行录音,请在设置中开启录音权限", confirmText: "去设置", success: (res) => { if (res.confirm) { common_vendor.index.openSetting(); } } }); this.hasPermission = false; return false; } }, async startRecording() { if (!await this.checkPermission()) { return; } this.isRecording = true; this.recordingTime = 0; this.silenceCounter = 0; this.statusMessage = "正在检测声音..."; this.waveData = this.waveData.map(() => 20); this.lastVolume = 0; this.recordingStarted = true; const systemInfo = common_vendor.index.getSystemInfoSync(); const platform = systemInfo.platform; const recorderOptions = { duration: 6e4, // 最长录音时间 format: "mp3", // 使用mp3格式提高兼容性 frameSize: 5 }; if (platform === "ios") { recorderOptions.sampleRate = 44100; recorderOptions.numberOfChannels = 1; recorderOptions.encodeBitRate = 96e3; } else { recorderOptions.sampleRate = 16e3; recorderOptions.numberOfChannels = 1; recorderOptions.encodeBitRate = 48e3; } recorderManager.start(recorderOptions); this.recordingTimer = setInterval(() => { this.recordingTime++; }, 1e3); this.animationTimer = setInterval(() => { if (this.isRecording) { this.updateWaveform(); } }, 50); this.noSoundTimeout = setTimeout(() => { if (this.silenceCounter > 20) { this.showNoSoundTip(); } }, 3e3); }, stopRecording() { this.isRecording = false; this.recordingStarted = false; recorderManager.stop(); clearInterval(this.recordingTimer); clearInterval(this.animationTimer); clearTimeout(this.noSoundTimeout); this.statusMessage = "录制完成!"; setTimeout(() => { this.waveData = Array(30).fill(20); }, 500); setTimeout(() => { this.processAudioFile(); this.$emit("complete", { success: true, audioPath: this.audioPath, platform: this.platform }); }, 300); }, // 添加音频文件处理方法 processAudioFile() { if (!this.audioPath) return; const systemInfo = common_vendor.index.getSystemInfoSync(); this.platform = systemInfo.platform; console.log("处理音频文件:", this.audioPath, "平台:", this.platform); if (this.platform === "ios") { if (!this.audioPath.startsWith("file://")) { this.audioPath = "file://" + this.audioPath; } } else if (this.platform === "android") ; else if (this.platform === "harmony") ; common_vendor.index.getFileInfo({ filePath: this.audioPath, success: (res) => { console.log("音频文件信息:", res); if (res.size === 0) { console.warn("警告: 音频文件大小为0"); } }, fail: (err) => { console.error("获取音频文件信息失败:", err); } }); }, // 显示无声音提示 showNoSoundTip() { if (!this.isRecording) return; common_vendor.index.showModal({ title: "未检测到声音", content: "请检查以下问题:\n1. 麦克风是否正常工作\n2. 是否允许使用麦克风\n3. 是否靠近麦克风说话\n4. 说话音量是否足够", showCancel: true, cancelText: "重新录制", confirmText: "继续录制", success: (res) => { if (res.cancel) { this.restartRecording(); } else { this.continueRecording(); } } }); }, // 重新录制方法 restartRecording() { recorderManager.stop(); clearInterval(this.recordingTimer); clearInterval(this.animationTimer); clearTimeout(this.noSoundTimeout); this.isRecording = false; this.recordingStarted = false; this.recordingTime = 0; this.silenceCounter = 0; this.volumeLevel = 0; this.lastVolume = 0; this.waveData = Array(30).fill(20); setTimeout(() => { this.startRecording(); }, 500); }, // 继续录制方法 continueRecording() { this.silenceCounter = 0; this.statusMessage = "正在检测声音..."; clearTimeout(this.noSoundTimeout); this.noSoundTimeout = setTimeout(() => { if (this.silenceCounter > 20) { this.showNoSoundTip(); } }, 3e3); }, detectAudio(int16Array) { const systemInfo = common_vendor.index.getSystemInfoSync(); const platform = systemInfo.platform; let sum = 0; let peak = 0; const blockSize = 128; for (let i = 0; i < int16Array.length; i += blockSize) { const end = Math.min(i + blockSize, int16Array.length); for (let j = i; j < end; j++) { const absValue = Math.abs(int16Array[j]); sum += absValue; peak = Math.max(peak, absValue); } } const avg = sum / int16Array.length; let SPEAK_THRESHOLD = 500; let volumeScale = 2e3; if (platform === "ios") { SPEAK_THRESHOLD = 300; volumeScale = 1500; } else if (platform === "android") { SPEAK_THRESHOLD = 500; volumeScale = 2e3; } else { SPEAK_THRESHOLD = 400; volumeScale = 1800; } const combinedLevel = (avg * 0.7 + peak * 0.3) / volumeScale; this.volumeLevel = this.smoothVolume(combinedLevel); if (avg > SPEAK_THRESHOLD || peak > SPEAK_THRESHOLD * 3) { this.statusMessage = "检测到声音:录音中..."; this.silenceCounter = 0; clearTimeout(this.noSoundTimeout); this.updateWaveform(false); } else { this.silenceCounter++; if (this.silenceCounter > 10) { this.statusMessage = "未检测到声音,请靠近麦克风并说话..."; if (this.silenceCounter === 30 && this.isRecording) { this.showNoSoundTip(); } this.updateWaveform(true); } } }, // 添加音量平滑处理函数 smoothVolume(newVolume) { const smoothFactor = 0.3; const smoothedVolume = this.lastVolume + (newVolume - this.lastVolume) * smoothFactor; this.lastVolume = smoothedVolume; return Math.min(1, Math.max(0, smoothedVolume)); }, updateWaveform(isSilent = false) { const systemInfo = common_vendor.index.getSystemInfoSync(); const platform = systemInfo.platform; let decayFactor = 0.95; let animationScale = 1; if (platform === "ios") { decayFactor = 0.97; animationScale = 0.9; } else if (platform === "android") { decayFactor = 0.95; animationScale = 1; } else { decayFactor = 0.96; animationScale = 0.95; } if (isSilent) { this.waveData = this.waveData.map((height) => Math.max(20, height * decayFactor)); } else { const now = Date.now() * 2e-3; const volumeEffect = this.volumeLevel * animationScale; const baseHeight = 20 + volumeEffect * 60; const waveAmplitude = volumeEffect * 40; this.waveData = this.waveData.map((currentHeight, i) => { const phase = i * 0.2 + now; const wave1 = Math.cos(phase) * 0.5; const wave2 = Math.sin(phase * 1.5) * 0.3; const randomFactor = Math.random() * 0.1; const combinedWave = (wave1 + wave2 + randomFactor) * volumeEffect; const targetHeight = baseHeight + combinedWave * waveAmplitude; const smoothFactor = platform === "ios" ? 0.3 : 0.4; const newHeight = currentHeight + (targetHeight - currentHeight) * smoothFactor; return Math.max(20, Math.min(120, newHeight)); }); } }, handleConfirm() { if (!this.isRecording) { this.startRecording(); } else { this.stopRecording(); } }, formatTime(seconds) { const mins = Math.floor(seconds / 60); const secs = seconds % 60; return `${mins.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}`; } }, mounted() { const systemInfo = common_vendor.index.getSystemInfoSync(); const platform = systemInfo.platform; recorderManager.onError((error) => { console.error("录音错误:", error); let errorMessage = "录音出现错误,请检查麦克风权限或重试"; if (platform === "ios") { if (error.errMsg && error.errMsg.includes("authorize")) { errorMessage = "iOS需要麦克风权限,请在设置中允许访问麦克风"; } else if (error.errMsg && error.errMsg.includes("busy")) { errorMessage = "麦克风正被其他应用使用,请关闭其他使用麦克风的应用"; } } else if (platform === "android") { if (error.errMsg && error.errMsg.includes("permission")) { errorMessage = "安卓系统需要麦克风权限,请在设置中允许访问麦克风"; } } else if (platform === "harmony") { errorMessage = "请确保已授予麦克风权限并重试"; } common_vendor.index.showModal({ title: "录音错误", content: errorMessage, showCancel: false, success: () => { this.stopRecording(); } }); }); recorderManager.onStart(() => { console.log("录音开始"); this.recordingStarted = true; if (platform === "ios") { setTimeout(() => { if (this.silenceCounter > 5) { this.updateWaveform(false); } }, 500); } }); recorderManager.onFrameRecorded((res) => { if (!res.frameBuffer || !this.recordingStarted) return; try { const int16Array = new Int16Array(res.frameBuffer); this.detectAudio(int16Array); } catch (error) { console.error("处理音频帧数据错误:", error); this.updateWaveform(false); } }); recorderManager.onStop((res) => { this.audioPath = res.tempFilePath; this.isRecording = false; this.recordingStarted = false; clearInterval(this.recordingTimer); clearInterval(this.animationTimer); clearTimeout(this.noSoundTimeout); this.statusMessage = "录制完成!"; if (res.tempFilePath) { console.log("录音文件路径:", res.tempFilePath); if (platform === "ios" && !res.tempFilePath.startsWith("file://")) { this.audioPath = "file://" + res.tempFilePath; } else { this.audioPath = res.tempFilePath; } } else { console.error("未获取到录音文件路径"); common_vendor.index.showToast({ title: "录音保存失败", icon: "none" }); } }); recorderManager.onInterruptionBegin && recorderManager.onInterruptionBegin(() => { console.log("录音被中断"); this.statusMessage = "录音被中断,请重试"; this.stopRecording(); }); recorderManager.onInterruptionEnd && recorderManager.onInterruptionEnd(() => { console.log("录音中断结束"); }); }, beforeDestroy() { clearInterval(this.recordingTimer); clearInterval(this.animationTimer); clearTimeout(this.noSoundTimeout); if (this.isRecording) { recorderManager.stop(); } try { this.isRecording = false; this.recordingStarted = false; } catch (error) { console.error("清理资源时出错:", error); } } }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { return common_vendor.e({ a: $props.visible }, $props.visible ? common_vendor.e({ b: $data.isRecording }, $data.isRecording ? { c: common_vendor.f($data.waveData, (item, index, i0) => { return { a: index, b: item + "rpx" }; }) } : {}, { d: common_vendor.t($data.isRecording ? "停止录制" : "开始录制"), e: common_vendor.o((...args) => $options.handleConfirm && $options.handleConfirm(...args)), f: common_vendor.t($data.statusMessage) }) : {}); } const Component = /* @__PURE__ */ common_vendor._export_sfc(_sfc_main, [["render", _sfc_render], ["__scopeId", "data-v-ce199c44"]]); wx.createComponent(Component);