|
@@ -505,6 +505,10 @@ const state = reactive({
|
|
|
loadingText: '音频资源加载中,请稍后…',
|
|
|
/** 是否是简单的单行谱模式页面 */
|
|
|
isSimplePage: false,
|
|
|
+ /** xml的速度和后台设置的速度,计算出的基础音频播放倍率 */
|
|
|
+ originAudioPlayRate: 1,
|
|
|
+ /** 开始播放时,记录的mp3播放倍率,用户当前设置的速度/当前小节的速度 */
|
|
|
+ basePlayRate: 1,
|
|
|
});
|
|
|
const browserInfo = browser();
|
|
|
let offset_duration = 0;
|
|
@@ -580,6 +584,35 @@ export const onEnded = () => {
|
|
|
autoResetPlay();
|
|
|
};
|
|
|
|
|
|
+// 根据当前小节动态设置,右上角展示的速度
|
|
|
+const dynamicShowPlaySpeed = (index: number) => {
|
|
|
+ const item: any = state.times[index];
|
|
|
+ if (item && item.measureSpeed ) {
|
|
|
+ state.playIngSpeed = Math.floor(state.basePlayRate * item.measureSpeed)
|
|
|
+ state.speed = state.playIngSpeed
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 开始播放时,计算mp3的播放倍率
|
|
|
+export const initSetPlayRate = () => {
|
|
|
+ const item: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[state.activeNoteIndex];
|
|
|
+ if (item && item.measureSpeed) {
|
|
|
+ const ratio = state.speed / item.measureSpeed
|
|
|
+ // state.audiosInstance?.setSpeed(ratio)
|
|
|
+ state.basePlayRate = ratio || 1;
|
|
|
+ console.log('播放倍率',state.basePlayRate)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 重置播放倍率
|
|
|
+export const resetBaseRate = () => {
|
|
|
+ const currentItem: any = state.times[0];
|
|
|
+ const currentSpeed = currentItem?.measureSpeed ? currentItem.measureSpeed : state.originSpeed;
|
|
|
+ state.speed = currentSpeed
|
|
|
+ //state.activeNoteIndex = 0
|
|
|
+ state.basePlayRate = 1;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* 播放一直触发的事件
|
|
|
*/
|
|
@@ -588,17 +621,8 @@ const handlePlaying = () => {
|
|
|
const duration = getAudioDuration();
|
|
|
state.playProgress = (currentTime / duration) * 100;
|
|
|
let item = getNote(currentTime);
|
|
|
- // console.log(11111,currentTime,duration,state.playSource, item)
|
|
|
- // console.log(item?.i,item?.noteId,item?.measureSpeed,'播放')
|
|
|
- // 练习模式下,实时刷新小节速度
|
|
|
- if (item && state.modeType === "practise" && state.playState === "play" && item.measureSpeed && item.measureSpeed !== state.playIngSpeed) {
|
|
|
- const ratio = state.speed / state.originSpeed
|
|
|
- state.playIngSpeed = Math.ceil(ratio * item.measureSpeed) || state.speed
|
|
|
- } else if (state.modeType === "practise" && state.playState === "play" && item && !item.measureSpeed) {
|
|
|
- state.playIngSpeed = state.speed
|
|
|
- }
|
|
|
- state.playIngSpeed = state.playIngSpeed || state.speed;
|
|
|
if (item) {
|
|
|
+ dynamicShowPlaySpeed(item.i);
|
|
|
// 选段状态下
|
|
|
if (state.sectionStatus && state.section.length === 2) {
|
|
|
// 如果开启了预备拍
|
|
@@ -641,6 +665,8 @@ const handlePlaying = () => {
|
|
|
// #8698 bug修复
|
|
|
if (state.modeType === "practise" && state.sectionStatus) {
|
|
|
onEnded();
|
|
|
+ // state.activeNoteIndex = state.sectionFirst ? state.sectionFirst.i : state.section[0].i
|
|
|
+ // dynamicShowPlaySpeed(state.activeNoteIndex)
|
|
|
resetPlaybackToStart();
|
|
|
return;
|
|
|
}
|
|
@@ -670,6 +696,10 @@ export const skipNotePlay = async (itemIndex: number, isStart = false) => {
|
|
|
itemTime = 0;
|
|
|
}
|
|
|
if (item) {
|
|
|
+ // 非选段模式,点击音符,动态设置右下角的速度
|
|
|
+ if (item.measureSpeed && state.section.length < 2) {
|
|
|
+ state.speed = Math.floor(state.basePlayRate * item.measureSpeed)
|
|
|
+ }
|
|
|
setAudioCurrentTime(itemTime, itemIndex);
|
|
|
// 一行谱,点击音符,或者播放完成,需要跳转音符位置
|
|
|
gotoNext(item, true);
|
|
@@ -753,6 +783,7 @@ export const togglePlay = async (playState?: "play" | "paused", sourceType?: str
|
|
|
clearSelection();
|
|
|
}
|
|
|
}
|
|
|
+ initSetPlayRate();
|
|
|
audioListStart(state.playState);
|
|
|
return true;
|
|
|
};
|
|
@@ -995,14 +1026,20 @@ export const handleResetPlay = () => {
|
|
|
if (state.isAppPlay) {
|
|
|
audioData.progress = 0
|
|
|
}
|
|
|
+ resetBaseRate();
|
|
|
resetPlaybackToStart();
|
|
|
// 如果是暂停, 直接播放
|
|
|
togglePlay("play");
|
|
|
};
|
|
|
/** 设置速度 */
|
|
|
export const handleSetSpeed = (speed: number) => {
|
|
|
- setStorageSpeed(state.examSongId, speed);
|
|
|
+ // setStorageSpeed(state.examSongId, speed);
|
|
|
state.speed = speed;
|
|
|
+ // 当前的音符
|
|
|
+ const currentItem: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[state.activeNoteIndex];
|
|
|
+ state.basePlayRate = currentItem?.measureSpeed ? state.speed / currentItem.measureSpeed : state.speed / state.originSpeed;
|
|
|
+ const actualRate = state.originAudioPlayRate * state.basePlayRate;
|
|
|
+ console.log('速度设置',speed,'小节计算的倍率',state.basePlayRate,'实际播放倍率',actualRate)
|
|
|
};
|
|
|
/** 清除选段状态 */
|
|
|
export const clearSelection = () => {
|
|
@@ -1017,7 +1054,7 @@ export const handleChangeSection = () => {
|
|
|
if (state.sectionStatus) {
|
|
|
togglePlay("paused");
|
|
|
clearSelection();
|
|
|
- skipNotePlay(0, true);
|
|
|
+ //skipNotePlay(0, true); 取消选段的时候 不跳回开头
|
|
|
state.sectionFirst = null;
|
|
|
return;
|
|
|
}
|
|
@@ -1127,7 +1164,7 @@ export const setSection = (start: number, end: number, userSpeed?: number) => {
|
|
|
// 设置小节
|
|
|
hanldeDirectSelection([startNote, endNote]);
|
|
|
|
|
|
- //设置速度
|
|
|
+ // 评测作业,练习作业的场景,需要用老师布置的速度,设置播放速度
|
|
|
if (userSpeed) {
|
|
|
handleSetSpeed(userSpeed);
|
|
|
}
|
|
@@ -1141,6 +1178,11 @@ export const hanldeDirectSelection = (list: any[]) => {
|
|
|
state.sectionStatus = true;
|
|
|
setTimeout(() => {
|
|
|
state.section = formateSelectMearure(list);
|
|
|
+ // 选段完成后,需要根据预报小节的速度,设置右下角显示的速度
|
|
|
+ const currentItem: any = (state.sectionStatus && state.section.length === 2) ? state.sectionFirst || state.section[0] : state.times[state.activeNoteIndex];
|
|
|
+ if (currentItem.measureSpeed && query.workRecord === undefined) {
|
|
|
+ handleSetSpeed(currentItem.measureSpeed);
|
|
|
+ }
|
|
|
console.log('选段小节', state.section)
|
|
|
}, 0);
|
|
|
};
|
|
@@ -1316,6 +1358,10 @@ const setState = (data: any, index: number) => {
|
|
|
state.coverImg = data.musicCover ?? "";
|
|
|
// 单声部多声轨合并展示
|
|
|
state.isCombineRender = data.musicSheetType === "SINGLE" && data.musicSheetSoundList?.length > 1
|
|
|
+ // 如果是simple页面,只显示单轨
|
|
|
+ if (state.isSimplePage) {
|
|
|
+ state.isCombineRender = false;
|
|
|
+ }
|
|
|
setCustom(state.isCombineRender ? data.musicSheetSoundList?.length : 0);
|
|
|
// 解析扩展字段
|
|
|
if (data.extConfigJson) {
|
|
@@ -1350,12 +1396,11 @@ const setState = (data: any, index: number) => {
|
|
|
const track = data.code || data.track;
|
|
|
state.track = track ? track.replace(/ /g, "").toLocaleLowerCase() : "";
|
|
|
// 能否评测,根据当前声轨有无伴奏判断
|
|
|
- // if (state.isAppPlay) {
|
|
|
- // state.enableEvaluation = state.midiUrl ? true : false
|
|
|
- // } else {
|
|
|
- // state.enableEvaluation = state.accompany ? true : false
|
|
|
- // }
|
|
|
- state.enableEvaluation = true
|
|
|
+ if (state.isAppPlay) {
|
|
|
+ state.enableEvaluation = state.midiUrl ? true : false
|
|
|
+ } else {
|
|
|
+ state.enableEvaluation = state.accompany || state.music ? true : false
|
|
|
+ }
|
|
|
state.isConcert = data.musicSheetType === "CONCERT" ? true : false;
|
|
|
// multiTracksSelection 返回为空,默认代表全部分轨
|
|
|
state.canSelectTracks = data.multiTracksSelection === "null" || data.multiTracksSelection === "" || data.multiTracksSelection === null ? [] : data.multiTracksSelection?.split(',');
|
|
@@ -1622,11 +1667,13 @@ watch(
|
|
|
() => state.activeMeasureIndex,
|
|
|
() => {
|
|
|
// 需要减去的合并小节数
|
|
|
- const needReduceMultipleRestNum = getNeedReduceMultipleRestNum(state.activeMeasureIndex)
|
|
|
- const matchMeasureNum = state.activeMeasureIndex - needReduceMultipleRestNum - 1
|
|
|
- console.log('选中的小节', matchMeasureNum, '需要减去的小节', needReduceMultipleRestNum, '当前的小节', state.activeMeasureIndex)
|
|
|
+ // const needReduceMultipleRestNum = getNeedReduceMultipleRestNum(state.activeMeasureIndex)
|
|
|
+ // const matchMeasureNum = state.activeMeasureIndex - needReduceMultipleRestNum - 1
|
|
|
+ // console.log('选中的小节',matchMeasureNum,'需要减去的小节',needReduceMultipleRestNum,'当前的小节',state.activeMeasureIndex)
|
|
|
state.vfmeasures.forEach((item: any, idx: number) => {
|
|
|
- if (idx === matchMeasureNum) {
|
|
|
+ const measureNum = item.getAttribute('data-num') ? Number(item.getAttribute('data-num')) : -1;
|
|
|
+ const nextMeasureNum = state.vfmeasures[idx+1]?.getAttribute('data-num') ? Number(state.vfmeasures[idx+1]?.getAttribute('data-num')) : -1;
|
|
|
+ if (measureNum >= 0 && (measureNum === state.activeMeasureIndex || (measureNum < state.activeMeasureIndex && nextMeasureNum > state.activeMeasureIndex)) ) {
|
|
|
item.querySelector('.vf-custom-bg')?.setAttribute("fill", "#132D4C")
|
|
|
item.querySelector('.vf-custom-bot')?.setAttribute("fill", "#040D1E")
|
|
|
} else {
|
|
@@ -1639,18 +1686,14 @@ watch(
|
|
|
leftMeasureNumberXML = state.section[1].MeasureNumberXML
|
|
|
rightMeasureNumberXML = state.section[0].MeasureNumberXML
|
|
|
}
|
|
|
- const leftVfmeasuresIndex = leftMeasureNumberXML - getNeedReduceMultipleRestNum(leftMeasureNumberXML) - 1
|
|
|
- const rightVfmeasuresIndex = rightMeasureNumberXML - getNeedReduceMultipleRestNum(rightMeasureNumberXML) - 1
|
|
|
- if (idx >= leftVfmeasuresIndex && idx <= rightVfmeasuresIndex) {
|
|
|
+ if(measureNum >= leftMeasureNumberXML && measureNum <= rightMeasureNumberXML){
|
|
|
item.querySelector('.vf-custom-bg')?.setAttribute("fill", "#609FCF")
|
|
|
item.querySelector('.vf-custom-bot')?.setAttribute("fill", "#2B70A5")
|
|
|
}
|
|
|
// 预备小节
|
|
|
- if (state.sectionFirst) {
|
|
|
- const sectionFirstVfmeasuresIndex = state.sectionFirst.MeasureNumberXML - getNeedReduceMultipleRestNum(state.sectionFirst.MeasureNumberXML) - 1
|
|
|
- const sectionFirstDom = state.vfmeasures[sectionFirstVfmeasuresIndex]
|
|
|
- sectionFirstDom?.querySelector('.vf-custom-bg')?.setAttribute("fill", "#71B8BD")
|
|
|
- sectionFirstDom?.querySelector('.vf-custom-bot')?.setAttribute("fill", "#448F9C")
|
|
|
+ if(state.sectionFirst && measureNum === state.sectionFirst.MeasureNumberXML){
|
|
|
+ item?.querySelector('.vf-custom-bg')?.setAttribute("fill", "#71B8BD")
|
|
|
+ item?.querySelector('.vf-custom-bot')?.setAttribute("fill", "#448F9C")
|
|
|
}
|
|
|
} else {
|
|
|
item.querySelector('.vf-custom-bg')?.setAttribute("fill", "#609FCF")
|
|
@@ -1674,32 +1717,30 @@ watch(
|
|
|
leftMeasureNumberXML = state.section[1].MeasureNumberXML
|
|
|
rightMeasureNumberXML = state.section[0].MeasureNumberXML
|
|
|
}
|
|
|
- const leftVfmeasuresIndex = leftMeasureNumberXML - getNeedReduceMultipleRestNum(leftMeasureNumberXML) - 1
|
|
|
- const rightVfmeasuresIndex = rightMeasureNumberXML - getNeedReduceMultipleRestNum(rightMeasureNumberXML) - 1
|
|
|
state.vfmeasures.forEach((item: any, idx: number) => {
|
|
|
+ const measureNum = item.getAttribute('data-num') ? Number(item.getAttribute('data-num')) : 1;
|
|
|
// 小于选中置灰
|
|
|
- if (idx < leftVfmeasuresIndex) {
|
|
|
+ if (measureNum < leftMeasureNumberXML) {
|
|
|
item.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(96,159,207,0.5)")
|
|
|
item.querySelector('.vf-custom-bot')?.setAttribute("fill", "rgba(43,112,165,0.5)")
|
|
|
}
|
|
|
// 大于选中置灰
|
|
|
- if (idx > rightVfmeasuresIndex) {
|
|
|
+ if(measureNum > rightMeasureNumberXML){
|
|
|
item.querySelector('.vf-custom-bg')?.setAttribute("fill", "rgba(96,159,207,0.5)")
|
|
|
item.querySelector('.vf-custom-bot')?.setAttribute("fill", "rgba(43,112,165,0.5)")
|
|
|
}
|
|
|
+ // 预备小节
|
|
|
+ if(state.sectionFirst && measureNum === state.sectionFirst.MeasureNumberXML){
|
|
|
+ item?.querySelector('.vf-custom-bg')?.setAttribute("fill", "#71B8BD")
|
|
|
+ item?.querySelector('.vf-custom-bot')?.setAttribute("fill", "#448F9C")
|
|
|
+ }
|
|
|
})
|
|
|
- // 预备小节
|
|
|
- if (state.sectionFirst) {
|
|
|
- const sectionFirstVfmeasuresIndex = state.sectionFirst.MeasureNumberXML - getNeedReduceMultipleRestNum(state.sectionFirst.MeasureNumberXML) - 1
|
|
|
- const sectionFirstDom = state.vfmeasures[sectionFirstVfmeasuresIndex]
|
|
|
- sectionFirstDom?.querySelector('.vf-custom-bg')?.setAttribute("fill", "#71B8BD")
|
|
|
- sectionFirstDom?.querySelector('.vf-custom-bot')?.setAttribute("fill", "#448F9C")
|
|
|
- }
|
|
|
- } else {
|
|
|
+ }else{
|
|
|
// 恢复选段前
|
|
|
- const matchMeasureNum = state.activeMeasureIndex - getNeedReduceMultipleRestNum(state.activeMeasureIndex) - 1
|
|
|
state.vfmeasures.forEach((item: any, idx: number) => {
|
|
|
- if (idx === matchMeasureNum) {
|
|
|
+ const measureNum = item.getAttribute('data-num') ? Number(item.getAttribute('data-num')) : -1;
|
|
|
+ const nextMeasureNum = state.vfmeasures[idx+1]?.getAttribute('data-num') ? Number(state.vfmeasures[idx+1]?.getAttribute('data-num')) : -1;
|
|
|
+ if (measureNum >= 0 && (measureNum === state.activeMeasureIndex || (measureNum < state.activeMeasureIndex && nextMeasureNum > state.activeMeasureIndex)) ) {
|
|
|
item.querySelector('.vf-custom-bg')?.setAttribute("fill", "#132D4C")
|
|
|
item.querySelector('.vf-custom-bot')?.setAttribute("fill", "#040D1E")
|
|
|
} else {
|
|
@@ -1714,10 +1755,8 @@ watch(
|
|
|
/** 刷新谱面 */
|
|
|
export const refreshMusicSvg = () => {
|
|
|
state.loadingText = '正在加载中,请稍等…'
|
|
|
- if (state.isSingleLine) {
|
|
|
- // 销毁旋律线
|
|
|
- destroySmoothAnimation()
|
|
|
- }
|
|
|
+ // 销毁旋律线
|
|
|
+ destroySmoothAnimation()
|
|
|
musicScoreRef.value?.refreshMusicScore()
|
|
|
}
|
|
|
|