|
@@ -14,6 +14,8 @@ import {
|
|
|
OpenSheetMusicDisplay,
|
|
|
} from "/osmd-extended/src";
|
|
|
import { GradualChange, speedInfo } from "./calcSpeed";
|
|
|
+import { beatUnitTo, speedBeatTo } from "/src/helpers/beatConfig"
|
|
|
+
|
|
|
const browserInfo = browser();
|
|
|
dayjs.extend(duration);
|
|
|
|
|
@@ -32,7 +34,7 @@ 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";
|
|
|
+ const beatUnit = "quarter";
|
|
|
// if (state.repeatedBeats) {
|
|
|
// // 音频制作问题仅2拍不重复
|
|
|
// numerator = numerator === 2 ? 4 : numerator;
|
|
@@ -149,9 +151,9 @@ export const getDuration = (osmd?: OpenSheetMusicDisplay): Duration => {
|
|
|
const { Duration, TempoInBPM, ActiveTimeSignature, TempoExpressions } = osmd.GraphicSheet.MeasureList[0][0]?.parentSourceMeasure;
|
|
|
if (Duration) {
|
|
|
let beatUnit = "quarter";
|
|
|
- for (const item of TempoExpressions) {
|
|
|
- beatUnit = item.InstantaneousTempo.beatUnit || "quarter";
|
|
|
- }
|
|
|
+ // for (const item of TempoExpressions) {
|
|
|
+ // beatUnit = item.InstantaneousTempo.beatUnit || "quarter";
|
|
|
+ // }
|
|
|
const duration = formatDuration(ActiveTimeSignature, Duration) as unknown as FractionDefault;
|
|
|
return {
|
|
|
...duration,
|
|
@@ -238,11 +240,6 @@ export function formatBeatUnit(beatUnit: string) {
|
|
|
return multiple;
|
|
|
}
|
|
|
|
|
|
-/** 根据音符单位,速度,几几拍计算正确的时间 */
|
|
|
-export function getTimeByBeatUnit(beatUnit: string, bpm: number, denominator: number) {
|
|
|
- return (denominator / formatBeatUnit(beatUnit)) * bpm;
|
|
|
-}
|
|
|
-
|
|
|
export type CustomInfo = {
|
|
|
showSpeed: boolean;
|
|
|
parsedXML: string;
|
|
@@ -366,7 +363,7 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
|
const detailId = state.examSongId + "";
|
|
|
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?.trim() || "");
|
|
|
+ const partListNames = Array.from(partList).map((item) => item.getElementsByTagName("part-name")?.[0]?.textContent?.trim() || "");
|
|
|
const parts: any = xmlParse.getElementsByTagName("part");
|
|
|
// const firstTimeInfo = parts[0]?.getElementsByTagName('metronome')[0]?.parentElement?.parentElement?.cloneNode(true)
|
|
|
const firstMeasures = [...parts[0]?.getElementsByTagName("measure")];
|
|
@@ -376,10 +373,10 @@ export const onlyVisible = (xml: string, partIndex: number): string => {
|
|
|
const rehearsals = [...parts[0]?.getElementsByTagName("rehearsal")];
|
|
|
|
|
|
/** 第一分谱如果是约定的配置分谱则跳过 */
|
|
|
- // if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
|
|
|
- // partIndex++;
|
|
|
- // partListNames.shift();
|
|
|
- // }
|
|
|
+ if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
|
|
|
+ partIndex++;
|
|
|
+ partListNames.shift();
|
|
|
+ }
|
|
|
const visiblePartInfo = partList[partIndex];
|
|
|
// console.log(visiblePartInfo, partIndex)
|
|
|
// 根据后台已选择的分轨筛选出能切换的声轨
|
|
@@ -664,8 +661,8 @@ export const formatXML = (xml: string, xmlUrl?: string): string => {
|
|
|
for (const minute of minutes) {
|
|
|
let measureSpeed = minute.textContent ? Number(minute.textContent) : 0;
|
|
|
// 速度带附点,需要转换成不带附点的速度值
|
|
|
- const hasSpeedDot = Array.from(minute?.parentElement?.children || []).some((item: any) => item?.tagName === 'beat-unit-dot')
|
|
|
- measureSpeed = hasSpeedDot ? measureSpeed + measureSpeed/2 : measureSpeed;
|
|
|
+ // const hasSpeedDot = Array.from(minute?.parentElement?.children || []).some((item: any) => item?.tagName === 'beat-unit-dot')
|
|
|
+ // measureSpeed = hasSpeedDot ? measureSpeed + measureSpeed/2 : measureSpeed;
|
|
|
if (minute.textContent && measureSpeed) {
|
|
|
speeds.push(Number(measureSpeed))
|
|
|
}
|
|
@@ -682,15 +679,29 @@ export const formatXML = (xml: string, xmlUrl?: string): string => {
|
|
|
state.originSpeed = speeds[0] ? speeds[0] : 100;
|
|
|
state.speed = state.originSpeed;
|
|
|
}
|
|
|
+ // 赋值谱面速度节拍器,没有的时候 以后台传入的为准
|
|
|
+ const metronomeXml = xmlParse.getElementsByTagName('metronome')?.[0]
|
|
|
+ const beatUnit = metronomeXml?.getElementsByTagName('beat-unit')?.[0]?.textContent || ''
|
|
|
+ if(beatUnit){
|
|
|
+ const beatUnitDot = metronomeXml?.getElementsByTagName('beat-unit-dot')?.[0]
|
|
|
+ state.speedBeatUnit = beatUnitTo(beatUnit, !!beatUnitDot)
|
|
|
+ }
|
|
|
// 如果谱面和小节都没有打速度,osmd设置的小节速度默认取后台设置的速度
|
|
|
if (speeds.length === 0) {
|
|
|
;(window as any).baseMeasureSpeed = state.originSpeed
|
|
|
} else {
|
|
|
- state.originAudioPlayRate = speeds[0] / state.originSpeed
|
|
|
+ // 当前谱面的速度转为4分音符速度 因为我们速度比例转为4分音符了
|
|
|
+ state.originAudioPlayRate = speedBeatTo({unit:state.speedBeatUnit, speed:speeds[0]}, "1/4") / state.originSpeed
|
|
|
}
|
|
|
console.log('是否是变速的曲子:',hasVaryingSpeed,speeds)
|
|
|
-
|
|
|
- const repeats: any = Array.from(xmlParse.querySelectorAll('repeat'));
|
|
|
+ let repeats: any = [];
|
|
|
+ if (state.partIndex === 999) {
|
|
|
+ repeats = Array.from(xmlParse.querySelectorAll('repeat')) || [];
|
|
|
+ } else {
|
|
|
+ const hasCommon = xmlParse.querySelectorAll('part-name')?.[0]?.textContent === 'common';
|
|
|
+ const currentTrackIndex = hasCommon ? state.partIndex + 1 : state.partIndex;
|
|
|
+ repeats = Array.from(xmlParse.querySelectorAll('part')?.[currentTrackIndex]?.querySelectorAll('repeat')) || [];
|
|
|
+ }
|
|
|
compatibleXmlPitchVoice(xmlParse);
|
|
|
// 获取作词、作曲家
|
|
|
getComposer(xmlParse);
|
|
@@ -791,7 +802,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
const customNoteCurrentTime = customData.customNoteCurrentTime;
|
|
|
const detailId = state.examSongId + "";
|
|
|
const partIndex = state.partIndex + "";
|
|
|
- let fixtime = browserInfo.huawei ? 0.08 : 0; //getFixTime()
|
|
|
+ //let fixtime = browserInfo.huawei ? 0.08 : 0; //getFixTime()
|
|
|
+ let fixtime = 0;
|
|
|
const allNotes: any[] = [];
|
|
|
const allNoteId: string[] = [];
|
|
|
const allMeasures: any[] = [];
|
|
@@ -849,6 +861,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
|
|
|
let preNoteMeasureNumber = 0; // 上一个小节的number值
|
|
|
|
|
|
+ let currentRealTempo: any = {}; // 当前小节的速度与拍号信息
|
|
|
+
|
|
|
const _notes = [] as any[];
|
|
|
if (state.gradualTimes) {
|
|
|
console.log("后台设置的渐慢小节时间", state.gradual, state.gradualTimes);
|
|
@@ -963,13 +977,20 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
}
|
|
|
note.maxNoteNum = maxNoteNum;
|
|
|
note.trackIndex = minIndex;
|
|
|
+ currentRealTempo = iterator.currentMeasure.tempoExpressions.length ? iterator.currentMeasure.tempoExpressions.find((item: any) => item?.InstantaneousTempo?.isMetronomeMark)?.InstantaneousTempo || currentRealTempo : currentRealTempo;
|
|
|
+ const { beatUnit="quarter", dotted=false, tempoInBpm=state.originSpeed } = currentRealTempo
|
|
|
+ const speedBeatUnit = beatUnitTo(beatUnit, dotted)
|
|
|
_notes.push({
|
|
|
note,
|
|
|
iterator: { ...iterator },
|
|
|
currentTime,
|
|
|
isDouble,
|
|
|
isMutileSubject,
|
|
|
- measuresTempoInBPM: note?.sourceMeasure?.tempoInBPM
|
|
|
+ // measuresTempoInBPM: note?.sourceMeasure?.tempoInBPM,
|
|
|
+ // 转换成1/4拍的速度
|
|
|
+ measuresTempoInBPM: speedBeatTo({unit: speedBeatUnit || "1/4",speed: tempoInBpm || 0}, `1/4`),
|
|
|
+ speedBeatUnit, // 当前谱面小节的速度对应的是几分音符
|
|
|
+ currentRealTempo
|
|
|
});
|
|
|
}
|
|
|
|
|
@@ -987,7 +1008,7 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
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, speedBeatUnit, measuresTempoInBPM } of _notes) {
|
|
|
if (note) {
|
|
|
if (preMeasureNumber != note?.sourceMeasure?.MeasureNumberXML) {
|
|
|
si = 0
|
|
@@ -996,14 +1017,14 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
preMeasureNumber = note?.sourceMeasure?.MeasureNumberXML
|
|
|
allMeasures.push(note.sourceMeasure);
|
|
|
}
|
|
|
- if (si === 0 && state.isSpecialBookCategory) {
|
|
|
- for (const expression of (note.sourceMeasure as SourceMeasure)?.TempoExpressions) {
|
|
|
- if (expression?.InstantaneousTempo?.beatUnit) {
|
|
|
- // 取最后一个有效的tempo
|
|
|
- beatUnit = expression.InstantaneousTempo.beatUnit;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // if (si === 0 && state.isSpecialBookCategory) {
|
|
|
+ // for (const expression of (note.sourceMeasure as SourceMeasure)?.TempoExpressions) {
|
|
|
+ // if (expression?.InstantaneousTempo?.beatUnit) {
|
|
|
+ // // 取最后一个有效的tempo
|
|
|
+ // beatUnit = expression.InstantaneousTempo.beatUnit;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
// 判断是否是同一小节
|
|
|
if (staveIndex == note.sourceMeasure?.MeasureNumberXML && i !== 0) {
|
|
|
staveNoteIndex++
|
|
@@ -1095,12 +1116,15 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
|
|
|
let beatSpeed = 0;
|
|
|
// 速度不能为0 此处的速度应该是按照设置的速度而不是校准后的速度,否则mp3速度不对
|
|
|
- if (measureSpeed !== baseSpeed && !hasVaryingSpeed) {
|
|
|
- beatSpeed = baseSpeed || measureSpeed || 100
|
|
|
- } else {
|
|
|
- beatSpeed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
- }
|
|
|
+ // if (measureSpeed !== baseSpeed && !hasVaryingSpeed) {
|
|
|
+ // beatSpeed = baseSpeed || measureSpeed || 100
|
|
|
+ // } else {
|
|
|
+ // beatSpeed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
+ // }
|
|
|
+ // 计算音符时值,使用转换成1/4的速度计算
|
|
|
+ beatSpeed = measuresTempoInBPM;
|
|
|
// let beatSpeed = measureSpeed || baseSpeed
|
|
|
+ beatSpeed = beatSpeed / state.originAudioPlayRate;
|
|
|
// 如果有节拍器,需要将节拍器的时间算出来
|
|
|
if (i === 0) {
|
|
|
if(state.isOpenMetronome){
|
|
@@ -1111,9 +1135,9 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
xmlMp3BeatFixTime = getFixTime(beatSpeed)
|
|
|
// console.log("fixtime:", fixtime, '速度:', beatSpeed, "state.isSpecialBookCategory:", state.isSpecialBookCategory, 'state.isOpenMetronome:', state.isOpenMetronome);
|
|
|
}
|
|
|
- // console.log(getTimeByBeatUnit(beatUnit, measureSpeed, iterator.currentMeasure.activeTimeSignature.Denominator))
|
|
|
let gradualLength = 0;
|
|
|
- let speed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
+ // let speed = (state.isSpecialBookCategory ? measureSpeed : baseSpeed) || 1;
|
|
|
+ let speed = measureSpeed ? measureSpeed : baseSpeed;
|
|
|
gradualChange = iterator.currentMeasure.speedInfo || gradualChange;
|
|
|
gradualSpeed = osmd.Sheet.SoundTempos?.get(note.sourceMeasure.measureListIndex) || gradualSpeed;
|
|
|
if (!gradualSpeed || gradualSpeed.length < 2) {
|
|
@@ -1151,34 +1175,6 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- } else if (state.appName === "GYM" && gradualChange && gradualSpeed && (gradualChange.startXmlNoteIndex === si || gradualChangeIndex > 0)) {
|
|
|
- const startSpeed = gradualSpeed[0] - (gradualSpeed[1] - gradualSpeed[0]);
|
|
|
- const { resetXmlNoteIndex, endXmlNoteIndex } = gradualChange;
|
|
|
- const noteDiff = endXmlNoteIndex;
|
|
|
- let stepSpeed = (gradualSpeed[gradualSpeed.length - 1] - startSpeed) / noteDiff;
|
|
|
- stepSpeed = note.DotsXml ? stepSpeed / 1.5 : stepSpeed;
|
|
|
- if (gradualChangeIndex < noteDiff) {
|
|
|
- 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;
|
|
|
- } else if (resetXmlNoteIndex > gradualChangeIndex) {
|
|
|
- speed = allNotes[i - 1]?.speed;
|
|
|
- }
|
|
|
- beatSpeed =
|
|
|
- (state.isSpecialBookCategory ? getTimeByBeatUnit(beatUnit, speed, iterator.currentMeasure.activeTimeSignature.Denominator) : baseSpeed) || 1;
|
|
|
- const isEnd = !(gradualChangeIndex < noteDiff) && !(resetXmlNoteIndex > gradualChangeIndex);
|
|
|
- gradualChangeIndex++;
|
|
|
- if (isEnd) {
|
|
|
- gradualChangeIndex = 0;
|
|
|
- gradualChange = undefined;
|
|
|
- gradualSpeed = undefined;
|
|
|
- stepSpeeds = [];
|
|
|
- }
|
|
|
}
|
|
|
const _noteLength = NoteRealValue;
|
|
|
// 当前音符的持续时长,当前音符的RealValue值*拍数*(60/后台设置的基准速度)
|
|
@@ -1225,9 +1221,11 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
const staffEntries = note.sourceMeasure.verticalMeasureList?.[0]?.staffEntries || [];
|
|
|
//计算第一个小节里面的音符时值是否等于整个小节的时值
|
|
|
staffEntries.forEach((_a: any) => {
|
|
|
- if (_a?.sourceStaffEntry?.voiceEntries?.[0]?.notes?.[0]?.length?.realValue) {
|
|
|
- _firstMeasureRealValue += _a.sourceStaffEntry.voiceEntries[0].notes[0].length.realValue;
|
|
|
- }
|
|
|
+ // 需要过滤掉倚音音符
|
|
|
+ const matchNote = _a?.sourceStaffEntry?.voiceEntries?.length > 1 ? _a?.sourceStaffEntry?.voiceEntries.find((item: any) => !item.isGrace) : _a?.sourceStaffEntry?.voiceEntries?.[0]
|
|
|
+ if (matchNote?.notes?.[0]?.length?.realValue) {
|
|
|
+ _firstMeasureRealValue += matchNote.notes[0].length.realValue;
|
|
|
+ }
|
|
|
});
|
|
|
if (_firstMeasureRealValue < vRealValue) {
|
|
|
// console.log(_firstMeasureRealValue, vRealValue)
|
|
@@ -1292,7 +1290,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
// 找出这个音符前面音符的结束时间
|
|
|
let preNoteTImes = allNotes[allNotes.length - 1]?.endtime*1000
|
|
|
if(!preNoteTImes){
|
|
|
- preNoteTImes = Math.max(fixtime - noteLength, 0)*1000 //如果前一个音符没有结束时间,证明这个音符是第一个音符没有打时间,所以往前奏里面找补
|
|
|
+ //如果前一个音符没有结束时间,证明这个音符是第一个音符没有打时间,当有timegap以fixtime当开始时间(1795013294269087745),当第一个小节有times这个往前奏里面找补(1795013306436763649)
|
|
|
+ preNoteTImes = (state.evXmlBeginArr.length>0 ? fixtime : Math.max(fixtime - noteLength, 0))*1000
|
|
|
}
|
|
|
// 找出这个音符后面音符的开始时间
|
|
|
let nextI = i
|
|
@@ -1405,7 +1404,8 @@ export const formateTimes = (osmd: OpenSheetMusicDisplay) => {
|
|
|
firstVerticalMeasure: activeVerticalMeasureList[0],
|
|
|
noteLength: 1,
|
|
|
//osdmContext: osmd,
|
|
|
- speedbeatUnit: beatUnit,
|
|
|
+ // speedbeatUnit: beatUnit,
|
|
|
+ speedBeatUnit, // 当前谱面小节的速度对应的是几分音符
|
|
|
multipleRestMeasures: multipleRestMeasures, // 当前合并小节的索引,从1开始到当前的totalMultipleRestMeasures结束,
|
|
|
totalMultipleRestMeasures, // 当前小节总的合并小节数
|
|
|
measureSpeed, // 小节速度
|
|
@@ -1759,7 +1759,9 @@ export const compatibleXmlPitchVoice = (xmlParse: any) => {
|
|
|
xmlNeedAdjustVoice = !instrumentName || instrumentName.includes('solo') ? true : false
|
|
|
break;
|
|
|
}
|
|
|
- (window as any).xmlNeedAdjustVoice = xmlNeedAdjustVoice
|
|
|
+ // (window as any).xmlNeedAdjustVoice = xmlNeedAdjustVoice
|
|
|
+ // 管乐迷的曲子不需要上述判断,修改为都需要程序处理(保持和之前逻辑一致)
|
|
|
+ (window as any).xmlNeedAdjustVoice = true
|
|
|
}
|
|
|
}
|
|
|
|