123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- "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);
|