|
@@ -3,7 +3,7 @@ import { reactive } from "vue";
|
|
|
import { OpenSheetMusicDisplay } from "../osmd-extended/src";
|
|
|
import { metronomeData } from "./helpers/metronome";
|
|
|
import { GradualNote, GradualTimes, GradualVersion } from "./type";
|
|
|
-import { handleEndBegin, handleStartEvaluat, sendEvaluatingOffsetTime } from "./view/evaluating";
|
|
|
+import { handleEndEvaluat, handleStartEvaluat, sendEvaluatingOffsetTime } from "./view/evaluating";
|
|
|
import { IFingering } from "src/view/fingering/fingering-config";
|
|
|
import { handleStartTick } from "./view/tick";
|
|
|
import { audioListStart, getAudioCurrentTime, getAudioDuration, setAudioCurrentTime, setAudioPlaybackRate } from "./view/audio-list";
|
|
@@ -11,12 +11,12 @@ import { toggleFollow } from "./view/follow-practice";
|
|
|
|
|
|
/** 入门 | 进阶 | 大师 */
|
|
|
export type IDifficulty = "BEGINNER" | "ADVANCED" | "PERFORMER";
|
|
|
-export type IMusicRenderType = 'staff' | 'firstTone' | 'fixedTone'
|
|
|
-export const musicscoresettingKey = "musicscoresetting"
|
|
|
+export type IMusicRenderType = "staff" | "firstTone" | "fixedTone";
|
|
|
+export const musicscoresettingKey = "musicscoresetting";
|
|
|
|
|
|
const state = reactive({
|
|
|
appName: "" as "GYM" | "COLEXIU",
|
|
|
- musicRenderType: 'staff' as IMusicRenderType,
|
|
|
+ musicRenderType: "staff" as IMusicRenderType,
|
|
|
/**曲谱是否渲染完成 */
|
|
|
musicRendered: false,
|
|
|
/** 当前曲谱数据ID, 和曲谱ID不一致 */
|
|
@@ -120,7 +120,7 @@ const state = reactive({
|
|
|
/** 开启伴奏 */
|
|
|
enableAccompaniment: false,
|
|
|
/** 反应时间 */
|
|
|
- reactionTimeMs: 0
|
|
|
+ reactionTimeMs: 0,
|
|
|
},
|
|
|
/** 节拍器的时间 */
|
|
|
fixtime: 0,
|
|
@@ -145,7 +145,7 @@ const state = reactive({
|
|
|
/** 缩放 */
|
|
|
zoom: 0.8,
|
|
|
/** 练习,评测是否是选段模式 */
|
|
|
- isSelectMeasureMode: false
|
|
|
+ isSelectMeasureMode: false,
|
|
|
});
|
|
|
/** 音频加载完成 */
|
|
|
export const onLoadedmetadata = (evt: Event) => {
|
|
@@ -168,34 +168,42 @@ const setStep = () => {
|
|
|
};
|
|
|
/** 开始播放 */
|
|
|
export const onPlay = () => {
|
|
|
- state.playEnd = false
|
|
|
+ state.playEnd = false;
|
|
|
setStep();
|
|
|
if (state.modeType === "evaluating") {
|
|
|
let currentTime = getAudioCurrentTime();
|
|
|
// 选段评测模式
|
|
|
if (state.isSelectMeasureMode) {
|
|
|
- currentTime = currentTime - state.section[0].time
|
|
|
- }
|
|
|
+ currentTime = currentTime - state.section[0].time;
|
|
|
+ }
|
|
|
sendEvaluatingOffsetTime(currentTime);
|
|
|
}
|
|
|
};
|
|
|
/** 播放中事件 */
|
|
|
export const onTimeupdate = (evt: Event) => {};
|
|
|
+
|
|
|
+/** 播放模式结束自动重播 */
|
|
|
+const autoResetPlay = () => {
|
|
|
+ // 没有开启自动重播, 不是练习模式
|
|
|
+ if (!state.setting.repeatAutoPlay || state.modeType !== "practise") return;
|
|
|
+ skipNotePlay(0, true);
|
|
|
+ scrollViewNote();
|
|
|
+ setTimeout(() => {
|
|
|
+ togglePlay("play");
|
|
|
+ }, 1000);
|
|
|
+};
|
|
|
+
|
|
|
/** 播放完成事件 */
|
|
|
export const onEnded = () => {
|
|
|
- state.playEnd = true
|
|
|
- handleStopPlay();
|
|
|
- if (state.modeType === "evaluating") {
|
|
|
- handleEndBegin(true);
|
|
|
- }
|
|
|
- // 重复自动播放如果为开启,自动开始播放, 且是练习模式
|
|
|
- if (state.setting.repeatAutoPlay && state.modeType === "practise") {
|
|
|
- skipNotePlay(0, true)
|
|
|
- scrollViewNote();
|
|
|
- setTimeout(() => {
|
|
|
- togglePlay("play");
|
|
|
- }, 1000);
|
|
|
- }
|
|
|
+ // 修改状态为结束
|
|
|
+ state.playEnd = true;
|
|
|
+ state.playState = "paused";
|
|
|
+ // 结束播放
|
|
|
+ audioListStart(state.playState);
|
|
|
+ // 调用结束评测
|
|
|
+ handleEndEvaluat();
|
|
|
+ // 调用自动重复播放
|
|
|
+ autoResetPlay();
|
|
|
};
|
|
|
|
|
|
/**
|
|
@@ -205,27 +213,25 @@ const handlePlaying = (_item?: any) => {
|
|
|
const currentTime = getAudioCurrentTime();
|
|
|
const duration = getAudioDuration();
|
|
|
state.playProgress = (currentTime / duration) * 100;
|
|
|
- const item = _item ? _item : getNote(currentTime);
|
|
|
+ let item = _item ? _item : getNote(currentTime);
|
|
|
|
|
|
if (item) {
|
|
|
// 选段状态下
|
|
|
if (state.sectionStatus && state.section.length === 2) {
|
|
|
- let startItemIndex = state.section[0].i;
|
|
|
- let startXmlIndex = state.section[0].MeasureNumberXML;
|
|
|
- // 开启预备拍
|
|
|
- if (state.sectionFirst) {
|
|
|
- startItemIndex = state.sectionFirst.i;
|
|
|
- startXmlIndex = state.sectionFirst.MeasureNumberXML;
|
|
|
- }
|
|
|
- if (item.MeasureNumberXML < startXmlIndex || item.MeasureNumberXML > state.section[1].MeasureNumberXML) {
|
|
|
- console.log('选段播放结束')
|
|
|
+ // 如果开启了预备拍
|
|
|
+ const selectStartItem = state.sectionFirst ? state.sectionFirst : state.section[0];
|
|
|
+ const selectEndItem = state.section[1];
|
|
|
+
|
|
|
+ if (currentTime < selectStartItem.time || currentTime > selectEndItem.endtime) {
|
|
|
+ console.log("选段播放结束");
|
|
|
// 如果为选段评测模式
|
|
|
- if (state.isSelectMeasureMode) {
|
|
|
- onEnded()
|
|
|
+ if (state.modeType === 'evaluating' && state.isSelectMeasureMode) {
|
|
|
+ onEnded();
|
|
|
+ return;
|
|
|
}
|
|
|
- skipNotePlay(startItemIndex);
|
|
|
+ item = selectStartItem;
|
|
|
+ setAudioCurrentTime(selectStartItem.time);
|
|
|
|
|
|
- return;
|
|
|
}
|
|
|
}
|
|
|
gotoNext(item);
|
|
@@ -242,7 +248,7 @@ export const skipNotePlay = (itemIndex: number, isStart = false) => {
|
|
|
}
|
|
|
// console.log("🚀 ~ itemTime:", itemTime);
|
|
|
if (item) {
|
|
|
- setAudioCurrentTime(itemTime)
|
|
|
+ setAudioCurrentTime(itemTime);
|
|
|
handlePlaying(item);
|
|
|
}
|
|
|
};
|
|
@@ -260,8 +266,8 @@ export const togglePlay = async (playState?: "play" | "paused") => {
|
|
|
// 节拍器返回false, 取消播放
|
|
|
if (!tickend) {
|
|
|
state.playState = "paused";
|
|
|
- return false
|
|
|
- };
|
|
|
+ return false;
|
|
|
+ }
|
|
|
}
|
|
|
// 如果选段没有结束, 直接开始播放,清空选段状态
|
|
|
if (state.playState == "play") {
|
|
@@ -270,7 +276,7 @@ export const togglePlay = async (playState?: "play" | "paused") => {
|
|
|
}
|
|
|
}
|
|
|
audioListStart(state.playState);
|
|
|
- return true
|
|
|
+ return true;
|
|
|
};
|
|
|
/** 结束播放 */
|
|
|
export const handleStopPlay = () => {
|
|
@@ -278,6 +284,16 @@ export const handleStopPlay = () => {
|
|
|
audioListStart(state.playState);
|
|
|
};
|
|
|
|
|
|
+/** 重置播放为开始 */
|
|
|
+export const resetPlaybackToStart = () => {
|
|
|
+ // 如果为选段状态
|
|
|
+ if (state.sectionStatus && state.section.length === 2) {
|
|
|
+ state.section = formateSelectMearure(state.section);
|
|
|
+ return
|
|
|
+ }
|
|
|
+ skipNotePlay(0, true);
|
|
|
+}
|
|
|
+
|
|
|
/** 跳转到指定音符 */
|
|
|
export const gotoCustomNote = (index: number) => {
|
|
|
try {
|
|
@@ -314,8 +330,8 @@ export const gotoNext = (note: any) => {
|
|
|
export const getNote = (currentTime: number) => {
|
|
|
const times = state.times;
|
|
|
const len = state.times.length;
|
|
|
- /** 播放超过了最后一个音符的时间,直接结束 */
|
|
|
- if (currentTime > times[len - 1].endtime + 1) {
|
|
|
+ /** 播放超过了最后一个音符的时间,直接结束, 2秒误差 */
|
|
|
+ if (currentTime > times[len - 1].endtime + 2) {
|
|
|
onEnded();
|
|
|
return;
|
|
|
}
|
|
@@ -337,7 +353,7 @@ export const getNote = (currentTime: number) => {
|
|
|
|
|
|
/** 重播 */
|
|
|
export const handleResetPlay = () => {
|
|
|
- skipNotePlay(0, true);
|
|
|
+ resetPlaybackToStart()
|
|
|
};
|
|
|
/** 设置速度 */
|
|
|
export const handleSetSpeed = (speed: number) => {
|
|
@@ -376,21 +392,39 @@ export const handleChangeSection = () => {
|
|
|
});
|
|
|
};
|
|
|
|
|
|
+/** 效验并格式化选段小节 */
|
|
|
+const formateSelectMearure = (_list: any[]): any[] => {
|
|
|
+ if (!_list.length) return [];
|
|
|
+ const list = _list.sort((a, b) => a.time - b.time);
|
|
|
+ const startXml = list[0]?.MeasureNumberXML;
|
|
|
+ const endXml = list.last()?.MeasureNumberXML;
|
|
|
+ const selectStartMeasure = state.times.filter((n: any) => startXml === n.MeasureNumberXML) || [];
|
|
|
+ const selectEndMeasure = state.times.filter((n: any) => endXml === n.MeasureNumberXML) || [];
|
|
|
+ // 没有找到选段小节
|
|
|
+ if (!selectStartMeasure.length || !selectEndMeasure.length) {
|
|
|
+ clearSelection();
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ list[0] = selectStartMeasure[0];
|
|
|
+ list[1] = selectEndMeasure.last();
|
|
|
+ let startItemINdex = list[0].i;
|
|
|
+ // 开启预备拍
|
|
|
+ if (state.isOpenPrepare) {
|
|
|
+ const startXmlIndex = list[0].MeasureNumberXML;
|
|
|
+ state.sectionFirst = state.times.find((n: any) => startXmlIndex - n.MeasureNumberXML === 1);
|
|
|
+ startItemINdex = state.sectionFirst ? state.sectionFirst.i : startItemINdex;
|
|
|
+ }
|
|
|
+ skipNotePlay(startItemINdex);
|
|
|
+ return list;
|
|
|
+};
|
|
|
+
|
|
|
/** 选择选段 */
|
|
|
export const handleSelection = (item: any) => {
|
|
|
if (!state.sectionStatus || state.section.length > 1) return;
|
|
|
if (state.section.length !== 2 && item) {
|
|
|
state.section.push(item);
|
|
|
if (state.section.length === 2) {
|
|
|
- state.section = state.section.sort((a, b) => a.time - b.time);
|
|
|
- let startItemINdex = state.section[0].index;
|
|
|
- // 开启预备拍
|
|
|
- if (state.isOpenPrepare) {
|
|
|
- const startXmlIndex = state.section[0].MeasureNumberXML;
|
|
|
- state.sectionFirst = state.times.find((n: any) => startXmlIndex - n.MeasureNumberXML === 1);
|
|
|
- startItemINdex = state.sectionFirst ? state.sectionFirst.i : startItemINdex;
|
|
|
- }
|
|
|
- skipNotePlay(startItemINdex);
|
|
|
+ state.section = formateSelectMearure(state.section);
|
|
|
state.sectionToast?.close();
|
|
|
state.sectionToast = null;
|
|
|
}
|
|
@@ -399,20 +433,13 @@ export const handleSelection = (item: any) => {
|
|
|
state.sectionToast.message = "请选择结束小节";
|
|
|
}
|
|
|
};
|
|
|
+
|
|
|
/** 直接设置选段 */
|
|
|
export const hanldeDirectSelection = (list: any[]) => {
|
|
|
- if(!Array.isArray(list) || list.length !== 2) return
|
|
|
+ if (!Array.isArray(list) || list.length !== 2) return;
|
|
|
state.sectionStatus = true;
|
|
|
- state.section = list.sort((a, b) => a.time - b.time);
|
|
|
- let startItemINdex = state.section[0].i;
|
|
|
- // 开启预备拍
|
|
|
- if (state.isOpenPrepare) {
|
|
|
- const startXmlIndex = state.section[0].MeasureNumberXML;
|
|
|
- state.sectionFirst = state.times.find((n: any) => startXmlIndex - n.MeasureNumberXML === 1);
|
|
|
- startItemINdex = state.sectionFirst ? state.sectionFirst.i : startItemINdex;
|
|
|
- }
|
|
|
- skipNotePlay(startItemINdex);
|
|
|
-}
|
|
|
+ state.section = formateSelectMearure(list);
|
|
|
+};
|
|
|
let offsetTop = 0;
|
|
|
/**
|
|
|
* 窗口内滚动到音符的区域
|
|
@@ -448,8 +475,8 @@ export const handleRessetState = () => {
|
|
|
handleStartEvaluat();
|
|
|
} else if (state.modeType === "practise") {
|
|
|
togglePlay("paused");
|
|
|
- } else if (state.modeType === 'follow') {
|
|
|
- toggleFollow(false)
|
|
|
+ } else if (state.modeType === "follow") {
|
|
|
+ toggleFollow(false);
|
|
|
}
|
|
|
};
|
|
|
export default state;
|