|
@@ -1,6 +1,7 @@
|
|
import dayjs from "dayjs";
|
|
import dayjs from "dayjs";
|
|
import duration from "dayjs/plugin/duration";
|
|
import duration from "dayjs/plugin/duration";
|
|
import state, { customData } from "/src/state";
|
|
import state, { customData } from "/src/state";
|
|
|
|
+import { metronomeData as metronomeDataState } from "./metronome"
|
|
import { browser } from "../utils/index";
|
|
import { browser } from "../utils/index";
|
|
import {
|
|
import {
|
|
isSpecialMark,
|
|
isSpecialMark,
|
|
@@ -17,6 +18,11 @@ const browserInfo = browser();
|
|
dayjs.extend(duration);
|
|
dayjs.extend(duration);
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
+ * 需要隐藏的中文速度文本
|
|
|
|
+ */
|
|
|
|
+const hideSpeedWords: string[] = ["中速"];
|
|
|
|
+
|
|
|
|
+/**
|
|
* 获取节拍器的时间
|
|
* 获取节拍器的时间
|
|
* @param speed 速度
|
|
* @param speed 速度
|
|
* @param firstMeasure 曲谱第一个小节
|
|
* @param firstMeasure 曲谱第一个小节
|
|
@@ -38,7 +44,7 @@ export const getFixTime = (speed: number) => {
|
|
numerator = numerator*2;
|
|
numerator = numerator*2;
|
|
}
|
|
}
|
|
// console.log('diff', speed, duration, formatBeatUnit(beatUnit), denominator, numerator, (numerator / denominator))
|
|
// console.log('diff', speed, duration, formatBeatUnit(beatUnit), denominator, numerator, (numerator / denominator))
|
|
- return state.isOpenMetronome ? (60 / speed) * formatBeatUnit(beatUnit) * (numerator / denominator) : 0;
|
|
|
|
|
|
+ return (60 / speed) * formatBeatUnit(beatUnit) * (numerator / denominator);
|
|
};
|
|
};
|
|
|
|
|
|
export const retain = (time: number) => {
|
|
export const retain = (time: number) => {
|
|
@@ -378,6 +384,7 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
// console.log(visiblePartInfo, partIndex)
|
|
// console.log(visiblePartInfo, partIndex)
|
|
// 根据后台已选择的分轨筛选出能切换的声轨
|
|
// 根据后台已选择的分轨筛选出能切换的声轨
|
|
state.partListNames = partListNames;
|
|
state.partListNames = partListNames;
|
|
|
|
+ // console.log('分轨名称',state.partListNames)
|
|
if (visiblePartInfo) {
|
|
if (visiblePartInfo) {
|
|
const id = visiblePartInfo.getAttribute("id");
|
|
const id = visiblePartInfo.getAttribute("id");
|
|
Array.from(parts).forEach((part: any) => {
|
|
Array.from(parts).forEach((part: any) => {
|
|
@@ -613,14 +620,22 @@ export const formatZoom = (num = 1) => {
|
|
/** 格式化曲谱
|
|
/** 格式化曲谱
|
|
* 1.全休止符的小节,没有音符默认加个全休止符
|
|
* 1.全休止符的小节,没有音符默认加个全休止符
|
|
*/
|
|
*/
|
|
-export const formatXML = (xml: string): string => {
|
|
|
|
|
|
+export const formatXML = (xml: string, xmlUrl?: string): string => {
|
|
if (!xml) return "";
|
|
if (!xml) return "";
|
|
|
|
+
|
|
const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
|
|
const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
|
|
const measures = Array.from(xmlParse.getElementsByTagName("measure"));
|
|
const measures = Array.from(xmlParse.getElementsByTagName("measure"));
|
|
const repeats: any = Array.from(xmlParse.querySelectorAll('repeat'));
|
|
const repeats: any = Array.from(xmlParse.querySelectorAll('repeat'));
|
|
compatibleXmlPitchVoice(xmlParse);
|
|
compatibleXmlPitchVoice(xmlParse);
|
|
|
|
+ // 获取作词、作曲家
|
|
|
|
+ getComposer(xmlParse);
|
|
// 处理重复小节信息
|
|
// 处理重复小节信息
|
|
parseXmlToRepeat(repeats)
|
|
parseXmlToRepeat(repeats)
|
|
|
|
+ // 解析处理evxml
|
|
|
|
+ if (state.isEvxml) {
|
|
|
|
+ analyzeEvxml(xmlParse, xmlUrl);
|
|
|
|
+ customizationXml(xmlParse);
|
|
|
|
+ }
|
|
// const words: any = xmlParse.getElementsByTagName("words");
|
|
// const words: any = xmlParse.getElementsByTagName("words");
|
|
// for (const word of words) {
|
|
// for (const word of words) {
|
|
// if (word && word.textContent?.trim() === "筒音作5") {
|
|
// if (word && word.textContent?.trim() === "筒音作5") {
|
|
@@ -644,6 +659,37 @@ export const formatXML = (xml: string): string => {
|
|
speed = Number(measure.getElementsByTagName('per-minute')[0]?.textContent)
|
|
speed = Number(measure.getElementsByTagName('per-minute')[0]?.textContent)
|
|
}
|
|
}
|
|
const divisions = parseInt(measure.getElementsByTagName("divisions")[0]?.textContent || "256");
|
|
const divisions = parseInt(measure.getElementsByTagName("divisions")[0]?.textContent || "256");
|
|
|
|
+ // 如果note节点里面有space节点,并且没有duration节点,代表这是一个空白节点,需要删除
|
|
|
|
+ if (measure.getElementsByTagName("note").length && state.isEvxml) {
|
|
|
|
+ const noteList = Array.from(measure.getElementsByTagName("note")) || [];
|
|
|
|
+ noteList.forEach((note: any) => {
|
|
|
|
+ // if (note.getElementsByTagName("space").length && !note.getElementsByTagName("duration").length) {
|
|
|
|
+ // measure.removeChild(note);
|
|
|
|
+ // }
|
|
|
|
+ // 非倚音音符
|
|
|
|
+ if (!note.getElementsByTagName("grace").length) {
|
|
|
|
+ if (!note.getElementsByTagName("duration").length || (note.getElementsByTagName("duration").length && note.getElementsByTagName("duration")[0]?.textContent == 0)) {
|
|
|
|
+ measure.removeChild(note);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ // 如果有特殊中文速度文本,需要删除
|
|
|
|
+ const reg = new RegExp("[\\u4E00-\\u9FFF]+", "g");
|
|
|
|
+ if (measure.getElementsByTagName("words").length && state.isEvxml) {
|
|
|
|
+ const wordList = Array.from(measure.getElementsByTagName("words")) || [];
|
|
|
|
+ wordList.forEach((word: any) => {
|
|
|
|
+ // TODO:删除妙极客曲子无意义的words
|
|
|
|
+ // wordArr?.push(word?.textContent)
|
|
|
|
+ if (word?.textContent && reg.test(word?.textContent) && word?.parentNode?.parentNode) {
|
|
|
|
+ measure.removeChild(word.parentNode.parentNode);
|
|
|
|
+ // deleteWordArr?.push(word?.textContent)
|
|
|
|
+ }
|
|
|
|
+ // if(hideSpeedWords.includes(word?.textContent) && word?.parentNode?.parentNode) {
|
|
|
|
+ // measure.removeChild(word.parentNode.parentNode);
|
|
|
|
+ // }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
if (measure.getElementsByTagName("note").length === 0) {
|
|
if (measure.getElementsByTagName("note").length === 0) {
|
|
const forwardTimeElement = measure.getElementsByTagName("forward")[0]?.getElementsByTagName("duration")[0];
|
|
const forwardTimeElement = measure.getElementsByTagName("forward")[0]?.getElementsByTagName("duration")[0];
|
|
if (forwardTimeElement) {
|
|
if (forwardTimeElement) {
|
|
@@ -661,6 +707,9 @@ export const formatXML = (xml: string): string => {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 如果曲谱详情接口没有返回速度,则取xml第一小节的速度,如果取不到,则取默认速度:100
|
|
// 如果曲谱详情接口没有返回速度,则取xml第一小节的速度,如果取不到,则取默认速度:100
|
|
|
|
+ if (!speed || speed == -1) {
|
|
|
|
+ speed = 100
|
|
|
|
+ }
|
|
if (!state.originSpeed) {
|
|
if (!state.originSpeed) {
|
|
state.originSpeed = state.speed = speed || 100
|
|
state.originSpeed = state.speed = speed || 100
|
|
}
|
|
}
|
|
@@ -726,6 +775,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
let staveNoteIndex = 0;
|
|
let staveNoteIndex = 0;
|
|
let staveIndex = 0;
|
|
let staveIndex = 0;
|
|
|
|
|
|
|
|
+ let preNoteEndTime = 0; // 上一个音符的结束时间
|
|
|
|
+
|
|
const _notes = [] as any[];
|
|
const _notes = [] as any[];
|
|
if (state.gradualTimes) {
|
|
if (state.gradualTimes) {
|
|
console.log("后台设置的渐慢小节时间", state.gradual, state.gradualTimes);
|
|
console.log("后台设置的渐慢小节时间", state.gradual, state.gradualTimes);
|
|
@@ -735,6 +786,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
const currentTimes = [] as any[];
|
|
const currentTimes = [] as any[];
|
|
let isSetNextNoteReal = false;
|
|
let isSetNextNoteReal = false;
|
|
let differFrom = 0;
|
|
let differFrom = 0;
|
|
|
|
+ // let testIdx = 0;
|
|
|
|
+ let repeatIdx = 0; // 循环的次数
|
|
while (!iterator.EndReached) {
|
|
while (!iterator.EndReached) {
|
|
// console.log({ ...iterator });
|
|
// console.log({ ...iterator });
|
|
const voiceEntries = iterator.CurrentVoiceEntries?.[0] ? [iterator.CurrentVoiceEntries?.[0]] : [];
|
|
const voiceEntries = iterator.CurrentVoiceEntries?.[0] ? [iterator.CurrentVoiceEntries?.[0]] : [];
|
|
@@ -818,10 +871,19 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
}
|
|
}
|
|
|
|
|
|
iterator.moveToNextVisibleVoiceEntry(false);
|
|
iterator.moveToNextVisibleVoiceEntry(false);
|
|
|
|
+ // 从头开始循环,repeatIdx标记+1
|
|
|
|
+ if (iterator.backJumpOccurred) {
|
|
|
|
+ repeatIdx += 1;
|
|
|
|
+ }
|
|
|
|
+ iterator.repeatIdx = repeatIdx;
|
|
|
|
+ // console.log('小节',testIdx,iterator.repeatIdx,iterator.EndReached,iterator.currentMeasureIndex,iterator.backJumpOccurred,iterator.forwardJumpOccurred)
|
|
|
|
+ // testIdx += 1;
|
|
}
|
|
}
|
|
// 是否是变速的曲子
|
|
// 是否是变速的曲子
|
|
const hasVaryingSpeed = _notes.some((item: any) => item.measuresTempoInBPM !== _notes[0].measuresTempoInBPM)
|
|
const hasVaryingSpeed = _notes.some((item: any) => item.measuresTempoInBPM !== _notes[0].measuresTempoInBPM)
|
|
- console.log('变速曲子',hasVaryingSpeed)
|
|
|
|
|
|
+ console.log('变速曲子',hasVaryingSpeed, _notes)
|
|
|
|
+ let noteIds: any = [];
|
|
|
|
+ // let voicesBBox: any = null;
|
|
for (let { note, iterator, currentTime, isDouble, isMutileSubject } of _notes) {
|
|
for (let { note, iterator, currentTime, isDouble, isMutileSubject } of _notes) {
|
|
if (note) {
|
|
if (note) {
|
|
if (preMeasureNumber != note?.sourceMeasure?.MeasureNumberXML) {
|
|
if (preMeasureNumber != note?.sourceMeasure?.MeasureNumberXML) {
|
|
@@ -852,7 +914,28 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
if (metronomeNoteIndex !== 0 && metronomeNoteIndex > si) {
|
|
if (metronomeNoteIndex !== 0 && metronomeNoteIndex > si) {
|
|
measureSpeed = allNotes[allNotes.length - 1]?.speed || 100;
|
|
measureSpeed = allNotes[allNotes.length - 1]?.speed || 100;
|
|
}
|
|
}
|
|
- const activeVerticalMeasureList = [note.sourceMeasure.verticalMeasureList?.[0]] || [];
|
|
|
|
|
|
+ // 当前的分轨
|
|
|
|
+ let activeVerticalMeasureList: any = [];
|
|
|
|
+ /**
|
|
|
|
+ * bug: #9959
|
|
|
|
+ * 多分轨合并展示,第一分轨又可能获取不到对应的音符,需要在当前小节中音符最多的分轨中去查找音符
|
|
|
|
+ */
|
|
|
|
+ // if (state.isCombineRender) {
|
|
|
|
+ // const allTrackList = note.sourceMeasure.verticalMeasureList;
|
|
|
|
+ // let maxIdx = 0, maxNote = 0;
|
|
|
|
+ // allTrackList.forEach((item: any, index: number) => {
|
|
|
|
+ // if (item?.vfVoices['1']?.tickables?.length > maxNote) {
|
|
|
|
+ // maxIdx = index
|
|
|
|
+ // maxNote = item?.vfVoices['1']?.tickables?.length
|
|
|
|
+ // }
|
|
|
|
+ // })
|
|
|
|
+ // activeVerticalMeasureList = [note.sourceMeasure?.verticalMeasureList?.[maxIdx]] || [];
|
|
|
|
+ // } else {
|
|
|
|
+ // activeVerticalMeasureList = [note.sourceMeasure?.verticalMeasureList?.[0]] || [];
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+ activeVerticalMeasureList = [note.sourceMeasure?.verticalMeasureList?.[0]] || [];
|
|
|
|
+
|
|
const { realValue } = iterator.currentTimeStamp;
|
|
const { realValue } = iterator.currentTimeStamp;
|
|
const { RealValue: vRealValue, Denominator: vDenominator } = formatDuration(
|
|
const { RealValue: vRealValue, Denominator: vDenominator } = formatDuration(
|
|
iterator.currentMeasure.activeTimeSignature,
|
|
iterator.currentMeasure.activeTimeSignature,
|
|
@@ -873,12 +956,18 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
// 管乐迷,按自定义按读取到的音符时值
|
|
// 管乐迷,按自定义按读取到的音符时值
|
|
if (customNoteCurrentTime) {
|
|
if (customNoteCurrentTime) {
|
|
if (isMutileSubject && currentTimes[i + 1] > 0 && NoteRealValue > currentTimes[i + 1]) {
|
|
if (isMutileSubject && currentTimes[i + 1] > 0 && NoteRealValue > currentTimes[i + 1]) {
|
|
- console.log(NoteRealValue, currentTimes[i + 1])
|
|
|
|
|
|
+ // console.log(NoteRealValue, currentTimes[i + 1])
|
|
NoteRealValue = currentTimes[i + 1];
|
|
NoteRealValue = currentTimes[i + 1];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
let relativeTime = usetime;
|
|
let relativeTime = usetime;
|
|
|
|
+
|
|
|
|
+ // 妙极客的曲子,修复有的音符有times,有的音符没有times导致的,累计时长错误问题
|
|
|
|
+ if (state.isEvxml && relativeTime < preNoteEndTime - fixtime) {
|
|
|
|
+ relativeTime = preNoteEndTime - fixtime
|
|
|
|
+ }
|
|
|
|
+
|
|
let beatSpeed = 0;
|
|
let beatSpeed = 0;
|
|
// 速度不能为0 此处的速度应该是按照设置的速度而不是校准后的速度,否则mp3速度不对
|
|
// 速度不能为0 此处的速度应该是按照设置的速度而不是校准后的速度,否则mp3速度不对
|
|
if (measureSpeed !== baseSpeed && !hasVaryingSpeed) {
|
|
if (measureSpeed !== baseSpeed && !hasVaryingSpeed) {
|
|
@@ -889,8 +978,13 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
// let beatSpeed = measureSpeed || baseSpeed
|
|
// let beatSpeed = measureSpeed || baseSpeed
|
|
// 如果有节拍器,需要将节拍器的时间算出来
|
|
// 如果有节拍器,需要将节拍器的时间算出来
|
|
if (i === 0) {
|
|
if (i === 0) {
|
|
- fixtime += getFixTime(beatSpeed);
|
|
|
|
- state.fixtime = fixtime;
|
|
|
|
|
|
+ if(state.isOpenMetronome){
|
|
|
|
+ fixtime += getFixTime(beatSpeed);
|
|
|
|
+ state.fixtime = fixtime;
|
|
|
|
+ }
|
|
|
|
+ // 存储mp3节拍器时间
|
|
|
|
+ metronomeDataState.xmlMp3BeatFixTime = getFixTime(beatSpeed)
|
|
|
|
+ //
|
|
// console.log("fixtime:", fixtime, '速度:', beatSpeed, "state.isSpecialBookCategory:", state.isSpecialBookCategory, 'state.isOpenMetronome:', state.isOpenMetronome);
|
|
// console.log("fixtime:", fixtime, '速度:', beatSpeed, "state.isSpecialBookCategory:", state.isSpecialBookCategory, 'state.isOpenMetronome:', state.isOpenMetronome);
|
|
}
|
|
}
|
|
// console.log(getTimeByBeatUnit(beatUnit, measureSpeed, iterator.currentMeasure.activeTimeSignature.Denominator))
|
|
// console.log(getTimeByBeatUnit(beatUnit, measureSpeed, iterator.currentMeasure.activeTimeSignature.Denominator))
|
|
@@ -969,6 +1063,20 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
const measureLength = vRealValue * vDenominator * (60 / beatSpeed);
|
|
const measureLength = vRealValue * vDenominator * (60 / beatSpeed);
|
|
// console.table({value: iterator.currentTimeStamp.realValue, vRealValue,NoteRealValue, noteLength,measureLength, MeasureNumberXML: note.sourceMeasure.MeasureNumberXML})
|
|
// console.table({value: iterator.currentTimeStamp.realValue, vRealValue,NoteRealValue, noteLength,measureLength, MeasureNumberXML: note.sourceMeasure.MeasureNumberXML})
|
|
// console.log(i, Math.min(vRealValue, NoteRealValue),noteLength,gradualLength, formatBeatUnit(beatUnit),beatSpeed, NoteRealValue * formatBeatUnit(beatUnit) * (60 / beatSpeed) )
|
|
// console.log(i, Math.min(vRealValue, NoteRealValue),noteLength,gradualLength, formatBeatUnit(beatUnit),beatSpeed, NoteRealValue * formatBeatUnit(beatUnit) * (60 / beatSpeed) )
|
|
|
|
+ /**
|
|
|
|
+ * TODO:摇篮曲-人音-排箫(1788501975122489346),第12小节音符持续时间特殊处理
|
|
|
|
+ */
|
|
|
|
+ if (['1788501975122489346','1788502467554750466'].includes(state.cbsExamSongId)) {
|
|
|
|
+ if (i == 13) {
|
|
|
|
+ noteLength = noteLength / 2;
|
|
|
|
+ }
|
|
|
|
+ if (i == 44) {
|
|
|
|
+ noteLength = noteLength * 6;
|
|
|
|
+ }
|
|
|
|
+ if (i == 56) {
|
|
|
|
+ noteLength = noteLength * 4;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
usetime += noteLength;
|
|
usetime += noteLength;
|
|
relaMeasureLength += noteLength;
|
|
relaMeasureLength += noteLength;
|
|
let relaEndtime = noteLength + relativeTime;
|
|
let relaEndtime = noteLength + relativeTime;
|
|
@@ -976,6 +1084,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
const fixedKey = note.fixedKey || 0;
|
|
const fixedKey = note.fixedKey || 0;
|
|
// const svgElement = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables[si];
|
|
// const svgElement = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables[si];
|
|
const svgElement = activeVerticalMeasureList[0]?.vfVoices['1']?.tickables[staveNoteIndex];
|
|
const svgElement = activeVerticalMeasureList[0]?.vfVoices['1']?.tickables[staveNoteIndex];
|
|
|
|
+
|
|
// console.log('si',si,i)
|
|
// console.log('si',si,i)
|
|
// console.log(note.sourceMeasure.MeasureNumberXML,note,svgElement, NoteRealValue, measureLength)
|
|
// console.log(note.sourceMeasure.MeasureNumberXML,note,svgElement, NoteRealValue, measureLength)
|
|
if (allNotes.length && allNotes[allNotes.length - 1].relativeTime === relativeTime) {
|
|
if (allNotes.length && allNotes[allNotes.length - 1].relativeTime === relativeTime) {
|
|
@@ -1006,6 +1115,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
} else {
|
|
} else {
|
|
if (difftime > 0) {
|
|
if (difftime > 0) {
|
|
fixtime += difftime;
|
|
fixtime += difftime;
|
|
|
|
+ state.fixtime = fixtime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// 管乐迷 diff获取不准确时, 弱起补齐
|
|
// 管乐迷 diff获取不准确时, 弱起补齐
|
|
@@ -1013,6 +1123,13 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
// difftime = iterator.currentTimeStamp.realValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
// difftime = iterator.currentTimeStamp.realValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
// fixtime += difftime;
|
|
// fixtime += difftime;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // 如果是evxml,fixtime取读取xml的值
|
|
|
|
+ if (state.isEvxml) {
|
|
|
|
+ fixtime = state.evXmlBeginTime ? state.evXmlBeginTime : fixtime
|
|
|
|
+ state.fixtime = fixtime
|
|
|
|
+ }
|
|
|
|
+ console.log('节拍器时间',fixtime,state.evXmlBeginTime)
|
|
}
|
|
}
|
|
let stave = activeVerticalMeasureList[0]?.stave;
|
|
let stave = activeVerticalMeasureList[0]?.stave;
|
|
|
|
|
|
@@ -1030,6 +1147,22 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
// console.log(note.tie)
|
|
// console.log(note.tie)
|
|
// console.log('👀看看endtime', duration, relaEndtime, fixtime, i)
|
|
// console.log('👀看看endtime', duration, relaEndtime, fixtime, i)
|
|
// console.log('频率',note?.pitch?.frequency,i)
|
|
// console.log('频率',note?.pitch?.frequency,i)
|
|
|
|
+ /**
|
|
|
|
+ * evxml的曲子,如果曲谱xml中带有times信息,则音符时值优先取times中的值
|
|
|
|
+ */
|
|
|
|
+ let evNoteStartTime = 0, evNoteEndTime = 0;
|
|
|
|
+ if (state.isEvxml && note?.noteTimeInfo?.length) {
|
|
|
|
+ const idx = noteIds.filter((item: any) => item === svgElement?.attrs.id)?.length || 0
|
|
|
|
+ evNoteStartTime = note?.noteTimeInfo[idx]?.begin
|
|
|
|
+ evNoteEndTime = note?.noteTimeInfo[idx]?.end
|
|
|
|
+ if (evNoteStartTime) {
|
|
|
|
+ relativeTime = evNoteStartTime - fixtime
|
|
|
|
+ // usetime = evNoteStartTime - fixtime
|
|
|
|
+ }
|
|
|
|
+ // usetime = evNoteStartTime - fixtime
|
|
|
|
+ }
|
|
|
|
+ svgElement?.attrs.id && noteIds.push(svgElement?.attrs.id)
|
|
|
|
+
|
|
const nodeDetail = {
|
|
const nodeDetail = {
|
|
isStaccato: note.voiceEntry.isStaccato(),
|
|
isStaccato: note.voiceEntry.isStaccato(),
|
|
isRestFlag: note.isRestFlag,
|
|
isRestFlag: note.isRestFlag,
|
|
@@ -1057,8 +1190,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
note: note.halfTone + 12, // see issue #224
|
|
note: note.halfTone + 12, // see issue #224
|
|
fixtime, // 弱起补充的时间
|
|
fixtime, // 弱起补充的时间
|
|
relativeTime: retain(relativeTime),
|
|
relativeTime: retain(relativeTime),
|
|
- time: retain(relativeTime + fixtime), // 开始播放的时间
|
|
|
|
- endtime: retain(relaEndtime + fixtime), // 播放完成的时间
|
|
|
|
|
|
+ time: state.isEvxml && evNoteStartTime ? retain(evNoteStartTime) : retain(relativeTime + fixtime), // 开始播放的时间
|
|
|
|
+ endtime: state.isEvxml && evNoteEndTime ? retain(evNoteEndTime) : retain(relaEndtime + fixtime), // 播放完成的时间
|
|
relaEndtime: retain(relaEndtime),
|
|
relaEndtime: retain(relaEndtime),
|
|
realValue,
|
|
realValue,
|
|
halfTone: note.halfTone,
|
|
halfTone: note.halfTone,
|
|
@@ -1072,10 +1205,26 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
noteLength: 1,
|
|
noteLength: 1,
|
|
osdmContext: osmd,
|
|
osdmContext: osmd,
|
|
speedbeatUnit: beatUnit,
|
|
speedbeatUnit: beatUnit,
|
|
- multipleRestMeasures: multipleRestMeasures,
|
|
|
|
|
|
+ multipleRestMeasures: multipleRestMeasures, // 当前合并小节的索引,从1开始到当前的totalMultipleRestMeasures结束,
|
|
|
|
+ totalMultipleRestMeasures, // 当前小节总的合并小节数
|
|
measureSpeed, // 小节速度
|
|
measureSpeed, // 小节速度
|
|
maxNoteNum: note.maxNoteNum, // 当前小节音符最多的分轨的音符数量
|
|
maxNoteNum: note.maxNoteNum, // 当前小节音符最多的分轨的音符数量
|
|
|
|
+ repeatIdx: iterator.repeatIdx || 0, // 标记是第几遍循环,从0开始
|
|
};
|
|
};
|
|
|
|
+ // 如果是妙极客的曲子,并且第二遍循环播放需要等待时间,并且是第二遍循环的第一个小节的第一个音符
|
|
|
|
+ // if (state.isEvxml && state.secondEvXmlBeginTime && nodeDetail.i > 0 && nodeDetail.MeasureNumberXML === 1 && nodeDetail.noteId === 0) {
|
|
|
|
+ // nodeDetail.time = nodeDetail.time + state.secondEvXmlBeginTime;
|
|
|
|
+ // nodeDetail.endtime = nodeDetail.endtime + state.secondEvXmlBeginTime;
|
|
|
|
+ // usetime = usetime + state.secondEvXmlBeginTime;
|
|
|
|
+ // relativeTime = relativeTime + state.secondEvXmlBeginTime;
|
|
|
|
+ // }
|
|
|
|
+ if (state.isEvxml && nodeDetail.repeatIdx && nodeDetail.i > 0 && nodeDetail.MeasureNumberXML === 1 && nodeDetail.noteId === 0) {
|
|
|
|
+ const currentWaitTime = state.evXmlBeginArr[nodeDetail.repeatIdx] || 0;
|
|
|
|
+ nodeDetail.time = nodeDetail.time + currentWaitTime;
|
|
|
|
+ nodeDetail.endtime = nodeDetail.endtime + currentWaitTime;
|
|
|
|
+ usetime = usetime + currentWaitTime;
|
|
|
|
+ relativeTime = relativeTime + currentWaitTime;
|
|
|
|
+ }
|
|
nodeDetail.realKey = formatRealKey(note.halfTone - fixedKey * 12, nodeDetail);
|
|
nodeDetail.realKey = formatRealKey(note.halfTone - fixedKey * 12, nodeDetail);
|
|
nodeDetail.duration = nodeDetail.endtime - nodeDetail.time;
|
|
nodeDetail.duration = nodeDetail.endtime - nodeDetail.time;
|
|
let tickables = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables || [];
|
|
let tickables = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables || [];
|
|
@@ -1084,6 +1233,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
}
|
|
}
|
|
// console.log(note.sourceMeasure.MeasureNumberXML, note.sourceMeasure.verticalSourceStaffEntryContainers.length)
|
|
// console.log(note.sourceMeasure.MeasureNumberXML, note.sourceMeasure.verticalSourceStaffEntryContainers.length)
|
|
// console.log('👀看看endtime', nodeDetail.duration, relaEndtime, fixtime, i)
|
|
// console.log('👀看看endtime', nodeDetail.duration, relaEndtime, fixtime, i)
|
|
|
|
+ // console.log('音符时间',nodeDetail.i,nodeDetail.time,nodeDetail.endtime)
|
|
tickables = tickables.filter((tickable: any) => tickable.attrs?.type !== "GhostNote")
|
|
tickables = tickables.filter((tickable: any) => tickable.attrs?.type !== "GhostNote")
|
|
const maxNum = (state.isCombineRender && note.maxNoteNum) ? note.maxNoteNum : tickables.length;
|
|
const maxNum = (state.isCombineRender && note.maxNoteNum) ? note.maxNoteNum : tickables.length;
|
|
nodeDetail.noteLength = maxNum || 1;
|
|
nodeDetail.noteLength = maxNum || 1;
|
|
@@ -1101,11 +1251,14 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
relaMeasureLength = 0;
|
|
relaMeasureLength = 0;
|
|
measures = [];
|
|
measures = [];
|
|
}
|
|
}
|
|
|
|
+ preNoteEndTime = nodeDetail.endtime;
|
|
}
|
|
}
|
|
i++;
|
|
i++;
|
|
}
|
|
}
|
|
// 按照时间轴排序
|
|
// 按照时间轴排序
|
|
const sortArray = allNotes.sort((a, b) => a.relativeTime - b.relativeTime).map((item, index) => ({ ...item, i: index }));
|
|
const sortArray = allNotes.sort((a, b) => a.relativeTime - b.relativeTime).map((item, index) => ({ ...item, i: index }));
|
|
|
|
+ // const sortArray = allNotes.sort((a, b) => a.time - b.time).map((item, index) => ({ ...item, i: index }));
|
|
|
|
+ // const sortArray = allNotes.map((item, index) => ({ ...item, i: index }));
|
|
console.timeEnd("音符跑完时间");
|
|
console.timeEnd("音符跑完时间");
|
|
try {
|
|
try {
|
|
osmd.cursor.reset();
|
|
osmd.cursor.reset();
|
|
@@ -1135,6 +1288,18 @@ export const getNoteByMeasuresSlursStart = (note: any) => {
|
|
return activeNote;
|
|
return activeNote;
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+// 解析xml,获取作词、作曲家名称
|
|
|
|
+const getComposer = (xmlParse: any) => {
|
|
|
|
+ const creators: any = Array.from(xmlParse.querySelectorAll('creator'));
|
|
|
|
+ for (const creator of creators) {
|
|
|
|
+ if (creator && creator.getAttribute('type') === 'composer' && !state.musicComposer) {
|
|
|
|
+ state.musicComposer = creator.textContent?.trim() || '';
|
|
|
|
+ }
|
|
|
|
+ if (creator && creator.getAttribute('type') === 'lyricist' && !state.musicLyricist) {
|
|
|
|
+ state.musicLyricist = creator.textContent?.trim() || '';
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
// 通过xml信息获取重播的小节信息
|
|
// 通过xml信息获取重播的小节信息
|
|
const parseXmlToRepeat = (repeats: any) => {
|
|
const parseXmlToRepeat = (repeats: any) => {
|
|
@@ -1162,7 +1327,7 @@ const parseXmlToRepeat = (repeats: any) => {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
state.repeatInfo = repeatInfo
|
|
state.repeatInfo = repeatInfo
|
|
- console.log('重播',repeatInfo)
|
|
|
|
|
|
+ // console.log('重播',repeatInfo)
|
|
}
|
|
}
|
|
|
|
|
|
// 校验当前选段是否满足重播条件
|
|
// 校验当前选段是否满足重播条件
|
|
@@ -1192,6 +1357,120 @@ export const verifyCanRepeat = (startNum: number, endNum: number) => {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// 处理妙极客xml谱面
|
|
|
|
+const customizationXml = (xmlParse: any) => {
|
|
|
|
+ const credits: any = Array.from(xmlParse.querySelectorAll('credit'));
|
|
|
|
+ const creators: any = Array.from(xmlParse.querySelectorAll('creator'));
|
|
|
|
+ const graces: any = Array.from(xmlParse.querySelectorAll('grace'));
|
|
|
|
+ const measures: any[] = Array.from(xmlParse.getElementsByTagName("measure"));
|
|
|
|
+ const notes: any[] = Array.from(xmlParse.getElementsByTagName("note"));
|
|
|
|
+
|
|
|
|
+ // 获取音符最多的歌词数,用于自定义循环播放次数
|
|
|
|
+ let maxLyricNum = 0;
|
|
|
|
+ if (notes && notes.length) {
|
|
|
|
+ for (const note of notes) {
|
|
|
|
+ if (maxLyricNum < note.getElementsByTagName("lyric").length) {
|
|
|
|
+ maxLyricNum = note.getElementsByTagName("lyric").length
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ state.maxLyricNum = maxLyricNum;
|
|
|
|
+ // state.osmd.EngravingRules.DYCustomRepeatCount = maxLyricNum;
|
|
|
|
+ ;(window as any).DYCustomRepeatCount = state.maxLyricNum;
|
|
|
|
+ console.log('歌词次数',maxLyricNum)
|
|
|
|
+
|
|
|
|
+ if (credits && credits.length) {
|
|
|
|
+ for (const credit of credits) {
|
|
|
|
+ if (credit.getElementsByTagName("credit-type")?.[0]?.textContent === 'lyricist') {
|
|
|
|
+ const creditWord = credit.getElementsByTagName("credit-words")
|
|
|
|
+ creditWord?.[0].setAttribute('justify', 'right')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (creators && creators.length) {
|
|
|
|
+ for (const creator of creators) {
|
|
|
|
+ if (creator.getAttribute('type') === 'lyricist') {
|
|
|
|
+ // creator.textContent = '测试一下1';
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // 妙极客xml的倚音(grace)标签需要加上slash=yes属性
|
|
|
|
+ if (graces && graces.length) {
|
|
|
|
+ for (const grace of graces) {
|
|
|
|
+ grace?.setAttribute('slash','yes');
|
|
|
|
+ // console.log(grace,'倚音')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // 妙极客xml部分小节没有音符,只有Segno,该小节不需要渲染,表示的是反复标记
|
|
|
|
+ for (const measure of measures) {
|
|
|
|
+ const hasNote = measure.getElementsByTagName("note").length;
|
|
|
|
+ const hasSegno = measure.getElementsByTagName("segno").length;
|
|
|
|
+ const sounds = Array.from(measure.getElementsByTagName("sound"));
|
|
|
|
+ const hasSoundSegno = sounds.some((item: any) => item.getAttribute('segno') === 'segno' );
|
|
|
|
+ if (!hasNote && hasSegno && hasSoundSegno) {
|
|
|
|
+ const parent = measure.parentNode;
|
|
|
|
+ parent.removeChild(measure);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * bug: #10289,曲目:1782672015612725196、1788040971888537602
|
|
|
|
+ * 妙极客xml,多遍歌词循环的曲目,如果没有repeat标签,需要加上repeat标签
|
|
|
|
+ * */
|
|
|
|
+ if (maxLyricNum > 1) {
|
|
|
|
+ const hasRepeat = xmlParse.querySelectorAll('repeat').length > 0
|
|
|
|
+ if (!hasRepeat) {
|
|
|
|
+ const lastMeasure = measures.last();
|
|
|
|
+ if (lastMeasure.getElementsByTagName('barline').length) {
|
|
|
|
+ const barlineDom = lastMeasure.getElementsByTagName('barline')[0]
|
|
|
|
+ barlineDom.innerHTML = barlineDom.innerHTML + `<repeat direction="backward" />`;
|
|
|
|
+ } else {
|
|
|
|
+ lastMeasure.innerHTML = lastMeasure.innerHTML + `
|
|
|
|
+ <barline location="right">
|
|
|
|
+ <bar-style>light-heavy</bar-style>
|
|
|
|
+ <repeat direction="backward" />
|
|
|
|
+ </barline>`
|
|
|
|
+ }
|
|
|
|
+ // console.log(lastMeasure)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// 计算evxml的起始播放时间
|
|
|
|
+const analyzeEvxml = (xmlParse: any, xmlUrl?: string) => {
|
|
|
|
+ // xml拍号数
|
|
|
|
+ const xmlNum = xmlParse.getElementsByTagName("timegap")[0]?.getElementsByTagName("values")[0]?.getElementsByTagName("item")[0]?.getAttribute('num');
|
|
|
|
+ const denNum = xmlParse.getElementsByTagName("timegap")[0]?.getElementsByTagName("values")[0]?.getElementsByTagName("item")[0]?.getAttribute('den');
|
|
|
|
+ const xmlNum2 = xmlParse.getElementsByTagName("timegap")[0]?.getElementsByTagName("values")[0]?.getElementsByTagName("item")[1]?.getAttribute('num');
|
|
|
|
+ const denNum2 = xmlParse.getElementsByTagName("timegap")[0]?.getElementsByTagName("values")[0]?.getElementsByTagName("item")[1]?.getAttribute('den');
|
|
|
|
+ const timeGaps: any = xmlParse.getElementsByTagName("timegap")?.length ? Array.from(xmlParse.getElementsByTagName("timegap")?.[0]?.getElementsByTagName("values")?.[0]?.getElementsByTagName("item")) : [];
|
|
|
|
+ // 第一个音符的起始时间
|
|
|
|
+ const firstMeasure = xmlParse.getElementsByTagName("measure")[0];
|
|
|
|
+ if (firstMeasure) {
|
|
|
|
+ const firstNoteBeginTime = firstMeasure.getElementsByTagName("times")[0]?.getElementsByTagName("time")[0]?.getAttribute('begin');
|
|
|
|
+ state.evXmlBeginTime = firstNoteBeginTime ? firstNoteBeginTime / 1000 : xmlNum ? 60 / state.originSpeed * xmlNum * 4/denNum : 0;
|
|
|
|
+ state.secondEvXmlBeginTime = firstNoteBeginTime ? 0 : xmlNum2 ? 60 / state.originSpeed * xmlNum2 * 4/denNum2 : 0;
|
|
|
|
+ const hasTimeGap = xmlParse.getElementsByTagName("timegap").length > 0;
|
|
|
|
+ const hasTimes = xmlParse.getElementsByTagName("times").length > 0;
|
|
|
|
+
|
|
|
|
+ if (timeGaps && timeGaps.length && !firstNoteBeginTime) {
|
|
|
|
+ for (const timeGap of timeGaps) {
|
|
|
|
+ const num: any = timeGap?.getAttribute('num'), den: any = timeGap?.getAttribute('den');
|
|
|
|
+ const startTime = num ? 60 / state.originSpeed * num * 4/den : 0;
|
|
|
|
+ state.evXmlBeginArr.push(startTime)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ console.log('🚀 ~ evxml解析','有timegap:',hasTimeGap,'有times:',hasTimes,'timegap集合',state.evXmlBeginArr,'第一个timegap',state.evXmlBeginTime)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // if (!hasTimeGap && !hasTimes) {
|
|
|
|
+ // state.noTimes.push(xmlUrl)
|
|
|
|
+ // }
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* 兼容处理xml声部移调
|
|
* 兼容处理xml声部移调
|
|
* 打谱软件可能会自动处理移调,这类型的xml就不用通过程序移调了
|
|
* 打谱软件可能会自动处理移调,这类型的xml就不用通过程序移调了
|
|
@@ -1201,12 +1480,12 @@ export const verifyCanRepeat = (startNum: number, endNum: number) => {
|
|
*/
|
|
*/
|
|
export const compatibleXmlPitchVoice = (xmlParse: any) => {
|
|
export const compatibleXmlPitchVoice = (xmlParse: any) => {
|
|
const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
|
|
const partNames = Array.from(xmlParse.getElementsByTagName('part-name'));
|
|
- const partListNames = partNames.map((item: any) => item[0]?.textContent?.trim().toLocaleUpperCase !== "COMMON");
|
|
|
|
|
|
+ const partListNames = partNames.filter((item: any) => item?.textContent?.trim().toLocaleUpperCase() !== "COMMON");
|
|
if (partListNames.length == 1) {
|
|
if (partListNames.length == 1) {
|
|
const instrumentNames = Array.from(xmlParse.getElementsByTagName('instrument-name')) || [];
|
|
const instrumentNames = Array.from(xmlParse.getElementsByTagName('instrument-name')) || [];
|
|
// @ts-ignore
|
|
// @ts-ignore
|
|
const instrumentName = instrumentNames[0]?.textContent?.trim()?.toLocaleLowerCase() || ''
|
|
const instrumentName = instrumentNames[0]?.textContent?.trim()?.toLocaleLowerCase() || ''
|
|
- // console.log('ins名称',instrumentName)
|
|
|
|
|
|
+ // console.log('instrument名称',instrumentName)
|
|
// 是否需要程序处理移调
|
|
// 是否需要程序处理移调
|
|
let xmlNeedAdjustVoice = false;
|
|
let xmlNeedAdjustVoice = false;
|
|
switch (state.musicalCodeId) {
|
|
switch (state.musicalCodeId) {
|
|
@@ -1222,7 +1501,10 @@ export const compatibleXmlPitchVoice = (xmlParse: any) => {
|
|
break;
|
|
break;
|
|
case 35:
|
|
case 35:
|
|
xmlNeedAdjustVoice = !instrumentName || instrumentName.includes('solo') || instrumentName.includes('woodwind') ? true : false
|
|
xmlNeedAdjustVoice = !instrumentName || instrumentName.includes('solo') || instrumentName.includes('woodwind') ? true : false
|
|
- break;
|
|
|
|
|
|
+ break;
|
|
|
|
+ case 39:
|
|
|
|
+ xmlNeedAdjustVoice = !instrumentName || instrumentName.includes('solo') || instrumentName.includes('whistling') ? true : false
|
|
|
|
+ break;
|
|
default:
|
|
default:
|
|
xmlNeedAdjustVoice = !instrumentName || instrumentName.includes('solo') ? true : false
|
|
xmlNeedAdjustVoice = !instrumentName || instrumentName.includes('solo') ? true : false
|
|
break;
|
|
break;
|