import { Popup, Snackbar } from "@varlet/ui"; import { defineComponent, onMounted, reactive, watch } from "vue"; import { connectWebsocket, evaluatingData, handleEndBegin, handleEndSoundCheck, handlePerformDetection, handleStartBegin, handleStartEvaluat, handleViewReport, } from "/src/view/evaluating"; import Earphone from "./earphone"; import styles from "./index.module.less"; import SoundEffect from "./sound-effect"; import state from "/src/state"; import { storeData } from "/src/store"; import { browser } from "/src/utils"; import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic"; import { Icon, NoticeBar, showToast, Swipe, SwipeItem } from "vant"; import iconStudent from "./icons/student.png"; import EvaluatResult from "./evaluat-result"; import EvaluatAudio from "./evaluat-audio"; import { api_openWebView, api_proxyServiceMessage, api_videoUpdate } from "/src/helpers/communication"; import EvaluatShare from "./evaluat-share"; // frequency 频率, amplitude 振幅, decibels 分贝 type TCriteria = "frequency" | "amplitude" | "decibels"; export default defineComponent({ name: "evaluat-model", setup() { const evaluatModel = reactive({ tips: true, evaluatUpdateAudio: false, isSaveVideo: state.setting.camera && state.setting.saveToAlbum, shareMode: false, }); /** * 木管(长笛 萨克斯 单簧管)乐器一级的2、3、6测评要放原音音频 * 铜管乐器一级的1a,1b,5,6测评要放原音音频 */ const getMusicMode = () => { const muguan = [2, 4, 5, 6]; const tongguan = [12, 13, 14, 15, 17]; if (muguan.includes(state.subjectId) && (state.examSongName || "").search(/[^\u0000-\u00FF](1-2|1-3|1-6)/gi) > -1) { return "music"; } if (tongguan.includes(state.subjectId) && (state.examSongName || "").search(/[^\u0000-\u00FF](1-1-1|1-1-2|1-5|1-6)/gi) > -1) { return "music"; } if ([23, 113, 121].includes(state.subjectId)) { return "music"; } return "background"; }; const browserInfo = browser(); /** 是否是节奏练习 */ const isRhythmicExercises = () => { const examSongName = state.examSongName || ""; return examSongName.indexOf("节奏练习") > -1; }; /** 获取评测标准 */ const getEvaluationCriteria = () => { let criteria: TCriteria = "frequency"; // 声部打击乐 if ([23, 113, 121].includes(state.subjectId)) { criteria = "amplitude"; } else if (isRhythmicExercises()) { // 分类为节奏练习 criteria = "decibels"; } return criteria; }; /** 生成评测曲谱数据 */ const formatTimes = () => { let ListenMode = false; let dontEvaluatingMode = false; let skip = false; const datas = []; for (let index = 0; index < state.times.length; index++) { const item = state.times[index]; const note = getNoteByMeasuresSlursStart(item); const rate = state.speed / state.originSpeed; const difftime = item.difftime; const start = difftime + (item.sourceRelativeTime || item.relativeTime); const end = difftime + (item.sourceRelaEndtime || item.relaEndtime); const isStaccato = note.noteElement.voiceEntry.isStaccato(); const noteRate = isStaccato ? 0.5 : 1; if (note.formatLyricsEntries.contains("Play") || note.formatLyricsEntries.contains("Play...")) { ListenMode = false; } if (note.formatLyricsEntries.contains("Listen")) { ListenMode = true; } if (note.formatLyricsEntries.contains("纯律结束")) { dontEvaluatingMode = false; } if (note.formatLyricsEntries.contains("纯律")) { dontEvaluatingMode = true; } const nextNote = state.times[index + 1]; // console.log("noteinfo", note.noteElement.isRestFlag && !!note.stave && !!nextNote) if (skip && (note.stave || !item.noteElement.isRestFlag || (nextNote && !nextNote.noteElement.isRestFlag))) { skip = false; } if (note.noteElement.isRestFlag && !!note.stave && !!nextNote && nextNote.noteElement.isRestFlag) { skip = true; } // console.log(note.measureOpenIndex, item.measureOpenIndex, note); // console.log("skip", skip) const data = { timeStamp: (start * 1000) / rate, duration: ((end * 1000) / rate - (start * 1000) / rate) * noteRate, frequency: item.frequency, nextFrequency: item.nextFrequency, prevFrequency: item.prevFrequency, // 重复的情况index会自然累加,render的index是谱面渲染的index measureIndex: note.measureOpenIndex, measureRenderIndex: item.measureListIndex, dontEvaluating: ListenMode || dontEvaluatingMode, musicalNotesIndex: item.i, denominator: note.noteElement?.Length.denominator, }; datas.push(data); } return datas; }; /** 连接websocket */ const handleConnect = async () => { const behaviorId = localStorage.getItem("behaviorId") || undefined; const rate = state.speed / state.originSpeed; const content = { musicXmlInfos: formatTimes(), id: state.examSongId, subjectId: state.subjectId, detailId: state.detailId, examSongId: state.examSongId, xmlUrl: state.xmlUrl, partIndex: state.partIndex, behaviorId, tenantId: storeData.user.tenantId, platform: browserInfo.ios ? "IOS" : browserInfo.android ? "ANDROID" : "WEB", clientId: storeData.platformType === "STUDENT" ? "student" : storeData.platformType === "TEACHER" ? "teacher" : "education", speed: state.speed, heardLevel: state.setting.evaluationDifficulty, beatLength: Math.round((state.fixtime * 1000) / rate), campId: sessionStorage.getItem("campId") || "", evaluationCriteria: getEvaluationCriteria(), }; const result = await connectWebsocket(content); state.playSource = getMusicMode(); }; /** 评测结果按钮处理 */ const handleEvaluatResult = (type: "practise" | "tryagain" | "look" | "share" | "update") => { if (type === "update") { // 上传云端 evaluatModel.evaluatUpdateAudio = true; return; } else if (type === "share") { // 分享 evaluatModel.shareMode = true; return; } else if (type === "look") { // 跳转 handleViewReport(); return; } else if (type === "practise") { // 去练习 handleStartEvaluat(); } else if (type === "tryagain") { // 再来一次 handleStartBegin(); } evaluatingData.resulstMode = false; }; /** 上传音视频 */ const hanldeUpdateVideoAndAudio = async (update = false) => { if (!update) { evaluatModel.evaluatUpdateAudio = false; return; } let res = null; if (evaluatModel.isSaveVideo) { res = await api_videoUpdate(); } api_proxyServiceMessage({ header: { commond: "videoUpload", status: 200, type: "SOUND_COMPARE", }, body: { filePath: res?.content?.filePath, recordId: res?.recordId, }, }); Snackbar.success("上传成功"); evaluatModel.evaluatUpdateAudio = false; }; onMounted(() => { handlePerformDetection(); }); watch( () => evaluatingData.checkEnd, () => { if (evaluatingData.checkEnd) { console.log("检测结束,连接websocket"); handleConnect(); } } ); watch( () => evaluatingData.startBegin, () => { if (evaluatingData.startBegin) { evaluatModel.tips = false; } } ); return () => (
{evaluatingData.websocketState && ( <> {!evaluatingData.startBegin && (
开始演奏
)} {evaluatingData.startBegin && (
handleEndBegin(false)}> 结束演奏
)} )} {evaluatModel.tips && ( <>
{ evaluatModel.tips = false; }} > 请在周围安静的环境下演奏,减少杂音 请选择稳定、良好的网络环境,避免信号中断 演奏前请调试好乐器,保证最佳演奏状态 演奏时请佩戴耳机,评测收音更精准
)} { evaluatingData.earphoneMode = false; handlePerformDetection(); }} /> { evaluatingData.soundEffectMode = false; if (value) { state.setting.soundEffect = false; } handleEndSoundCheck(); handlePerformDetection(); }} /> (evaluatModel.shareMode = false)} />
); }, });