|
@@ -1,16 +1,32 @@
|
|
|
import dayjs from "dayjs";
|
|
|
import duration from "dayjs/plugin/duration";
|
|
|
-import state, { GradualVersion } from "../state";
|
|
|
+import state from "../state";
|
|
|
// import appState from "/src/state";
|
|
|
import { browser } from "../utils/index";
|
|
|
-import runtime, { getFixTime } from "./runtime";
|
|
|
-// @ts-ignore
|
|
|
import { isSpecialMark, isSpeedKeyword, Fraction, SourceMeasure, isGradientWords, GRADIENT_SPEED_RESET_TAG, StringUtil, OpenSheetMusicDisplay } from "/osmd-extended/src";
|
|
|
import { GradualChange, speedInfo } from "./calcSpeed";
|
|
|
import { useRoute } from "vue-router";
|
|
|
const browserInfo = browser();
|
|
|
dayjs.extend(duration);
|
|
|
|
|
|
+/**
|
|
|
+ * 获取节拍器的时间
|
|
|
+ * @param speed 速度
|
|
|
+ * @param firstMeasure 曲谱第一个小节
|
|
|
+ * @returns 节拍器的时间
|
|
|
+ */
|
|
|
+export const getFixTime = (speed: number) => {
|
|
|
+ const duration: any = getDuration(state.osmd as unknown as OpenSheetMusicDisplay);
|
|
|
+ let numerator = duration.numerator || 0;
|
|
|
+ let denominator = duration.denominator || 4;
|
|
|
+ const beatUnit = duration.beatUnit || "quarter";
|
|
|
+ // if (state.repeatedBeats) {
|
|
|
+ // // 音频制作问题仅2拍不重复
|
|
|
+ // numerator = numerator === 2 ? 4 : numerator;
|
|
|
+ // }
|
|
|
+ return !state.needTick && !state.skipTick ? (60 / speed) * formatBeatUnit(beatUnit) * (numerator / denominator) : 0;
|
|
|
+};
|
|
|
+
|
|
|
const getLinkId = (): string => {
|
|
|
return location.hash.split("?")[0].split("/").pop() || "";
|
|
|
};
|
|
@@ -83,437 +99,6 @@ const tranTime = (str: string = "") => {
|
|
|
return `1970-01-01 00:${result}0`;
|
|
|
};
|
|
|
|
|
|
-export const getAllNodes = (osmd: any) => {
|
|
|
- const route = useRoute();
|
|
|
- const detailId = getLinkId();
|
|
|
- let fixtime = browserInfo.huawei ? 0.08 : 0; //getFixTime()
|
|
|
- const allNotes: any[] = [];
|
|
|
- const allNoteId: string[] = [];
|
|
|
- const allMeasures: any[] = [];
|
|
|
- const { baseSpeed = 90 } = state;
|
|
|
- const formatRealKey = (realKey: number, detail: any) => {
|
|
|
- // 长笛的LEVEL 2-5-1条练习是泛音练习,以每小节第一个音的指法为准,高音不变变指法。
|
|
|
- const olnyOneIds = ["906"];
|
|
|
- if (olnyOneIds.includes(detailId)) {
|
|
|
- return detail.measures[0]?.realKey || realKey;
|
|
|
- }
|
|
|
- // 圆号的LEVEL 2-5条练习是泛音练习,最后四小节指法以连音线第一个小节为准
|
|
|
- const olnyOneIds2 = ["782", "784"];
|
|
|
- if (olnyOneIds2.includes(detailId)) {
|
|
|
- const measureNumbers = [14, 16, 30, 32];
|
|
|
- if (measureNumbers.includes(detail.firstVerticalMeasure?.measureNumber)) {
|
|
|
- return allNotes[allNotes.length - 1]?.realKey || realKey;
|
|
|
- }
|
|
|
- }
|
|
|
- // 2-6 第三小节指法按照第一个音符显示
|
|
|
- const filterIds = ["900", "901", "640", "641", "739", "740", "800", "801", "773", "774", "869", "872", "714", "715"];
|
|
|
- if (filterIds.includes(detailId)) {
|
|
|
- if (detail.firstVerticalMeasure?.measureNumber === 3 || detail.firstVerticalMeasure?.measureNumber === 9) {
|
|
|
- return detail.measures[0]?.realKey || realKey;
|
|
|
- }
|
|
|
- }
|
|
|
- return realKey;
|
|
|
- };
|
|
|
- if (osmd?.cursor) {
|
|
|
- try {
|
|
|
- osmd.cursor.reset();
|
|
|
- } catch (error) {}
|
|
|
- const iterator = osmd.cursor.iterator;
|
|
|
- let i = 0;
|
|
|
- let si = 0;
|
|
|
- let measures: any[] = [];
|
|
|
- let stepSpeeds: number[] = [];
|
|
|
- /** 弱起时间 */
|
|
|
- let difftime = 0;
|
|
|
- let usetime = 0;
|
|
|
- let useGradualTime = 0;
|
|
|
- let relaMeasureLength = 0;
|
|
|
- let beatUnit = "quarter";
|
|
|
- let gradualSpeed;
|
|
|
- let gradualChange: GradualChange | undefined;
|
|
|
- let gradualChangeIndex = 0;
|
|
|
- const _notes = [] as any[];
|
|
|
-
|
|
|
- if (state.gradualTimes) {
|
|
|
- if (["12280"].includes(detailId) && route.query["part-index"] === "24") {
|
|
|
- state.gradualTimes["8"] = "00:25:63";
|
|
|
- state.gradualTimes["66"] = "01:53:35";
|
|
|
- state.gradualTimes["90"] = "02:41:40";
|
|
|
- }
|
|
|
- console.log("合奏速度", state.gradual, state.gradualTimes);
|
|
|
- }
|
|
|
-
|
|
|
- let currentTimeStamp = iterator.currentTimeStamp.realValue
|
|
|
- const currentTimes = [] as any[]
|
|
|
- let isSetNextNoteReal = false
|
|
|
- let differFrom = 0
|
|
|
- while (!iterator.endReached) {
|
|
|
- const voiceEntries = iterator.currentVoiceEntries?.[0] ? [iterator.currentVoiceEntries?.[0]] : [];
|
|
|
- let currentVoiceEntries: any[] = [];
|
|
|
- // 单声部多声轨
|
|
|
- if (state.multitrack > 0) {
|
|
|
- currentVoiceEntries = [...iterator.currentVoiceEntries];
|
|
|
- } else {
|
|
|
- currentVoiceEntries = [...iterator.currentVoiceEntries].filter((n) => {
|
|
|
- return n && n?.ParentVoice?.voiceId != 1;
|
|
|
- });
|
|
|
- }
|
|
|
- let currentTime = 0;
|
|
|
- let isDouble = false;
|
|
|
- let isMutileSubject = false
|
|
|
-
|
|
|
-
|
|
|
- // console.log(iterator.currentMeasureIndex, [...iterator.currentVoiceEntries])
|
|
|
- // iterator.currentMeasureIndex == 45 && console.log(currentTimeStamp, [...iterator.currentVoiceEntries])
|
|
|
- if (currentVoiceEntries.length && !isSetNextNoteReal) {
|
|
|
- // iterator.currentMeasureIndex == 15 && console.log(iterator.currentMeasureIndex, isSetNextNoteReal)
|
|
|
- isDouble = true;
|
|
|
- let voiceNotes = [...iterator.currentVoiceEntries].reduce((notes, n) => {
|
|
|
- notes.push(...n.notes);
|
|
|
- return notes;
|
|
|
- }, []);
|
|
|
- voiceNotes = voiceNotes.sort((a: any, b: any) => a?.length?.realValue - b?.length?.realValue);
|
|
|
- currentTime = voiceNotes?.[0]?.length?.realValue || 0;
|
|
|
- // iterator.currentMeasureIndex == 15 && console.log("🚀 ~ currentTime:", currentTime)
|
|
|
-
|
|
|
- if (state.multitrack > 0 && currentVoiceEntries.length === 2) {
|
|
|
- const min = voiceNotes[0]?.length?.realValue || 0
|
|
|
- const max = voiceNotes[voiceNotes.length - 1]?.length?.realValue || 0
|
|
|
- differFrom = max - min
|
|
|
- isSetNextNoteReal = differFrom === 0 ? false : true
|
|
|
- // iterator.currentMeasureIndex == 15 && console.log("🚀 ~ differFrom:", differFrom, isSetNextNoteReal)
|
|
|
- // console.log(iterator.currentMeasureIndex, min, max)
|
|
|
- }
|
|
|
- }
|
|
|
- // 多声部上下音符没对齐,光标多走一拍
|
|
|
- if (_notes[_notes.length - 1]?.isDouble && !currentVoiceEntries.length) {
|
|
|
- isMutileSubject = true
|
|
|
- }
|
|
|
- if (state.multitrack > 0 && !isDouble && isSetNextNoteReal) {
|
|
|
- isDouble = true;
|
|
|
- currentTime = differFrom
|
|
|
- isSetNextNoteReal = false
|
|
|
- differFrom = 0
|
|
|
- // iterator.currentMeasureIndex == 7 && console.log("🚀 ~ currentTime:",iterator.currentMeasure, currentTime)
|
|
|
- }
|
|
|
- currentTimes.push(iterator.currentTimeStamp.realValue - currentTimeStamp)
|
|
|
- currentTimeStamp = iterator.currentTimeStamp.realValue
|
|
|
- for (const v of voiceEntries) {
|
|
|
- const note = v.notes[0];
|
|
|
- note.fixedKey = note.ParentVoiceEntry.ParentVoice.Parent.SubInstruments[0].fixedKey || 0;
|
|
|
- // 有倚音
|
|
|
- if (note?.voiceEntry?.isGrace) {
|
|
|
- isDouble = true;
|
|
|
- let ns = [...iterator.currentVoiceEntries].reduce((notes, n) => {
|
|
|
- notes.push(...n.notes);
|
|
|
- return notes;
|
|
|
- }, []);
|
|
|
- ns = ns.sort((a: any, b: any) => b?.length?.realValue - a?.length?.realValue);
|
|
|
- currentTime = currentTime != 0 ? Math.min(ns?.[0]?.length?.realValue, currentTime) : ns?.[0]?.length?.realValue;
|
|
|
- }
|
|
|
- if (state.multitrack > 0 && currentTime > note.length.realValue) {
|
|
|
- // console.log(iterator.currentMeasureIndex, currentTime , note.length.realValue)
|
|
|
- currentTime = note.length.realValue
|
|
|
-
|
|
|
- }
|
|
|
- _notes.push({
|
|
|
- note,
|
|
|
- iterator: { ...iterator },
|
|
|
- currentTime,
|
|
|
- isDouble,
|
|
|
- isMutileSubject,
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- iterator.moveToNextVisibleVoiceEntry(false);
|
|
|
- }
|
|
|
- for (let { note, iterator, currentTime, isDouble,isMutileSubject } of _notes) {
|
|
|
- if (note) {
|
|
|
- if (si === 0) {
|
|
|
- allMeasures.push(note.sourceMeasure);
|
|
|
- }
|
|
|
- if (si === 0 && state.isSpecialBookCategory) {
|
|
|
- for (const expression of (note.sourceMeasure as SourceMeasure)?.TempoExpressions) {
|
|
|
- if (expression?.InstantaneousTempo?.beatUnit) {
|
|
|
- // 取最后一个有效的tempo
|
|
|
- // activeInstantaneousTempo = expression.InstantaneousTempo
|
|
|
- beatUnit = expression.InstantaneousTempo.beatUnit;
|
|
|
- // beatUnit = expression.InstantaneousTempo.beatUnit
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- let measureSpeed = note.sourceMeasure.tempoInBPM;
|
|
|
- const { metronomeNoteIndex } = iterator.currentMeasure;
|
|
|
- if (metronomeNoteIndex !== 0 && metronomeNoteIndex > si) {
|
|
|
- measureSpeed = allNotes[allNotes.length - 1]?.speed || 100;
|
|
|
- }
|
|
|
- const activeVerticalMeasureList = [note.sourceMeasure.verticalMeasureList?.[0]] || [];
|
|
|
- // console.log([...activeVerticalMeasureList])
|
|
|
- const { realValue } = iterator.currentTimeStamp;
|
|
|
- // console.log({...iterator}, i)
|
|
|
- const { RealValue: vRealValue, Denominator: vDenominator } = formatDuration(iterator.currentMeasure.activeTimeSignature, iterator.currentMeasure.duration);
|
|
|
- let { wholeValue, numerator, denominator, realValue: NoteRealValue } = note.length;
|
|
|
- if (i === 0 && ["2670"].includes(detailId)) {
|
|
|
- NoteRealValue = 0.03125;
|
|
|
- }
|
|
|
- if (isDouble && currentTime > 0) {
|
|
|
- if (currentTime != NoteRealValue) {
|
|
|
- console.log(`小节 ${note.sourceMeasure.MeasureNumberXML} 替换: noteLength: ${NoteRealValue}, 最小: ${currentTime}`);
|
|
|
- NoteRealValue = currentTime;
|
|
|
- }
|
|
|
- }
|
|
|
- // note.sourceMeasure.MeasureNumberXML === 8 && console.error(`小节 ${note.sourceMeasure.MeasureNumberXML}`, NoteRealValue)
|
|
|
- if (['12667', '12673'].includes(detailId)) {
|
|
|
- if (isMutileSubject && currentTimes[i + 1] > 0 && NoteRealValue > currentTimes[i + 1]) {
|
|
|
- NoteRealValue = currentTimes[i + 1]
|
|
|
- }
|
|
|
- }
|
|
|
- if (["12673"].includes(detailId) && route.query["part-index"] === "22" && i == 208) {
|
|
|
- console.log(note.sourceMeasure.MeasureNumberXML, i)
|
|
|
- NoteRealValue = 0.125
|
|
|
- }
|
|
|
- let relativeTime = usetime; //realValue * 4 * (60 / measureSpeed)
|
|
|
- // if (!useedmeasures.has(iterator.currentMeasureIndex)) {
|
|
|
- // usetime += vRealValue * 4 * (60 / measureSpeed)
|
|
|
- // }
|
|
|
- // useedmeasures.add(iterator.currentMeasureIndex)
|
|
|
- // console.table({
|
|
|
- // currentMeasureIndex: iterator.currentMeasureIndex,
|
|
|
- // i,
|
|
|
- // vRealValue,
|
|
|
- // measureSpeed,
|
|
|
- // usetime,
|
|
|
- // relativeTime,
|
|
|
- // })
|
|
|
- // console.log({...iterator}, {...iterator.currentTimeStamp})
|
|
|
- // console.log('relativeTime', relativeTime, 'realValue', realValue, 'baseSpeed', baseSpeed)
|
|
|
- // 速度不能为0
|
|
|
- // 此处的速度应该是按照设置的速度而不是校准后的速度,否则mp3速度不对
|
|
|
- let beatSpeed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
- // if(['2590'].includes(detailId)){
|
|
|
- // beatSpeed = 160
|
|
|
- // }
|
|
|
- // console.log(getTimeByBeatUnit(beatUnit, measureSpeed, iterator.currentMeasure.activeTimeSignature.Denominator))
|
|
|
- let gradualLength = 0;
|
|
|
- // console.log("metronomeNoteIndex", i, iterator.currentMeasure.metronomeNoteIndex)
|
|
|
- let speed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
- gradualChange = iterator.currentMeasure.speedInfo || gradualChange;
|
|
|
- gradualSpeed = osmd.sheet.soundTempos?.get(note.sourceMeasure.measureListIndex) || gradualSpeed;
|
|
|
- if (!gradualSpeed || gradualSpeed.length < 2) {
|
|
|
- gradualSpeed = createSpeedInfo(gradualChange, speed);
|
|
|
- }
|
|
|
- const measureListIndex = iterator.currentMeasure.measureListIndex;
|
|
|
- // 计算相差时间按照比例分配到所有音符上
|
|
|
- if (state.gradualTimes && Object.keys(state.gradualTimes).length > 0) {
|
|
|
- const withInRangeNote = state.gradual.find((item, index) => {
|
|
|
- const nextItem: any = state.gradual[index + 1];
|
|
|
- return item[0].measureIndex <= measureListIndex && item[1]?.measureIndex! >= measureListIndex && (!nextItem || nextItem?.[0].measureIndex !== measureListIndex);
|
|
|
- });
|
|
|
- if (!withInRangeNote) {
|
|
|
- useGradualTime = 0;
|
|
|
- }
|
|
|
- const [first, last] = withInRangeNote || [];
|
|
|
- if (first && last) {
|
|
|
- // 小节数量
|
|
|
- const continuous = last.measureIndex - first.measureIndex;
|
|
|
- // 开始小节内
|
|
|
- const inTheFirstMeasure = first.closedMeasureIndex == measureListIndex && si >= first.noteInMeasureIndex;
|
|
|
- // 结束小节内
|
|
|
- const inTheLastMeasure = last.closedMeasureIndex === measureListIndex && si < last.noteInMeasureIndex;
|
|
|
- // 范围内小节
|
|
|
- const inFiestOrLastMeasure = first.closedMeasureIndex !== measureListIndex && last.closedMeasureIndex !== measureListIndex;
|
|
|
- if (inTheFirstMeasure || inTheLastMeasure || inFiestOrLastMeasure) {
|
|
|
- const startTime = state.gradualTimes[first.measureIndex];
|
|
|
- const endTime = state.gradualTimes[last.measureIndex];
|
|
|
- if (startTime && endTime) {
|
|
|
- const times = continuous - first.leftDuration / first.allDuration + last.leftDuration / last.allDuration;
|
|
|
- const diff = dayjs(tranTime(endTime)).diff(dayjs(tranTime(startTime)), "millisecond");
|
|
|
- gradualLength = ((NoteRealValue / vRealValue / times) * diff) / 1000;
|
|
|
- useGradualTime += gradualLength;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else if (gradualChange && gradualSpeed && (gradualChange.startXmlNoteIndex === si || gradualChangeIndex > 0)) {
|
|
|
- // const tmpNoteLength = (wholeValue + numerator / denominator) * vDenominator * (60 / beatSpeed)
|
|
|
- // const tmpMeasureLength = vRealValue * 4 * (60 / beatSpeed)
|
|
|
- const startSpeed = gradualSpeed[0] - (gradualSpeed[1] - gradualSpeed[0]);
|
|
|
- // console.log((gradualSpeed[gradualSpeed.length - 1] - startSpeed) * tmpNoteLength/tmpMeasureLength)
|
|
|
- // console.log(gradualChange, gradualSpeed, startSpeed, gradualChange.startXmlNoteIndex, si, gradualChangeIndex)
|
|
|
- // gradualChangeIndex = 0
|
|
|
- const { resetXmlNoteIndex, endXmlNoteIndex } = gradualChange;
|
|
|
- const noteDiff = endXmlNoteIndex;
|
|
|
- let stepSpeed = (gradualSpeed[gradualSpeed.length - 1] - startSpeed) / noteDiff;
|
|
|
- stepSpeed = note.DotsXml ? stepSpeed / 1.5 : stepSpeed;
|
|
|
- // console.log(gradualChangeIndex, stepSpeed, stepSpeed * gradualChangeIndex, stepSpeed * (gradualChangeIndex + 1), noteDiff, resetXmlNoteIndex)
|
|
|
- if (gradualChangeIndex < noteDiff) {
|
|
|
- // stepSpeeds.push((gradualSpeed[gradualSpeed.length - 1] - startSpeed) * tmpNoteLength/tmpMeasureLength)
|
|
|
- // speed += Math.ceil((stepSpeed) * (gradualChangeIndex + 1))
|
|
|
- const tempSpeed = Math.ceil(speed + stepSpeed * gradualChangeIndex);
|
|
|
- let tmpSpeed = getTimeByBeatUnit(beatUnit, tempSpeed, iterator.currentMeasure.activeTimeSignature.Denominator);
|
|
|
- const maxLength = (wholeValue + numerator / denominator) * vDenominator * (60 / tmpSpeed);
|
|
|
- // speed += stepSpeeds.reduce((a, b) => a + b, 0)
|
|
|
- speed += Math.ceil(stepSpeed * (gradualChangeIndex + 1));
|
|
|
- tmpSpeed = getTimeByBeatUnit(beatUnit, speed, iterator.currentMeasure.activeTimeSignature.Denominator);
|
|
|
- const minLength = (wholeValue + numerator / denominator) * vDenominator * (60 / tmpSpeed);
|
|
|
- gradualLength = (maxLength + minLength) / 2;
|
|
|
- // console.table({maxLength, minLength, gradualLength, tempSpeed, speed, tmpSpeed, dot: note.DotsXml})
|
|
|
- } else if (resetXmlNoteIndex > gradualChangeIndex) {
|
|
|
- speed = allNotes[i - 1]?.speed;
|
|
|
- // console.log('resetXmlNoteIndex', resetXmlNoteIndex, 'gradualChangeIndex', gradualChangeIndex, allNotes[i -1]?.speed)
|
|
|
- }
|
|
|
- beatSpeed = (state.isSpecialBookCategory ? getTimeByBeatUnit(beatUnit, speed, iterator.currentMeasure.activeTimeSignature.Denominator) : baseSpeed) || 1;
|
|
|
- const isEnd = !(gradualChangeIndex < noteDiff) && !(resetXmlNoteIndex > gradualChangeIndex);
|
|
|
- gradualChangeIndex++;
|
|
|
- // console.log(gradualChangeIndex)
|
|
|
- if (isEnd) {
|
|
|
- gradualChangeIndex = 0;
|
|
|
- gradualChange = undefined;
|
|
|
- gradualSpeed = undefined;
|
|
|
- stepSpeeds = [];
|
|
|
- }
|
|
|
- }
|
|
|
- if (i === 0) {
|
|
|
- // console.log(getFixTime(speed))
|
|
|
- // if(['2590'].includes(detailId)){
|
|
|
- // fixtime += getFixTime(90)
|
|
|
- // } else {
|
|
|
- fixtime += getFixTime(beatSpeed);
|
|
|
- // }
|
|
|
- }
|
|
|
- // console.log(speed, beatSpeed)
|
|
|
- // const vDenominator = 8
|
|
|
- // console.log(NoteRealValue)
|
|
|
- // console.log(activeInstantaneousTempo)
|
|
|
- // console.log({vDenominator, NoteRealValue, denominator, numerator, wholeValue, realValue, vRealValue, measureSpeed, speed, beatSpeed})
|
|
|
- // console.log(gradualLength)
|
|
|
- let noteLength = gradualLength ? gradualLength : Math.min(vRealValue, NoteRealValue) * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
|
- const measureLength = vRealValue * vDenominator * (60 / beatSpeed);
|
|
|
- // console.table({value: iterator.currentTimeStamp.realValue, vRealValue,NoteRealValue, noteLength,measureLength, MeasureNumberXML: note.sourceMeasure.MeasureNumberXML})
|
|
|
- usetime += noteLength;
|
|
|
- relaMeasureLength += noteLength;
|
|
|
- let relaEndtime = noteLength + relativeTime;
|
|
|
- const fixedKey = note.fixedKey || 0;
|
|
|
- const svgElelent = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables[si];
|
|
|
- // console.log(note.sourceMeasure.MeasureNumberXML,note,svgElelent, NoteRealValue, measureLength)
|
|
|
- if (allNotes.length && allNotes[allNotes.length - 1].relativeTime === relativeTime) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- // console.log(iterator.currentMeasure)
|
|
|
- // 如果是弱起就补齐缺省的时长
|
|
|
- if (i === 0) {
|
|
|
- const diff = getMeasureDurationDiff(iterator.currentMeasure);
|
|
|
- if (diff > 0) {
|
|
|
- difftime = diff * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
|
- fixtime += difftime;
|
|
|
- }
|
|
|
- // diff获取不准确时, 弱起补齐
|
|
|
- if (["2589", "2561", "2560", "2559", "2558", "2556", "2555", "2554"].includes(detailId)) {
|
|
|
- difftime = iterator.currentTimeStamp.realValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
|
- fixtime += difftime;
|
|
|
- }
|
|
|
- }
|
|
|
- // console.log(note.tie)
|
|
|
- const nodeDetail = {
|
|
|
- difftime,
|
|
|
- octaveOffset: activeVerticalMeasureList[0]?.octaveOffset,
|
|
|
- frequency: note.pitch?.frequency,
|
|
|
- speed,
|
|
|
- beatSpeed,
|
|
|
- i,
|
|
|
- si,
|
|
|
- stepSpeeds,
|
|
|
- measureOpenIndex: allMeasures.length - 1,
|
|
|
- measures,
|
|
|
- tempoInBPM: note.sourceMeasure.tempoInBPM,
|
|
|
- measureLength,
|
|
|
- relaMeasureLength,
|
|
|
- id: svgElelent?.attrs.id,
|
|
|
- note: note.halfTone + 12, // see issue #224
|
|
|
- relativeTime: retain(relativeTime),
|
|
|
- time: retain(relativeTime + fixtime),
|
|
|
- endtime: retain(relaEndtime + fixtime),
|
|
|
- relaEndtime: retain(relaEndtime),
|
|
|
- realValue,
|
|
|
- halfTone: note.halfTone,
|
|
|
- noteElement: note,
|
|
|
- svgElelent,
|
|
|
- fixedKey,
|
|
|
- realKey: 0,
|
|
|
- duration: 0,
|
|
|
- formatLyricsEntries: formatLyricsEntries(note),
|
|
|
- stave: activeVerticalMeasureList[0]?.stave,
|
|
|
- firstVerticalMeasure: activeVerticalMeasureList[0],
|
|
|
- noteLength: 1,
|
|
|
- osdmContext: osmd,
|
|
|
- speedbeatUnit: beatUnit,
|
|
|
- };
|
|
|
- nodeDetail.realKey = formatRealKey(note.halfTone - fixedKey * 12, nodeDetail);
|
|
|
- nodeDetail.duration = nodeDetail.endtime - nodeDetail.time;
|
|
|
- // iterator.currentMeasureIndex == 61 && console.log("🚀 ~ differFrom:",svgElelent, note)
|
|
|
- // if (nodeDetail.id && allNoteId.includes(nodeDetail.id)) {
|
|
|
- // continue
|
|
|
- // }
|
|
|
- // console.log(nodeDetail.noteElement.sourceMeasure?.staffLinkedExpressions)
|
|
|
- let tickables = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables || [];
|
|
|
- if ([121].includes(state.subjectId)){
|
|
|
- tickables= note.sourceMeasure.verticalSourceStaffEntryContainers
|
|
|
- }
|
|
|
- // console.log(note.sourceMeasure.MeasureNumberXML, note.sourceMeasure.verticalSourceStaffEntryContainers.length)
|
|
|
- nodeDetail.noteLength = tickables.length || 1;
|
|
|
- allNotes.push(nodeDetail);
|
|
|
- allNoteId.push(nodeDetail.id);
|
|
|
- measures.push(nodeDetail);
|
|
|
- if (si < tickables.length - 1) {
|
|
|
- si++;
|
|
|
- } else {
|
|
|
- // usetime += measureLength - relaMeasureLength
|
|
|
- // console.log(relaMeasureLength, measureLength)
|
|
|
- si = 0;
|
|
|
- relaMeasureLength = 0;
|
|
|
- measures = [];
|
|
|
- }
|
|
|
- }
|
|
|
- i++;
|
|
|
- }
|
|
|
- }
|
|
|
- // 按照时间轴排序
|
|
|
- const sortArray = allNotes.sort((a, b) => a.relativeTime - b.relativeTime).map((item, index) => ({ ...item, i: index }));
|
|
|
- // for (let i = 0; i < sortArray.length; i++) {
|
|
|
- // const note = { ...sortArray[i] }
|
|
|
- // const prevNote = sortArray[i - 1]
|
|
|
- // const tieNote =
|
|
|
- // note?.noteElement?.tie?.notes.map(
|
|
|
- // (_n: any) => _n.NoteToGraphicalNoteObjectId
|
|
|
- // ) || []
|
|
|
- // const isNotNeedStop =
|
|
|
- // note.noteElement.tie &&
|
|
|
- // prevNote?.noteElement.tie &&
|
|
|
- // note.halfTone === prevNote?.halfTone &&
|
|
|
- // tieNote.includes(prevNote?.noteElement?.NoteToGraphicalNoteObjectId)
|
|
|
- // const isOvertone = false //note.noteElement.slurs.length && note.noteElement.slurs[0].endNote === note.noteElement
|
|
|
- // if (prevNote) {
|
|
|
- // if (isNotNeedStop || isOvertone) {
|
|
|
- // note.sourceStartTime = note.time
|
|
|
- // note.sourceRelativeTime = note.relativeTime
|
|
|
- // note.sourceRealValue = note.realValue
|
|
|
- // note.sourceEndTime = note.endtime
|
|
|
- // note.sourceRelaEndtime = note.relaEndtime
|
|
|
- // note.relativeTime = prevNote.relativeTime
|
|
|
- // note.realValue = prevNote.realValue
|
|
|
- // note.time = prevNote.time
|
|
|
- // note.endtime = prevNote.endtime
|
|
|
- // note.relaEndtime = prevNote.relaEndtime
|
|
|
- // }
|
|
|
- // // 此处会导致休止符继续上一个音的指法
|
|
|
- // if (note.halfTone === 0) {
|
|
|
- // note.realKey = prevNote.realKey
|
|
|
- // }
|
|
|
- // }
|
|
|
- // sortArray[i] = note
|
|
|
- // }
|
|
|
- return sortArray;
|
|
|
-};
|
|
|
-
|
|
|
export const getAllNoteElements = (osmd: any) => {
|
|
|
const list: any[] = [];
|
|
|
const listById: {
|
|
@@ -711,7 +296,7 @@ export const getBoundingBoxByverticalNote = (note: any) => {
|
|
|
measures = !measures || !measures[0] ? note?.noteElement?.isRestFlag && getPrevHasSourceNote(note)?.noteElement?.sourceMeasure?.verticalMeasureList : measures;
|
|
|
let height = 0;
|
|
|
if (measures) {
|
|
|
- const firstMeasure = measures[runtime.partIndex];
|
|
|
+ const firstMeasure = measures[state.partIndex];
|
|
|
for (let index = 0; index < measures.length; index++) {
|
|
|
const measure = measures[index];
|
|
|
if (measure?.stave) {
|
|
@@ -763,7 +348,6 @@ export type Duration = FractionDefault & {
|
|
|
|
|
|
export const getDuration = (osmd?: OpenSheetMusicDisplay): Duration => {
|
|
|
if (osmd) {
|
|
|
- // console.log(osmd.GraphicSheet.MeasureList[0][0]?.parentSourceMeasure)
|
|
|
const { Duration, TempoInBPM, ActiveTimeSignature, TempoExpressions } = osmd.GraphicSheet.MeasureList[0][0]?.parentSourceMeasure;
|
|
|
if (Duration) {
|
|
|
let beatUnit = "quarter";
|
|
@@ -921,44 +505,6 @@ export const setPrefix = (url: string): string => {
|
|
|
return "";
|
|
|
};
|
|
|
|
|
|
-export const formatXML = (xml: string): string => {
|
|
|
- if (!xml) return "";
|
|
|
- const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
|
|
|
- const measures = xmlParse.getElementsByTagName("measure");
|
|
|
- // let speed = -1
|
|
|
- let beats = -1;
|
|
|
- let beatType = -1;
|
|
|
- // 小节中如果没有节点默认为休止符
|
|
|
- for (const measure of measures) {
|
|
|
- if (beats === -1 && measure.getElementsByTagName("beats").length) {
|
|
|
- beats = parseInt(measure.getElementsByTagName("beats")[0].textContent || "4");
|
|
|
- }
|
|
|
- if (beatType === -1 && measure.getElementsByTagName("beat-type").length) {
|
|
|
- beatType = parseInt(measure.getElementsByTagName("beat-type")[0].textContent || "4");
|
|
|
- }
|
|
|
- // if (speed === -1 && measure.getElementsByTagName('per-minute').length) {
|
|
|
- // speed = parseInt(measure.getElementsByTagName('per-minute')[0].textContent || this.firstLib?.speed)
|
|
|
- // }
|
|
|
- const divisions = parseInt(measure.getElementsByTagName("divisions")[0]?.textContent || "256");
|
|
|
- if (measure.getElementsByTagName("note").length === 0) {
|
|
|
- const forwardTimeElement = measure.getElementsByTagName("forward")[0]?.getElementsByTagName("duration")[0];
|
|
|
- if (forwardTimeElement) {
|
|
|
- forwardTimeElement.textContent = "0";
|
|
|
- }
|
|
|
- measure.innerHTML =
|
|
|
- measure.innerHTML +
|
|
|
- `
|
|
|
- <note>
|
|
|
- <rest measure="yes"/>
|
|
|
- <duration>${divisions * beats}</duration>
|
|
|
- <voice>1</voice>
|
|
|
- <type>whole</type>
|
|
|
- </note>`;
|
|
|
- }
|
|
|
- }
|
|
|
- return new XMLSerializer().serializeToString(xmlParse);
|
|
|
-};
|
|
|
-
|
|
|
export type CustomInfo = {
|
|
|
showSpeed: boolean;
|
|
|
parsedXML: string;
|
|
@@ -971,7 +517,7 @@ export const getCustomInfo = (xml: string): CustomInfo => {
|
|
|
parsedXML: xml,
|
|
|
};
|
|
|
const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
|
|
|
- const words = xmlParse.getElementsByTagName("words");
|
|
|
+ const words: any = xmlParse.getElementsByTagName("words");
|
|
|
for (const word of words) {
|
|
|
if (word && word.textContent?.trim() === "隐藏速度") {
|
|
|
data.showSpeed = false;
|
|
@@ -989,7 +535,7 @@ export const getCustomInfo = (xml: string): CustomInfo => {
|
|
|
* 替换文本标签中的内容
|
|
|
*/
|
|
|
const replaceTextConent = (beforeText: string, afterText: string, ele: Element): Element => {
|
|
|
- const words = ele?.getElementsByTagName("words");
|
|
|
+ const words: any = ele?.getElementsByTagName("words");
|
|
|
for (const word of words) {
|
|
|
if (word && word.textContent?.trim() === beforeText) {
|
|
|
word.textContent = afterText;
|
|
@@ -1055,23 +601,7 @@ export const isRepeatWord = (text: string): boolean => {
|
|
|
const dsRegEx: string = "d\\s?\\.s\\.";
|
|
|
const dcRegEx: string = "d\\.\\s?c\\.";
|
|
|
|
|
|
- return (
|
|
|
- innerText === "@" ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, dsRegEx + " al fine", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, dsRegEx + " al coda", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, dcRegEx + " al fine", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, dcRegEx + " al coda", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, dcRegEx) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "da\\s?capo", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, dsRegEx, true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "dal\\s?segno", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "al\\s?coda", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "to\\s?coda", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "a (la )?coda", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "fine", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "coda", true) ||
|
|
|
- StringUtil.StringContainsSeparatedWord(innerText, "segno", true)
|
|
|
- );
|
|
|
+ return innerText === "@" || StringUtil.StringContainsSeparatedWord(innerText, dsRegEx + " al fine", true) || StringUtil.StringContainsSeparatedWord(innerText, dsRegEx + " al coda", true) || StringUtil.StringContainsSeparatedWord(innerText, dcRegEx + " al fine", true) || StringUtil.StringContainsSeparatedWord(innerText, dcRegEx + " al coda", true) || StringUtil.StringContainsSeparatedWord(innerText, dcRegEx) || StringUtil.StringContainsSeparatedWord(innerText, "da\\s?capo", true) || StringUtil.StringContainsSeparatedWord(innerText, dsRegEx, true) || StringUtil.StringContainsSeparatedWord(innerText, "dal\\s?segno", true) || StringUtil.StringContainsSeparatedWord(innerText, "al\\s?coda", true) || StringUtil.StringContainsSeparatedWord(innerText, "to\\s?coda", true) || StringUtil.StringContainsSeparatedWord(innerText, "a (la )?coda", true) || StringUtil.StringContainsSeparatedWord(innerText, "fine", true) || StringUtil.StringContainsSeparatedWord(innerText, "coda", true) || StringUtil.StringContainsSeparatedWord(innerText, "segno", true);
|
|
|
}
|
|
|
return false;
|
|
|
};
|
|
@@ -1082,7 +612,7 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
|
const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
|
|
|
const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || [];
|
|
|
const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0].textContent || "");
|
|
|
- const parts = xmlParse.getElementsByTagName("part");
|
|
|
+ const parts: any = xmlParse.getElementsByTagName("part");
|
|
|
// const firstTimeInfo = parts[0]?.getElementsByTagName('metronome')[0]?.parentElement?.parentElement?.cloneNode(true)
|
|
|
const firstMeasures = [...parts[0]?.getElementsByTagName("measure")];
|
|
|
const metronomes = [...parts[0]?.getElementsByTagName("metronome")];
|
|
@@ -1100,7 +630,7 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
|
state.partListNames = partListNames;
|
|
|
if (visiblePartInfo) {
|
|
|
const id = visiblePartInfo.getAttribute("id");
|
|
|
- Array.from(parts).forEach((part) => {
|
|
|
+ Array.from(parts).forEach((part: any) => {
|
|
|
if (part && part.getAttribute("id") !== id) {
|
|
|
part.parentNode?.removeChild(part);
|
|
|
// 不等于第一行才添加避免重复添加
|
|
@@ -1118,8 +648,8 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
|
}
|
|
|
}
|
|
|
Object.values(metronomeData).forEach((metronome) => {
|
|
|
- const metronomeContainer = metronome.parentElement?.parentElement;
|
|
|
- const parentMeasure = metronomeContainer?.parentElement;
|
|
|
+ const metronomeContainer: any = metronome.parentElement?.parentElement;
|
|
|
+ const parentMeasure: any = metronomeContainer?.parentElement;
|
|
|
const measureMetronomes = [...(parentMeasure?.childNodes || [])];
|
|
|
const metronomesIndex = metronomeContainer ? measureMetronomes.indexOf(metronomeContainer) : -1;
|
|
|
// console.log(parentMeasure)
|
|
@@ -1148,7 +678,7 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
|
// 找当前小节是否包含word标签
|
|
|
const _words = Array.from(activeMeasure?.getElementsByTagName("words") || []);
|
|
|
// 遍历word标签,检查是否和第一小节重复,如果有重复则不平移word
|
|
|
- const total = _words.reduce((total: any, _word) => {
|
|
|
+ const total = _words.reduce((total: any, _word: any) => {
|
|
|
if (_word.textContent?.includes(text)) {
|
|
|
total++;
|
|
|
}
|
|
@@ -1256,7 +786,7 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
|
// 倚音后连音线
|
|
|
export const appoggianceFormate = (xmlParse: Document): Document => {
|
|
|
if (!xmlParse) return xmlParse;
|
|
|
- const graces = xmlParse.querySelectorAll("grace");
|
|
|
+ const graces: any = xmlParse.querySelectorAll("grace");
|
|
|
if (!graces.length) return xmlParse;
|
|
|
const getNextElement = (el: HTMLElement): HTMLElement => {
|
|
|
if (el.querySelector("grace")) {
|
|
@@ -1285,7 +815,8 @@ export const appoggianceFormate = (xmlParse: Document): Document => {
|
|
|
};
|
|
|
|
|
|
export const getVoicePartInfo = () => {
|
|
|
- const { MusicalInstrumentClassification, chinesePartName } = appState;
|
|
|
+ // const { MusicalInstrumentClassification, chinesePartName } = appState;
|
|
|
+ const { MusicalInstrumentClassification, chinesePartName } = { MusicalInstrumentClassification: {}, chinesePartName: [] as any[] };
|
|
|
|
|
|
let subjectId = -1;
|
|
|
const { partListNames, partIndex } = state;
|
|
@@ -1293,7 +824,7 @@ export const getVoicePartInfo = () => {
|
|
|
if (filterPartNames.length) {
|
|
|
for (const Classification of Object.entries(MusicalInstrumentClassification)) {
|
|
|
const [key, value] = Classification as [string, string[]];
|
|
|
- const activePart = partListNames[partIndex];
|
|
|
+ const activePart: any = partListNames[partIndex];
|
|
|
// console.log({activePart, value, partListNames})
|
|
|
const filterValue = value.filter((item) => item && (activePart || "").indexOf(item || "") > -1);
|
|
|
if (activePart && (filterValue.length || value.includes(activePart))) {
|
|
@@ -1400,3 +931,424 @@ export const getNotesByid = (id: string): NoteList => {
|
|
|
const notes = new NoteList(state.times.filter((item) => item.id === id));
|
|
|
return notes;
|
|
|
};
|
|
|
+
|
|
|
+/** 格式化当前曲谱缩放比例 */
|
|
|
+export const formatZoom = (num = 1) => {
|
|
|
+ return num * state.zoom;
|
|
|
+};
|
|
|
+
|
|
|
+/** 格式化曲谱
|
|
|
+ * 1.全休止符的小节,没有音符默认加个全休止符
|
|
|
+ */
|
|
|
+export const formatXML = (xml: string): string => {
|
|
|
+ if (!xml) return "";
|
|
|
+ const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
|
|
|
+ const measures = Array.from(xmlParse.getElementsByTagName("measure"));
|
|
|
+ // let speed = -1
|
|
|
+ let beats = -1;
|
|
|
+ let beatType = -1;
|
|
|
+ // 小节中如果没有节点默认为休止符
|
|
|
+ for (const measure of measures) {
|
|
|
+ if (beats === -1 && measure.getElementsByTagName("beats").length) {
|
|
|
+ beats = parseInt(measure.getElementsByTagName("beats")[0].textContent || "4");
|
|
|
+ }
|
|
|
+ if (beatType === -1 && measure.getElementsByTagName("beat-type").length) {
|
|
|
+ beatType = parseInt(measure.getElementsByTagName("beat-type")[0].textContent || "4");
|
|
|
+ }
|
|
|
+ // if (speed === -1 && measure.getElementsByTagName('per-minute').length) {
|
|
|
+ // speed = parseInt(measure.getElementsByTagName('per-minute')[0].textContent || this.firstLib?.speed)
|
|
|
+ // }
|
|
|
+ const divisions = parseInt(measure.getElementsByTagName("divisions")[0]?.textContent || "256");
|
|
|
+ if (measure.getElementsByTagName("note").length === 0) {
|
|
|
+ const forwardTimeElement = measure.getElementsByTagName("forward")[0]?.getElementsByTagName("duration")[0];
|
|
|
+ if (forwardTimeElement) {
|
|
|
+ forwardTimeElement.textContent = "0";
|
|
|
+ }
|
|
|
+ measure.innerHTML =
|
|
|
+ measure.innerHTML +
|
|
|
+ `
|
|
|
+ <note>
|
|
|
+ <rest measure="yes"/>
|
|
|
+ <duration>${divisions * beats}</duration>
|
|
|
+ <voice>1</voice>
|
|
|
+ <type>whole</type>
|
|
|
+ </note>`;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return new XMLSerializer().serializeToString(xmlParse);
|
|
|
+};
|
|
|
+
|
|
|
+export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
+ const detailId = state.examSongId?.toString();
|
|
|
+ const partIndex = state.partIndex + "";
|
|
|
+ let fixtime = browserInfo.huawei ? 0.08 : 0; //getFixTime()
|
|
|
+ const allNotes: any[] = [];
|
|
|
+ const allNoteId: string[] = [];
|
|
|
+ const allMeasures: any[] = [];
|
|
|
+ const { baseSpeed = 90 } = state;
|
|
|
+ const formatRealKey = (realKey: number, detail: any) => {
|
|
|
+ // 长笛的LEVEL 2-5-1条练习是泛音练习,以每小节第一个音的指法为准,高音不变变指法。
|
|
|
+ const olnyOneIds = ["906"];
|
|
|
+ if (olnyOneIds.includes(detailId)) {
|
|
|
+ return detail.measures[0]?.realKey || realKey;
|
|
|
+ }
|
|
|
+ // 圆号的LEVEL 2-5条练习是泛音练习,最后四小节指法以连音线第一个小节为准
|
|
|
+ const olnyOneIds2 = ["782", "784"];
|
|
|
+ if (olnyOneIds2.includes(detailId)) {
|
|
|
+ const measureNumbers = [14, 16, 30, 32];
|
|
|
+ if (measureNumbers.includes(detail.firstVerticalMeasure?.measureNumber)) {
|
|
|
+ return allNotes[allNotes.length - 1]?.realKey || realKey;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 2-6 第三小节指法按照第一个音符显示
|
|
|
+ const filterIds = ["900", "901", "640", "641", "739", "740", "800", "801", "773", "774", "869", "872", "714", "715"];
|
|
|
+ if (filterIds.includes(detailId)) {
|
|
|
+ if (detail.firstVerticalMeasure?.measureNumber === 3 || detail.firstVerticalMeasure?.measureNumber === 9) {
|
|
|
+ return detail.measures[0]?.realKey || realKey;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return realKey;
|
|
|
+ };
|
|
|
+ if (!osmd.cursor) return [];
|
|
|
+ try {
|
|
|
+ // osmd.cursor.reset();
|
|
|
+ } catch (error) {}
|
|
|
+ const iterator: any = osmd.cursor.Iterator;
|
|
|
+ // console.log("🚀 ~ iterator:", iterator)
|
|
|
+ console.time("音符跑完时间");
|
|
|
+
|
|
|
+ let i = 0;
|
|
|
+ let si = 0;
|
|
|
+ let measures: any[] = [];
|
|
|
+ let stepSpeeds: number[] = [];
|
|
|
+ /** 弱起时间 */
|
|
|
+ let difftime = 0;
|
|
|
+ let usetime = 0;
|
|
|
+ let useGradualTime = 0;
|
|
|
+ let relaMeasureLength = 0;
|
|
|
+ let beatUnit = "quarter";
|
|
|
+ let gradualSpeed;
|
|
|
+ let gradualChange: GradualChange | undefined;
|
|
|
+ let gradualChangeIndex = 0;
|
|
|
+ const _notes = [] as any[];
|
|
|
+
|
|
|
+ if (state.gradualTimes) {
|
|
|
+ if (["12280"].includes(detailId) && ["24"].includes(partIndex)) {
|
|
|
+ state.gradualTimes["8"] = "00:25:63";
|
|
|
+ state.gradualTimes["66"] = "01:53:35";
|
|
|
+ state.gradualTimes["90"] = "02:41:40";
|
|
|
+ }
|
|
|
+ console.log("合奏速度", state.gradual, state.gradualTimes);
|
|
|
+ }
|
|
|
+
|
|
|
+ let currentTimeStamp = iterator.currentTimeStamp.RealValue;
|
|
|
+ const currentTimes = [] as any[];
|
|
|
+ let isSetNextNoteReal = false;
|
|
|
+ let differFrom = 0;
|
|
|
+ while (!iterator.EndReached) {
|
|
|
+ console.log({ ...iterator });
|
|
|
+ const voiceEntries = iterator.CurrentVoiceEntries?.[0] ? [iterator.CurrentVoiceEntries?.[0]] : [];
|
|
|
+ let currentVoiceEntries: any[] = [];
|
|
|
+ // 单声部多声轨
|
|
|
+ if (state.multitrack > 0) {
|
|
|
+ currentVoiceEntries = [...iterator.CurrentVoiceEntries];
|
|
|
+ } else {
|
|
|
+ currentVoiceEntries = [...iterator.CurrentVoiceEntries].filter((n) => {
|
|
|
+ return n && n?.ParentVoice?.VoiceId != 1;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ let currentTime = 0;
|
|
|
+ let isDouble = false;
|
|
|
+ let isMutileSubject = false;
|
|
|
+
|
|
|
+ // console.log(iterator.currentMeasureIndex, [...iterator.currentVoiceEntries])
|
|
|
+ // iterator.currentMeasureIndex == 45 && console.log(currentTimeStamp, [...iterator.currentVoiceEntries])
|
|
|
+ if (currentVoiceEntries.length && !isSetNextNoteReal) {
|
|
|
+ // iterator.currentMeasureIndex == 15 && console.log(iterator.currentMeasureIndex, isSetNextNoteReal)
|
|
|
+ isDouble = true;
|
|
|
+ let voiceNotes = [...iterator.CurrentVoiceEntries].reduce((notes, n) => {
|
|
|
+ notes.push(...n.Notes);
|
|
|
+ return notes;
|
|
|
+ }, [] as any);
|
|
|
+ voiceNotes = voiceNotes.sort((a: any, b: any) => a?.length?.realValue - b?.length?.realValue);
|
|
|
+ currentTime = voiceNotes?.[0]?.length?.realValue || 0;
|
|
|
+ // iterator.currentMeasureIndex == 15 && console.log("🚀 ~ currentTime:", currentTime)
|
|
|
+
|
|
|
+ if (state.multitrack > 0 && currentVoiceEntries.length === 2) {
|
|
|
+ const min = voiceNotes[0]?.length?.realValue || 0;
|
|
|
+ const max = voiceNotes[voiceNotes.length - 1]?.length?.realValue || 0;
|
|
|
+ differFrom = max - min;
|
|
|
+ isSetNextNoteReal = differFrom === 0 ? false : true;
|
|
|
+ // iterator.currentMeasureIndex == 15 && console.log("🚀 ~ differFrom:", differFrom, isSetNextNoteReal)
|
|
|
+ // console.log(iterator.currentMeasureIndex, min, max)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 多声部上下音符没对齐,光标多走一拍
|
|
|
+ if (_notes[_notes.length - 1]?.isDouble && !currentVoiceEntries.length) {
|
|
|
+ isMutileSubject = true;
|
|
|
+ }
|
|
|
+ if (state.multitrack > 0 && !isDouble && isSetNextNoteReal) {
|
|
|
+ isDouble = true;
|
|
|
+ currentTime = differFrom;
|
|
|
+ isSetNextNoteReal = false;
|
|
|
+ differFrom = 0;
|
|
|
+ // iterator.currentMeasureIndex == 7 && console.log("🚀 ~ currentTime:",iterator.currentMeasure, currentTime)
|
|
|
+ }
|
|
|
+ currentTimes.push(iterator.currentTimeStamp.realValue - currentTimeStamp);
|
|
|
+ currentTimeStamp = iterator.currentTimeStamp.realValue;
|
|
|
+ for (const v of voiceEntries) {
|
|
|
+ const note = v.notes[0];
|
|
|
+ note.fixedKey = note.ParentVoiceEntry.ParentVoice.Parent.SubInstruments[0].fixedKey || 0;
|
|
|
+ // 有倚音
|
|
|
+ if (note?.voiceEntry?.isGrace) {
|
|
|
+ isDouble = true;
|
|
|
+ let ns = [...iterator.currentVoiceEntries].reduce((notes, n) => {
|
|
|
+ notes.push(...n.notes);
|
|
|
+ return notes;
|
|
|
+ }, []);
|
|
|
+ ns = ns.sort((a: any, b: any) => b?.length?.realValue - a?.length?.realValue);
|
|
|
+ currentTime = currentTime != 0 ? Math.min(ns?.[0]?.length?.realValue, currentTime) : ns?.[0]?.length?.realValue;
|
|
|
+ }
|
|
|
+ if (state.multitrack > 0 && currentTime > note.length.realValue) {
|
|
|
+ // console.log(iterator.currentMeasureIndex, currentTime , note.length.realValue)
|
|
|
+ currentTime = note.length.realValue;
|
|
|
+ }
|
|
|
+ _notes.push({
|
|
|
+ note,
|
|
|
+ iterator: { ...iterator },
|
|
|
+ currentTime,
|
|
|
+ isDouble,
|
|
|
+ isMutileSubject,
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ iterator.moveToNextVisibleVoiceEntry(false);
|
|
|
+ }
|
|
|
+ for (let { note, iterator, currentTime, isDouble, isMutileSubject } of _notes) {
|
|
|
+ if (note) {
|
|
|
+ // console.log("🚀 ~ note:", note)
|
|
|
+ if (si === 0) {
|
|
|
+ allMeasures.push(note.sourceMeasure);
|
|
|
+ }
|
|
|
+ if (si === 0 && state.isSpecialBookCategory) {
|
|
|
+ for (const expression of (note.sourceMeasure as SourceMeasure)?.TempoExpressions) {
|
|
|
+ if (expression?.InstantaneousTempo?.beatUnit) {
|
|
|
+ // 取最后一个有效的tempo
|
|
|
+ // activeInstantaneousTempo = expression.InstantaneousTempo
|
|
|
+ beatUnit = expression.InstantaneousTempo.beatUnit;
|
|
|
+ // beatUnit = expression.InstantaneousTempo.beatUnit
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let measureSpeed = note.sourceMeasure.tempoInBPM;
|
|
|
+ const { metronomeNoteIndex } = iterator.currentMeasure;
|
|
|
+ if (metronomeNoteIndex !== 0 && metronomeNoteIndex > si) {
|
|
|
+ measureSpeed = allNotes[allNotes.length - 1]?.speed || 100;
|
|
|
+ }
|
|
|
+
|
|
|
+ const activeVerticalMeasureList = [note.sourceMeasure.verticalMeasureList?.[0]] || [];
|
|
|
+ // console.log([...activeVerticalMeasureList])
|
|
|
+ const { realValue } = iterator.currentTimeStamp;
|
|
|
+ // console.log({...iterator}, i)
|
|
|
+ const { RealValue: vRealValue, Denominator: vDenominator } = formatDuration(iterator.currentMeasure.activeTimeSignature, iterator.currentMeasure.duration);
|
|
|
+ let { wholeValue, numerator, denominator, realValue: NoteRealValue } = note.length;
|
|
|
+ if (i === 0 && ["2670"].includes(detailId)) {
|
|
|
+ NoteRealValue = 0.03125;
|
|
|
+ }
|
|
|
+ if (isDouble && currentTime > 0) {
|
|
|
+ if (currentTime != NoteRealValue) {
|
|
|
+ console.log(`小节 ${note.sourceMeasure.MeasureNumberXML} 替换: noteLength: ${NoteRealValue}, 最小: ${currentTime}`);
|
|
|
+ NoteRealValue = currentTime;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // note.sourceMeasure.MeasureNumberXML === 8 && console.error(`小节 ${note.sourceMeasure.MeasureNumberXML}`, NoteRealValue)
|
|
|
+ if (["12667", "12673"].includes(detailId)) {
|
|
|
+ if (isMutileSubject && currentTimes[i + 1] > 0 && NoteRealValue > currentTimes[i + 1]) {
|
|
|
+ NoteRealValue = currentTimes[i + 1];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (["12673"].includes(detailId) && ["22"].includes(partIndex) && i == 208) {
|
|
|
+ console.log(note.sourceMeasure.MeasureNumberXML, i);
|
|
|
+ NoteRealValue = 0.125;
|
|
|
+ }
|
|
|
+ let relativeTime = usetime;
|
|
|
+ // 速度不能为0 此处的速度应该是按照设置的速度而不是校准后的速度,否则mp3速度不对
|
|
|
+ let beatSpeed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
+ // 如果有节拍器,需要将节拍器的时间算出来
|
|
|
+ if (i === 0) {
|
|
|
+ fixtime += getFixTime(beatSpeed);
|
|
|
+ }
|
|
|
+ // console.log(getTimeByBeatUnit(beatUnit, measureSpeed, iterator.currentMeasure.activeTimeSignature.Denominator))
|
|
|
+ let gradualLength = 0;
|
|
|
+ let speed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
+ gradualChange = iterator.currentMeasure.speedInfo || gradualChange;
|
|
|
+ gradualSpeed = osmd.Sheet.SoundTempos?.get(note.sourceMeasure.measureListIndex) || gradualSpeed;
|
|
|
+ if (!gradualSpeed || gradualSpeed.length < 2) {
|
|
|
+ gradualSpeed = createSpeedInfo(gradualChange, speed);
|
|
|
+ }
|
|
|
+ const measureListIndex = iterator.currentMeasure.measureListIndex;
|
|
|
+ // 计算相差时间按照比例分配到所有音符上
|
|
|
+ if (state.gradualTimes && Object.keys(state.gradualTimes).length > 0) {
|
|
|
+ const withInRangeNote = state.gradual.find((item, index) => {
|
|
|
+ const nextItem: any = state.gradual[index + 1];
|
|
|
+ return item[0].measureIndex <= measureListIndex && item[1]?.measureIndex! >= measureListIndex && (!nextItem || nextItem?.[0].measureIndex !== measureListIndex);
|
|
|
+ });
|
|
|
+ if (!withInRangeNote) {
|
|
|
+ useGradualTime = 0;
|
|
|
+ }
|
|
|
+ const [first, last] = withInRangeNote || [];
|
|
|
+ if (first && last) {
|
|
|
+ // 小节数量
|
|
|
+ const continuous = last.measureIndex - first.measureIndex;
|
|
|
+ // 开始小节内
|
|
|
+ const inTheFirstMeasure = first.closedMeasureIndex == measureListIndex && si >= first.noteInMeasureIndex;
|
|
|
+ // 结束小节内
|
|
|
+ const inTheLastMeasure = last.closedMeasureIndex === measureListIndex && si < last.noteInMeasureIndex;
|
|
|
+ // 范围内小节
|
|
|
+ const inFiestOrLastMeasure = first.closedMeasureIndex !== measureListIndex && last.closedMeasureIndex !== measureListIndex;
|
|
|
+ if (inTheFirstMeasure || inTheLastMeasure || inFiestOrLastMeasure) {
|
|
|
+ const startTime = state.gradualTimes[first.measureIndex];
|
|
|
+ const endTime = state.gradualTimes[last.measureIndex];
|
|
|
+ if (startTime && endTime) {
|
|
|
+ const times = continuous - first.leftDuration / first.allDuration + last.leftDuration / last.allDuration;
|
|
|
+ const diff = dayjs(tranTime(endTime)).diff(dayjs(tranTime(startTime)), "millisecond");
|
|
|
+ gradualLength = ((NoteRealValue / vRealValue / times) * diff) / 1000;
|
|
|
+ useGradualTime += gradualLength;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (gradualChange && gradualSpeed && (gradualChange.startXmlNoteIndex === si || gradualChangeIndex > 0)) {
|
|
|
+ // const tmpNoteLength = (wholeValue + numerator / denominator) * vDenominator * (60 / beatSpeed)
|
|
|
+ // const tmpMeasureLength = vRealValue * 4 * (60 / beatSpeed)
|
|
|
+ const startSpeed = gradualSpeed[0] - (gradualSpeed[1] - gradualSpeed[0]);
|
|
|
+ // console.log((gradualSpeed[gradualSpeed.length - 1] - startSpeed) * tmpNoteLength/tmpMeasureLength)
|
|
|
+ // console.log(gradualChange, gradualSpeed, startSpeed, gradualChange.startXmlNoteIndex, si, gradualChangeIndex)
|
|
|
+ // gradualChangeIndex = 0
|
|
|
+ const { resetXmlNoteIndex, endXmlNoteIndex } = gradualChange;
|
|
|
+ const noteDiff = endXmlNoteIndex;
|
|
|
+ let stepSpeed = (gradualSpeed[gradualSpeed.length - 1] - startSpeed) / noteDiff;
|
|
|
+ stepSpeed = note.DotsXml ? stepSpeed / 1.5 : stepSpeed;
|
|
|
+ // console.log(gradualChangeIndex, stepSpeed, stepSpeed * gradualChangeIndex, stepSpeed * (gradualChangeIndex + 1), noteDiff, resetXmlNoteIndex)
|
|
|
+ if (gradualChangeIndex < noteDiff) {
|
|
|
+ // stepSpeeds.push((gradualSpeed[gradualSpeed.length - 1] - startSpeed) * tmpNoteLength/tmpMeasureLength)
|
|
|
+ // speed += Math.ceil((stepSpeed) * (gradualChangeIndex + 1))
|
|
|
+ const tempSpeed = Math.ceil(speed + stepSpeed * gradualChangeIndex);
|
|
|
+ let tmpSpeed = getTimeByBeatUnit(beatUnit, tempSpeed, iterator.currentMeasure.activeTimeSignature.Denominator);
|
|
|
+ const maxLength = (wholeValue + numerator / denominator) * vDenominator * (60 / tmpSpeed);
|
|
|
+ // speed += stepSpeeds.reduce((a, b) => a + b, 0)
|
|
|
+ speed += Math.ceil(stepSpeed * (gradualChangeIndex + 1));
|
|
|
+ tmpSpeed = getTimeByBeatUnit(beatUnit, speed, iterator.currentMeasure.activeTimeSignature.Denominator);
|
|
|
+ const minLength = (wholeValue + numerator / denominator) * vDenominator * (60 / tmpSpeed);
|
|
|
+ gradualLength = (maxLength + minLength) / 2;
|
|
|
+ // console.table({maxLength, minLength, gradualLength, tempSpeed, speed, tmpSpeed, dot: note.DotsXml})
|
|
|
+ } else if (resetXmlNoteIndex > gradualChangeIndex) {
|
|
|
+ speed = allNotes[i - 1]?.speed;
|
|
|
+ // console.log('resetXmlNoteIndex', resetXmlNoteIndex, 'gradualChangeIndex', gradualChangeIndex, allNotes[i -1]?.speed)
|
|
|
+ }
|
|
|
+ beatSpeed = (state.isSpecialBookCategory ? getTimeByBeatUnit(beatUnit, speed, iterator.currentMeasure.activeTimeSignature.Denominator) : baseSpeed) || 1;
|
|
|
+ const isEnd = !(gradualChangeIndex < noteDiff) && !(resetXmlNoteIndex > gradualChangeIndex);
|
|
|
+ gradualChangeIndex++;
|
|
|
+ // console.log(gradualChangeIndex)
|
|
|
+ if (isEnd) {
|
|
|
+ gradualChangeIndex = 0;
|
|
|
+ gradualChange = undefined;
|
|
|
+ gradualSpeed = undefined;
|
|
|
+ stepSpeeds = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // console.log(speed, beatSpeed)
|
|
|
+ // const vDenominator = 8
|
|
|
+ // console.log(NoteRealValue)
|
|
|
+ // console.log(activeInstantaneousTempo)
|
|
|
+ // console.log({vDenominator, NoteRealValue, denominator, numerator, wholeValue, realValue, vRealValue, measureSpeed, speed, beatSpeed})
|
|
|
+ // console.log(gradualLength)
|
|
|
+ let noteLength = gradualLength ? gradualLength : Math.min(vRealValue, NoteRealValue) * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
|
+ const measureLength = vRealValue * vDenominator * (60 / beatSpeed);
|
|
|
+ // console.table({value: iterator.currentTimeStamp.realValue, vRealValue,NoteRealValue, noteLength,measureLength, MeasureNumberXML: note.sourceMeasure.MeasureNumberXML})
|
|
|
+ usetime += noteLength;
|
|
|
+ relaMeasureLength += noteLength;
|
|
|
+ let relaEndtime = noteLength + relativeTime;
|
|
|
+ const fixedKey = note.fixedKey || 0;
|
|
|
+ const svgElelent = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables[si];
|
|
|
+ // console.log(note.sourceMeasure.MeasureNumberXML,note,svgElelent, NoteRealValue, measureLength)
|
|
|
+ if (allNotes.length && allNotes[allNotes.length - 1].relativeTime === relativeTime) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ // console.log(iterator.currentMeasure)
|
|
|
+ // 如果是弱起就补齐缺省的时长
|
|
|
+ if (i === 0) {
|
|
|
+ const diff = getMeasureDurationDiff(iterator.currentMeasure);
|
|
|
+ if (diff > 0) {
|
|
|
+ difftime = diff * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
|
+ fixtime += difftime;
|
|
|
+ }
|
|
|
+ // diff获取不准确时, 弱起补齐
|
|
|
+ if (["2589", "2561", "2560", "2559", "2558", "2556", "2555", "2554"].includes(detailId)) {
|
|
|
+ difftime = iterator.currentTimeStamp.realValue * formatBeatUnit(beatUnit) * (60 / beatSpeed);
|
|
|
+ fixtime += difftime;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // console.log(note.tie)
|
|
|
+ const nodeDetail = {
|
|
|
+ difftime,
|
|
|
+ octaveOffset: activeVerticalMeasureList[0]?.octaveOffset,
|
|
|
+ frequency: note.pitch?.frequency,
|
|
|
+ speed,
|
|
|
+ beatSpeed,
|
|
|
+ i,
|
|
|
+ si,
|
|
|
+ stepSpeeds,
|
|
|
+ measureOpenIndex: allMeasures.length - 1,
|
|
|
+ measures,
|
|
|
+ tempoInBPM: note.sourceMeasure.tempoInBPM,
|
|
|
+ measureLength,
|
|
|
+ relaMeasureLength,
|
|
|
+ id: svgElelent?.attrs.id,
|
|
|
+ note: note.halfTone + 12, // see issue #224
|
|
|
+ relativeTime: retain(relativeTime),
|
|
|
+ time: retain(relativeTime + fixtime),
|
|
|
+ endtime: retain(relaEndtime + fixtime),
|
|
|
+ relaEndtime: retain(relaEndtime),
|
|
|
+ realValue,
|
|
|
+ halfTone: note.halfTone,
|
|
|
+ noteElement: note,
|
|
|
+ svgElelent,
|
|
|
+ fixedKey,
|
|
|
+ realKey: 0,
|
|
|
+ duration: 0,
|
|
|
+ formatLyricsEntries: formatLyricsEntries(note),
|
|
|
+ stave: activeVerticalMeasureList[0]?.stave,
|
|
|
+ firstVerticalMeasure: activeVerticalMeasureList[0],
|
|
|
+ noteLength: 1,
|
|
|
+ osdmContext: osmd,
|
|
|
+ speedbeatUnit: beatUnit,
|
|
|
+ };
|
|
|
+ nodeDetail.realKey = formatRealKey(note.halfTone - fixedKey * 12, nodeDetail);
|
|
|
+ nodeDetail.duration = nodeDetail.endtime - nodeDetail.time;
|
|
|
+ let tickables = activeVerticalMeasureList[0]?.vfVoices["1"]?.tickables || [];
|
|
|
+ if ([121].includes(state.subjectId)) {
|
|
|
+ tickables = note.sourceMeasure.verticalSourceStaffEntryContainers;
|
|
|
+ }
|
|
|
+ // console.log(note.sourceMeasure.MeasureNumberXML, note.sourceMeasure.verticalSourceStaffEntryContainers.length)
|
|
|
+ nodeDetail.noteLength = tickables.length || 1;
|
|
|
+ allNotes.push(nodeDetail);
|
|
|
+ allNoteId.push(nodeDetail.id);
|
|
|
+ measures.push(nodeDetail);
|
|
|
+ if (si < tickables.length - 1) {
|
|
|
+ si++;
|
|
|
+ } else {
|
|
|
+ si = 0;
|
|
|
+ relaMeasureLength = 0;
|
|
|
+ measures = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ // 按照时间轴排序
|
|
|
+ const sortArray = allNotes.sort((a, b) => a.relativeTime - b.relativeTime).map((item, index) => ({ ...item, i: index }));
|
|
|
+ console.timeEnd("音符跑完时间");
|
|
|
+ try {
|
|
|
+ osmd.cursor.reset();
|
|
|
+ } catch (error) {}
|
|
|
+ return sortArray;
|
|
|
+};
|