|
@@ -3,7 +3,7 @@ import { connectWebsocket, evaluatingData, handleEndBegin, handleStartBegin, han
|
|
import Earphone from "./earphone";
|
|
import Earphone from "./earphone";
|
|
import styles from "./index.module.less";
|
|
import styles from "./index.module.less";
|
|
import SoundEffect from "./sound-effect";
|
|
import SoundEffect from "./sound-effect";
|
|
-import state from "/src/state";
|
|
|
|
|
|
+import state, { handleRessetState } from "/src/state";
|
|
import { storeData } from "/src/store";
|
|
import { storeData } from "/src/store";
|
|
import { browser } from "/src/utils";
|
|
import { browser } from "/src/utils";
|
|
import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic";
|
|
import { getNoteByMeasuresSlursStart } from "/src/helpers/formateMusic";
|
|
@@ -19,20 +19,42 @@ import iconTastBg from "./icons/task-bg.svg";
|
|
import iconEvaluat from "./icons/evaluating.json";
|
|
import iconEvaluat from "./icons/evaluating.json";
|
|
import { api_musicPracticeRecordVideoUpload } from "../api";
|
|
import { api_musicPracticeRecordVideoUpload } from "../api";
|
|
import DelayCheck from "./delay-check";
|
|
import DelayCheck from "./delay-check";
|
|
|
|
+import { headTopData } from "../header-top/index";
|
|
|
|
+import { getQuery } from "/src/utils/queryString";
|
|
|
|
|
|
// frequency 频率, amplitude 振幅, decibels 分贝
|
|
// frequency 频率, amplitude 振幅, decibels 分贝
|
|
type TCriteria = "frequency" | "amplitude" | "decibels";
|
|
type TCriteria = "frequency" | "amplitude" | "decibels";
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * 节拍器时长
|
|
|
|
+ * 评测模式时,应该传节拍器时长
|
|
|
|
+ * 阶段评测时,判断是否从第一小节开始,并且曲子本身含有节拍器,需要传节拍器时长,否则传0
|
|
|
|
+ */
|
|
|
|
+let actualBeatLength = 0
|
|
|
|
+let calculateInfo: any = {}
|
|
|
|
+
|
|
export default defineComponent({
|
|
export default defineComponent({
|
|
name: "evaluat-model",
|
|
name: "evaluat-model",
|
|
setup() {
|
|
setup() {
|
|
|
|
+ const query = getQuery();
|
|
const evaluatModel = reactive({
|
|
const evaluatModel = reactive({
|
|
tips: true,
|
|
tips: true,
|
|
evaluatUpdateAudio: false,
|
|
evaluatUpdateAudio: false,
|
|
isSaveVideo: state.setting.camera && state.setting.saveToAlbum,
|
|
isSaveVideo: state.setting.camera && state.setting.saveToAlbum,
|
|
shareMode: false,
|
|
shareMode: false,
|
|
});
|
|
});
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * 检测返回
|
|
|
|
+ */
|
|
|
|
+ const handleDelayBack = () => {
|
|
|
|
+ if (query.workRecord) {
|
|
|
|
+ evaluatingData.soundEffectMode = false;
|
|
|
|
+ } else {
|
|
|
|
+ evaluatingData.soundEffectMode = false;
|
|
|
|
+ handleRessetState();
|
|
|
|
+ headTopData.modeType = "init";
|
|
|
|
+ }
|
|
|
|
+ }
|
|
/**
|
|
/**
|
|
* 执行检测
|
|
* 执行检测
|
|
*/
|
|
*/
|
|
@@ -92,19 +114,56 @@ export default defineComponent({
|
|
|
|
|
|
/** 生成评测曲谱数据 */
|
|
/** 生成评测曲谱数据 */
|
|
const formatTimes = () => {
|
|
const formatTimes = () => {
|
|
|
|
+ let starTime = 0
|
|
let ListenMode = false;
|
|
let ListenMode = false;
|
|
let dontEvaluatingMode = false;
|
|
let dontEvaluatingMode = false;
|
|
let skip = false;
|
|
let skip = false;
|
|
const datas = [];
|
|
const datas = [];
|
|
- for (let index = 0; index < state.times.length; index++) {
|
|
|
|
- const item = state.times[index];
|
|
|
|
|
|
+ let selectTimes = state.times
|
|
|
|
+ let unitTestIdx = 0
|
|
|
|
+ let preTime = 0
|
|
|
|
+ let preTimes = []
|
|
|
|
+ // 系统节拍器时长
|
|
|
|
+ actualBeatLength = Math.round(state.times[0].fixtime * 1000 / 1)
|
|
|
|
+ // 如果是阶段评测,选取该阶段的times
|
|
|
|
+ if (state.isSelectMeasureMode && state.section.length) {
|
|
|
|
+ const startIndex = state.times.findIndex(
|
|
|
|
+ (n: any) => n.noteId == state.section[0].noteId
|
|
|
|
+ )
|
|
|
|
+ let endIndex = state.times.findIndex(
|
|
|
|
+ (n: any) => n.noteId == state.section[1].noteId
|
|
|
|
+ )
|
|
|
|
+ endIndex = endIndex < state.section[1].i ? state.section[1].i : endIndex
|
|
|
|
+ if (startIndex > 1) {
|
|
|
|
+ // firstNoteTime应该取预备小节的第一个音符的开始播放的时间
|
|
|
|
+ const idx = startIndex - 1 - (state.times[startIndex-1].si)
|
|
|
|
+ preTime = state.times[idx] ? state.times[idx].time * 1000 : 0
|
|
|
|
+ }
|
|
|
|
+ actualBeatLength = startIndex == 0 && !state.needTick ? actualBeatLength : 0
|
|
|
|
+ selectTimes = state.times.filter((n: any, index: number) => {
|
|
|
|
+ return index >= startIndex && index <= endIndex
|
|
|
|
+ })
|
|
|
|
+ preTimes = state.times.filter((n: any, index: number) => {
|
|
|
|
+ return index < startIndex
|
|
|
|
+ })
|
|
|
|
+ unitTestIdx = startIndex
|
|
|
|
+ starTime = selectTimes[0].sourceRelativeTime || selectTimes[0].relativeTime
|
|
|
|
+ }
|
|
|
|
+ // 阶段评测beatLength需要加上预备小节的持续时长
|
|
|
|
+ actualBeatLength = preTimes.length ? actualBeatLength + preTimes[preTimes.length - 1].relaMeasureLength * 1000 : actualBeatLength
|
|
|
|
+ let firstNoteTime = unitTestIdx > 1 ? preTime : 0
|
|
|
|
+ let measureIndex = -1
|
|
|
|
+ let recordMeasure = -1
|
|
|
|
+
|
|
|
|
+ for (let index = 0; index < selectTimes.length; index++) {
|
|
|
|
+ const item = selectTimes[index];
|
|
const note = getNoteByMeasuresSlursStart(item);
|
|
const note = getNoteByMeasuresSlursStart(item);
|
|
// #8701 bug: 评测模式,是以曲谱本身的速度进行评测,所以rate取1,不需要转换
|
|
// #8701 bug: 评测模式,是以曲谱本身的速度进行评测,所以rate取1,不需要转换
|
|
// const rate = state.speed / state.originSpeed;
|
|
// const rate = state.speed / state.originSpeed;
|
|
const rate = 1;
|
|
const rate = 1;
|
|
const difftime = item.difftime;
|
|
const difftime = item.difftime;
|
|
- const start = difftime + (item.sourceRelativeTime || item.relativeTime);
|
|
|
|
- const end = difftime + (item.sourceRelaEndtime || item.relaEndtime);
|
|
|
|
|
|
+ const start = difftime + (item.sourceRelativeTime || item.relativeTime) - starTime;
|
|
|
|
+ const end = difftime + (item.sourceRelaEndtime || item.relaEndtime) - starTime;
|
|
const isStaccato = note.noteElement.voiceEntry.isStaccato();
|
|
const isStaccato = note.noteElement.voiceEntry.isStaccato();
|
|
const noteRate = isStaccato ? 0.5 : 1;
|
|
const noteRate = isStaccato ? 0.5 : 1;
|
|
if (note.formatLyricsEntries.contains("Play") || note.formatLyricsEntries.contains("Play...")) {
|
|
if (note.formatLyricsEntries.contains("Play") || note.formatLyricsEntries.contains("Play...")) {
|
|
@@ -119,7 +178,7 @@ export default defineComponent({
|
|
if (note.formatLyricsEntries.contains("纯律")) {
|
|
if (note.formatLyricsEntries.contains("纯律")) {
|
|
dontEvaluatingMode = true;
|
|
dontEvaluatingMode = true;
|
|
}
|
|
}
|
|
- const nextNote = state.times[index + 1];
|
|
|
|
|
|
+ const nextNote = selectTimes[index + 1];
|
|
// console.log("noteinfo", note.noteElement.isRestFlag && !!note.stave && !!nextNote)
|
|
// console.log("noteinfo", note.noteElement.isRestFlag && !!note.stave && !!nextNote)
|
|
if (skip && (note.stave || !item.noteElement.isRestFlag || (nextNote && !nextNote.noteElement.isRestFlag))) {
|
|
if (skip && (note.stave || !item.noteElement.isRestFlag || (nextNote && !nextNote.noteElement.isRestFlag))) {
|
|
skip = false;
|
|
skip = false;
|
|
@@ -130,6 +189,10 @@ export default defineComponent({
|
|
// console.log(note.measureOpenIndex, item.measureOpenIndex, note);
|
|
// console.log(note.measureOpenIndex, item.measureOpenIndex, note);
|
|
// console.log("skip", skip)
|
|
// console.log("skip", skip)
|
|
// console.log(end,start,rate,noteRate, '评测')
|
|
// console.log(end,start,rate,noteRate, '评测')
|
|
|
|
+ if (note.measureOpenIndex != recordMeasure) {
|
|
|
|
+ measureIndex++
|
|
|
|
+ recordMeasure = note.measureOpenIndex
|
|
|
|
+ }
|
|
const data = {
|
|
const data = {
|
|
timeStamp: (start * 1000) / rate,
|
|
timeStamp: (start * 1000) / rate,
|
|
duration: ((end * 1000) / rate - (start * 1000) / rate) * noteRate,
|
|
duration: ((end * 1000) / rate - (start * 1000) / rate) * noteRate,
|
|
@@ -137,23 +200,27 @@ export default defineComponent({
|
|
nextFrequency: item.nextFrequency,
|
|
nextFrequency: item.nextFrequency,
|
|
prevFrequency: item.prevFrequency,
|
|
prevFrequency: item.prevFrequency,
|
|
// 重复的情况index会自然累加,render的index是谱面渲染的index
|
|
// 重复的情况index会自然累加,render的index是谱面渲染的index
|
|
- measureIndex: note.measureOpenIndex,
|
|
|
|
|
|
+ measureIndex: measureIndex,
|
|
measureRenderIndex: item.measureListIndex,
|
|
measureRenderIndex: item.measureListIndex,
|
|
dontEvaluating: ListenMode || dontEvaluatingMode || item.skipMode,
|
|
dontEvaluating: ListenMode || dontEvaluatingMode || item.skipMode,
|
|
- musicalNotesIndex: item.i,
|
|
|
|
|
|
+ musicalNotesIndex: index,
|
|
denominator: note.noteElement?.Length.denominator,
|
|
denominator: note.noteElement?.Length.denominator,
|
|
isOrnament: !!note?.voiceEntry?.ornamentContainer,
|
|
isOrnament: !!note?.voiceEntry?.ornamentContainer,
|
|
};
|
|
};
|
|
datas.push(data);
|
|
datas.push(data);
|
|
}
|
|
}
|
|
- return datas;
|
|
|
|
|
|
+ return {
|
|
|
|
+ datas,
|
|
|
|
+ firstNoteTime
|
|
|
|
+ }
|
|
};
|
|
};
|
|
/** 连接websocket */
|
|
/** 连接websocket */
|
|
const handleConnect = async () => {
|
|
const handleConnect = async () => {
|
|
const behaviorId = localStorage.getItem("behaviorId") || undefined;
|
|
const behaviorId = localStorage.getItem("behaviorId") || undefined;
|
|
const rate = state.speed / state.originSpeed;
|
|
const rate = state.speed / state.originSpeed;
|
|
|
|
+ calculateInfo = formatTimes()
|
|
const content = {
|
|
const content = {
|
|
- musicXmlInfos: formatTimes(),
|
|
|
|
|
|
+ musicXmlInfos: calculateInfo.datas,
|
|
subjectId: state.subjectId,
|
|
subjectId: state.subjectId,
|
|
detailId: state.detailId,
|
|
detailId: state.detailId,
|
|
examSongId: state.examSongId,
|
|
examSongId: state.examSongId,
|
|
@@ -166,7 +233,8 @@ export default defineComponent({
|
|
reactionTimeMs: state.setting.reactionTimeMs,
|
|
reactionTimeMs: state.setting.reactionTimeMs,
|
|
speed: state.speed,
|
|
speed: state.speed,
|
|
heardLevel: state.setting.evaluationDifficulty,
|
|
heardLevel: state.setting.evaluationDifficulty,
|
|
- beatLength: Math.round((state.fixtime * 1000) / rate),
|
|
|
|
|
|
+ // beatLength: Math.round((state.fixtime * 1000) / rate),
|
|
|
|
+ beatLength: actualBeatLength,
|
|
evaluationCriteria: getEvaluationCriteria(),
|
|
evaluationCriteria: getEvaluationCriteria(),
|
|
};
|
|
};
|
|
await connectWebsocket(content);
|
|
await connectWebsocket(content);
|
|
@@ -199,7 +267,7 @@ export default defineComponent({
|
|
handleStartEvaluat();
|
|
handleStartEvaluat();
|
|
} else if (type === "tryagain") {
|
|
} else if (type === "tryagain") {
|
|
// 再来一次
|
|
// 再来一次
|
|
- handleStartBegin();
|
|
|
|
|
|
+ startBtnHandle()
|
|
}
|
|
}
|
|
evaluatingData.resulstMode = false;
|
|
evaluatingData.resulstMode = false;
|
|
};
|
|
};
|
|
@@ -236,6 +304,10 @@ export default defineComponent({
|
|
showToast("上传成功");
|
|
showToast("上传成功");
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+ const startBtnHandle = () => {
|
|
|
|
+ handleConnect();
|
|
|
|
+ handleStartBegin(calculateInfo.firstNoteTime);
|
|
|
|
+ }
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
evaluatingData.isDisabledPlayMusic = true;
|
|
evaluatingData.isDisabledPlayMusic = true;
|
|
handlePerformDetection();
|
|
handlePerformDetection();
|
|
@@ -244,7 +316,9 @@ export default defineComponent({
|
|
<div>
|
|
<div>
|
|
<Transition name="pop-center">
|
|
<Transition name="pop-center">
|
|
{evaluatingData.websocketState && !evaluatingData.startBegin && evaluatingData.checkEnd && (
|
|
{evaluatingData.websocketState && !evaluatingData.startBegin && evaluatingData.checkEnd && (
|
|
- <div class={styles.startBtn} onClick={handleStartBegin}>
|
|
|
|
|
|
+ <div class={styles.startBtn} onClick={() => {
|
|
|
|
+ startBtnHandle()
|
|
|
|
+ }}>
|
|
<img src={iconEvaluat.evaluatingStart} />
|
|
<img src={iconEvaluat.evaluatingStart} />
|
|
</div>
|
|
</div>
|
|
)}
|
|
)}
|
|
@@ -275,6 +349,7 @@ export default defineComponent({
|
|
evaluatingData.soundEffectMode = false;
|
|
evaluatingData.soundEffectMode = false;
|
|
handlePerformDetection();
|
|
handlePerformDetection();
|
|
}}
|
|
}}
|
|
|
|
+ onBack={() => handleDelayBack()}
|
|
/>
|
|
/>
|
|
)}
|
|
)}
|
|
|
|
|