|
@@ -1,56 +1,56 @@
|
|
|
-import { OpenSheetMusicDisplay, SourceMeasure } from '../../accompany/osmd-extended/src'
|
|
|
+import { OpenSheetMusicDisplay, SourceMeasure } from "/osmd-extended/src";
|
|
|
|
|
|
export const noteDuration = {
|
|
|
- '1/2': 2,
|
|
|
- w: 1,
|
|
|
- h: 0.5,
|
|
|
- q: 0.25,
|
|
|
- '8': 0.125,
|
|
|
- '16': 0.0625,
|
|
|
- '32': 0.03125,
|
|
|
- '64': 0.015625,
|
|
|
- '128': 0.0078125,
|
|
|
-}
|
|
|
+ "1/2": 2,
|
|
|
+ w: 1,
|
|
|
+ h: 0.5,
|
|
|
+ q: 0.25,
|
|
|
+ "8": 0.125,
|
|
|
+ "16": 0.0625,
|
|
|
+ "32": 0.03125,
|
|
|
+ "64": 0.015625,
|
|
|
+ "128": 0.0078125,
|
|
|
+};
|
|
|
|
|
|
export type GradualChange = {
|
|
|
- resetXmlNoteIndex: number
|
|
|
- startXmlNoteIndex: number
|
|
|
- endXmlNoteIndex: number
|
|
|
- startWord: string
|
|
|
- startMeasureListIndex: number
|
|
|
- endMeasureListIndex: number
|
|
|
- resetMeasureListIndex: number
|
|
|
-}
|
|
|
+ resetXmlNoteIndex: number;
|
|
|
+ startXmlNoteIndex: number;
|
|
|
+ endXmlNoteIndex: number;
|
|
|
+ startWord: string;
|
|
|
+ startMeasureListIndex: number;
|
|
|
+ endMeasureListIndex: number;
|
|
|
+ resetMeasureListIndex: number;
|
|
|
+};
|
|
|
|
|
|
export const speedInfo: { [key in string]: number } = {
|
|
|
- 'rall.': 1.333333333,
|
|
|
- 'poco rit.': 1.333333333,
|
|
|
- 'rit.': 1.333333333,
|
|
|
- 'molto rit.': 1.333333333,
|
|
|
- 'molto rall': 1.333333333,
|
|
|
- 'molto': 1.333333333,
|
|
|
- lentando: 1.333333333,
|
|
|
- allargando: 1.333333333,
|
|
|
- morendo: 1.333333333,
|
|
|
- 'accel.': 0.8,
|
|
|
- calando: 2,
|
|
|
- 'poco accel.': 0.8,
|
|
|
- 'gradually slowing': 1.333333333,
|
|
|
- 'slowing': 1.333333333,
|
|
|
- 'slow': 1.333333333,
|
|
|
- 'slowly': 1.333333333,
|
|
|
- faster: 1.333333333,
|
|
|
-}
|
|
|
+ "rall.": 1.333333333,
|
|
|
+ "poco rit.": 1.333333333,
|
|
|
+ "rit.": 1.333333333,
|
|
|
+ "molto rit.": 1.333333333,
|
|
|
+ "molto rall": 1.333333333,
|
|
|
+ molto: 1.333333333,
|
|
|
+ lentando: 1.333333333,
|
|
|
+ allargando: 1.333333333,
|
|
|
+ morendo: 1.333333333,
|
|
|
+ "accel.": 0.8,
|
|
|
+ calando: 2,
|
|
|
+ "poco accel.": 0.8,
|
|
|
+ "gradually slowing": 1.333333333,
|
|
|
+ slowing: 1.333333333,
|
|
|
+ slow: 1.333333333,
|
|
|
+ slowly: 1.333333333,
|
|
|
+ faster: 1.333333333,
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
* 计算渐变速度
|
|
|
*/
|
|
|
-export const calcGradual = () => {}
|
|
|
+export const calcGradual = () => {};
|
|
|
|
|
|
/**
|
|
|
* 2022年9月14日版本 计算渐变速度,此方法不兼容之前的选择范围。
|
|
|
*/
|
|
|
-export const calcGradual2 = () => {}
|
|
|
+export const calcGradual2 = () => {};
|
|
|
|
|
|
/**
|
|
|
* 获取指定元素下一个Note元素
|
|
@@ -58,44 +58,44 @@ export const calcGradual2 = () => {}
|
|
|
* @param selectors 选择器
|
|
|
*/
|
|
|
const getNextNote = (ele: Element, selectors: string) => {
|
|
|
- let index = 0
|
|
|
- const parentEle = ele.closest(selectors)
|
|
|
- let pointer = parentEle
|
|
|
- const measure = parentEle?.closest('measure')
|
|
|
- let siblingNote: Element | null | undefined = null
|
|
|
- // 查找到相邻的第一个note元素
|
|
|
- while (!siblingNote && index < (measure?.childNodes.length || 50)) {
|
|
|
- index++
|
|
|
- if (pointer?.nextElementSibling?.tagName === 'note') {
|
|
|
- siblingNote = pointer?.nextElementSibling
|
|
|
- }
|
|
|
- pointer = pointer?.nextElementSibling!
|
|
|
- }
|
|
|
- return siblingNote
|
|
|
-}
|
|
|
+ let index = 0;
|
|
|
+ const parentEle = ele.closest(selectors);
|
|
|
+ let pointer = parentEle;
|
|
|
+ const measure = parentEle?.closest("measure");
|
|
|
+ let siblingNote: Element | null | undefined = null;
|
|
|
+ // 查找到相邻的第一个note元素
|
|
|
+ while (!siblingNote && index < (measure?.childNodes.length || 50)) {
|
|
|
+ index++;
|
|
|
+ if (pointer?.nextElementSibling?.tagName === "note") {
|
|
|
+ siblingNote = pointer?.nextElementSibling;
|
|
|
+ }
|
|
|
+ pointer = pointer?.nextElementSibling!;
|
|
|
+ }
|
|
|
+ return siblingNote;
|
|
|
+};
|
|
|
|
|
|
export type GradualElement = {
|
|
|
- ele: Element
|
|
|
- index: number
|
|
|
- noteInMeasureIndex: number
|
|
|
- textContent: string
|
|
|
- measureIndex: number
|
|
|
- type: 'words' | 'metronome'
|
|
|
- allDuration: number
|
|
|
- leftDuration: number
|
|
|
-}
|
|
|
+ ele: Element;
|
|
|
+ index: number;
|
|
|
+ noteInMeasureIndex: number;
|
|
|
+ textContent: string;
|
|
|
+ measureIndex: number;
|
|
|
+ type: "words" | "metronome";
|
|
|
+ allDuration: number;
|
|
|
+ leftDuration: number;
|
|
|
+};
|
|
|
|
|
|
-export type GradualNote = GradualItem[]
|
|
|
+export type GradualNote = GradualItem[];
|
|
|
|
|
|
export type GradualItem = {
|
|
|
- start: number
|
|
|
- measureIndex: number
|
|
|
- noteInMeasureIndex: number
|
|
|
- allDuration: number
|
|
|
- leftDuration: number
|
|
|
- type: string
|
|
|
- closedMeasureIndex: number
|
|
|
-}
|
|
|
+ start: number;
|
|
|
+ measureIndex: number;
|
|
|
+ noteInMeasureIndex: number;
|
|
|
+ allDuration: number;
|
|
|
+ leftDuration: number;
|
|
|
+ type: string;
|
|
|
+ closedMeasureIndex: number;
|
|
|
+};
|
|
|
|
|
|
// export type GradualItem = {
|
|
|
// start: number
|
|
@@ -113,173 +113,155 @@ export type GradualItem = {
|
|
|
* @param xml 始终按照第一分谱进行减慢速度的计算
|
|
|
*/
|
|
|
export const getGradualLengthByXml = (xml: string) => {
|
|
|
- // const firstPartXml = onlyVisible(xml, 0)
|
|
|
- const xmlParse = new DOMParser().parseFromString(xml, 'text/xml')
|
|
|
- const measures = Array.from(xmlParse.querySelectorAll('measure'))
|
|
|
- const notes = Array.from(xmlParse.querySelectorAll('note'))
|
|
|
- const words = Array.from(xmlParse.querySelectorAll('words'))
|
|
|
- const metronomes = Array.from(xmlParse.querySelectorAll('metronome'))
|
|
|
+ // const firstPartXml = onlyVisible(xml, 0)
|
|
|
+ const xmlParse = new DOMParser().parseFromString(xml, "text/xml");
|
|
|
+ const measures = Array.from(xmlParse.querySelectorAll("measure"));
|
|
|
+ const notes = Array.from(xmlParse.querySelectorAll("note"));
|
|
|
+ const words = Array.from(xmlParse.querySelectorAll("words"));
|
|
|
+ const metronomes = Array.from(xmlParse.querySelectorAll("metronome"));
|
|
|
|
|
|
- const eles: GradualElement[] = []
|
|
|
+ const eles: GradualElement[] = [];
|
|
|
|
|
|
- for (const ele of [...words, ...metronomes]) {
|
|
|
- const note = getNextNote(ele, 'direction')
|
|
|
- // console.log(ele, note)
|
|
|
- if (note) {
|
|
|
- const measure = note?.closest('measure')!
|
|
|
- const measureNotes = Array.from(measure.querySelectorAll('note'))
|
|
|
+ for (const ele of [...words, ...metronomes]) {
|
|
|
+ const note = getNextNote(ele, "direction");
|
|
|
+ // console.log(ele, note)
|
|
|
+ if (note) {
|
|
|
+ const measure = note?.closest("measure")!;
|
|
|
+ const measureNotes = Array.from(measure.querySelectorAll("note"));
|
|
|
|
|
|
- const noteInMeasureIndex = Array.from(measure.childNodes)
|
|
|
- .filter((item) => item.nodeName === 'note')
|
|
|
- .findIndex((item) => item === note)
|
|
|
+ const noteInMeasureIndex = Array.from(measure.childNodes)
|
|
|
+ .filter((item) => item.nodeName === "note")
|
|
|
+ .findIndex((item) => item === note);
|
|
|
|
|
|
- let allDuration = 0
|
|
|
- let leftDuration = 0
|
|
|
- for (let i = 0; i < measureNotes.length; i++) {
|
|
|
- const n = measureNotes[i]
|
|
|
- const duration = +(n.querySelector('duration')?.textContent || '0')
|
|
|
- allDuration += duration
|
|
|
- if (i < noteInMeasureIndex) {
|
|
|
- leftDuration = allDuration
|
|
|
- }
|
|
|
- }
|
|
|
- eles.push({
|
|
|
- ele,
|
|
|
- index: notes.indexOf(note!),
|
|
|
- noteInMeasureIndex,
|
|
|
- textContent: ele.textContent!,
|
|
|
- measureIndex: measures.indexOf(measure!),
|
|
|
- type: ele.tagName as GradualElement['type'],
|
|
|
- allDuration,
|
|
|
- leftDuration,
|
|
|
- })
|
|
|
- }
|
|
|
- }
|
|
|
+ let allDuration = 0;
|
|
|
+ let leftDuration = 0;
|
|
|
+ for (let i = 0; i < measureNotes.length; i++) {
|
|
|
+ const n = measureNotes[i];
|
|
|
+ const duration = +(n.querySelector("duration")?.textContent || "0");
|
|
|
+ allDuration += duration;
|
|
|
+ if (i < noteInMeasureIndex) {
|
|
|
+ leftDuration = allDuration;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ eles.push({
|
|
|
+ ele,
|
|
|
+ index: notes.indexOf(note!),
|
|
|
+ noteInMeasureIndex,
|
|
|
+ textContent: ele.textContent!,
|
|
|
+ measureIndex: measures.indexOf(measure!),
|
|
|
+ type: ele.tagName as GradualElement["type"],
|
|
|
+ allDuration,
|
|
|
+ leftDuration,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 结尾处手动插入一个音符节点
|
|
|
- eles.push({
|
|
|
- ele: notes[notes.length - 1],
|
|
|
- index: notes.length,
|
|
|
- noteInMeasureIndex: 0,
|
|
|
- textContent: '',
|
|
|
- type: 'metronome',
|
|
|
- allDuration: 1,
|
|
|
- leftDuration: 1,
|
|
|
- measureIndex: measures.length,
|
|
|
- })
|
|
|
+ // 结尾处手动插入一个音符节点
|
|
|
+ eles.push({
|
|
|
+ ele: notes[notes.length - 1],
|
|
|
+ index: notes.length,
|
|
|
+ noteInMeasureIndex: 0,
|
|
|
+ textContent: "",
|
|
|
+ type: "metronome",
|
|
|
+ allDuration: 1,
|
|
|
+ leftDuration: 1,
|
|
|
+ measureIndex: measures.length,
|
|
|
+ });
|
|
|
|
|
|
- const gradualNotes: GradualNote[] = []
|
|
|
- eles.sort((a, b) => a.index - b.index)
|
|
|
- const keys = Object.keys(speedInfo).map(w => w.toLocaleLowerCase())
|
|
|
- for (const ele of eles) {
|
|
|
- // 是否是同时也是关闭标签
|
|
|
- let isLastNoteAndNotClosed = false
|
|
|
- let closed = 0
|
|
|
- const textContent = ele.textContent?.toLocaleLowerCase().trim()
|
|
|
- if (ele === eles[eles.length - 1]) {
|
|
|
- if (gradualNotes[gradualNotes.length - 1]?.length === 1) {
|
|
|
- isLastNoteAndNotClosed = true
|
|
|
- }
|
|
|
- }
|
|
|
- const isKeyWork = keys.find(k => {
|
|
|
- const ks = k.split(' ')
|
|
|
- return textContent && ks.includes(textContent)
|
|
|
- })
|
|
|
- if (
|
|
|
- ele.type === "metronome" ||
|
|
|
- (ele.type === "words" && (textContent.startsWith("a tempo") || isKeyWork) || isLastNoteAndNotClosed)
|
|
|
- ) {
|
|
|
- const indexOf = gradualNotes.findIndex((item) => item.length === 1)
|
|
|
- if (indexOf > -1 && ele.index > gradualNotes[indexOf]?.[0].start) {
|
|
|
- closed = -1
|
|
|
- gradualNotes[indexOf][1] = {
|
|
|
- start: ele.index,
|
|
|
- measureIndex: ele.measureIndex,
|
|
|
- closedMeasureIndex: ele.measureIndex,
|
|
|
- noteInMeasureIndex: ele.noteInMeasureIndex,
|
|
|
- allDuration: ele.allDuration,
|
|
|
- leftDuration: ele.leftDuration,
|
|
|
- type: textContent,
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- if (ele.type === 'words' && isKeyWork) {
|
|
|
- gradualNotes.push([
|
|
|
- {
|
|
|
- start: ele.index,
|
|
|
- measureIndex: ele.measureIndex,
|
|
|
- closedMeasureIndex: ele.measureIndex + closed,
|
|
|
- noteInMeasureIndex: ele.noteInMeasureIndex,
|
|
|
- allDuration: ele.allDuration,
|
|
|
- leftDuration: ele.leftDuration,
|
|
|
- type: textContent,
|
|
|
- },
|
|
|
- ])
|
|
|
- }
|
|
|
- }
|
|
|
- return gradualNotes
|
|
|
-}
|
|
|
+ const gradualNotes: GradualNote[] = [];
|
|
|
+ eles.sort((a, b) => a.index - b.index);
|
|
|
+ const keys = Object.keys(speedInfo).map((w) => w.toLocaleLowerCase());
|
|
|
+ for (const ele of eles) {
|
|
|
+ // 是否是同时也是关闭标签
|
|
|
+ let isLastNoteAndNotClosed = false;
|
|
|
+ let closed = 0;
|
|
|
+ const textContent = ele.textContent?.toLocaleLowerCase().trim();
|
|
|
+ if (ele === eles[eles.length - 1]) {
|
|
|
+ if (gradualNotes[gradualNotes.length - 1]?.length === 1) {
|
|
|
+ isLastNoteAndNotClosed = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const isKeyWork = keys.find((k) => {
|
|
|
+ const ks = k.split(" ");
|
|
|
+ return textContent && ks.includes(textContent);
|
|
|
+ });
|
|
|
+ if (ele.type === "metronome" || (ele.type === "words" && (textContent.startsWith("a tempo") || isKeyWork)) || isLastNoteAndNotClosed) {
|
|
|
+ const indexOf = gradualNotes.findIndex((item) => item.length === 1);
|
|
|
+ if (indexOf > -1 && ele.index > gradualNotes[indexOf]?.[0].start) {
|
|
|
+ closed = -1;
|
|
|
+ gradualNotes[indexOf][1] = {
|
|
|
+ start: ele.index,
|
|
|
+ measureIndex: ele.measureIndex,
|
|
|
+ closedMeasureIndex: ele.measureIndex,
|
|
|
+ noteInMeasureIndex: ele.noteInMeasureIndex,
|
|
|
+ allDuration: ele.allDuration,
|
|
|
+ leftDuration: ele.leftDuration,
|
|
|
+ type: textContent,
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (ele.type === "words" && isKeyWork) {
|
|
|
+ gradualNotes.push([
|
|
|
+ {
|
|
|
+ start: ele.index,
|
|
|
+ measureIndex: ele.measureIndex,
|
|
|
+ closedMeasureIndex: ele.measureIndex + closed,
|
|
|
+ noteInMeasureIndex: ele.noteInMeasureIndex,
|
|
|
+ allDuration: ele.allDuration,
|
|
|
+ leftDuration: ele.leftDuration,
|
|
|
+ type: textContent,
|
|
|
+ },
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return gradualNotes;
|
|
|
+};
|
|
|
|
|
|
-export const getGradualLength = (
|
|
|
- gradualChange: GradualChange,
|
|
|
- speed: number,
|
|
|
- osdm: OpenSheetMusicDisplay
|
|
|
-) => {
|
|
|
- const {
|
|
|
- startMeasureListIndex,
|
|
|
- endMeasureListIndex,
|
|
|
- endXmlNoteIndex,
|
|
|
- startWord,
|
|
|
- } = gradualChange
|
|
|
- const measures: SourceMeasure[] = []
|
|
|
- for (
|
|
|
- let index = startMeasureListIndex;
|
|
|
- index <= endMeasureListIndex;
|
|
|
- index++
|
|
|
- ) {
|
|
|
- const measure = osdm.Sheet.SourceMeasures[index]
|
|
|
- measures.push(measure)
|
|
|
- }
|
|
|
- const allNoteDurations: number[] = []
|
|
|
- for (const measure of measures) {
|
|
|
- if (allNoteDurations.length >= endXmlNoteIndex) {
|
|
|
- break
|
|
|
- }
|
|
|
- // @ts-ignore
|
|
|
- measure.VerticalMeasureList[0]?.vfVoices['1']?.tickables?.forEach((item) =>
|
|
|
- allNoteDurations.push(
|
|
|
- noteDuration[item.duration as keyof typeof noteDuration]
|
|
|
- )
|
|
|
- )
|
|
|
- }
|
|
|
- const minDuration = Math.min(...allNoteDurations)
|
|
|
- const parts = allNoteDurations.map((item) => item / minDuration)
|
|
|
- const allParts = parts.reduce((total, val) => val + total, 0)
|
|
|
- // const startMeasure = osdm.Sheet.SourceMeasures[startMeasureListIndex]
|
|
|
- // const endMeasure = osdm.Sheet.SourceMeasures[endMeasureListIndex]
|
|
|
- let surplusSpeed = speed / speedInfo[startWord?.toLocaleLowerCase()] || 1
|
|
|
- const diffSpeed = speed - surplusSpeed
|
|
|
- let useSpeed = 0
|
|
|
- const speeds: number[] = parts.map((item) => {
|
|
|
- const s = ((diffSpeed - useSpeed) * item) / allParts
|
|
|
- return s
|
|
|
- })
|
|
|
- // 120 111.9 104.4 96.9
|
|
|
- // 8.1 7.5 7.2 6.9
|
|
|
- // 0.6 0.3 0.3
|
|
|
- const lingerSpeed: number[] = []
|
|
|
- for (let index = 0; index < speeds.length; index++) {
|
|
|
- const s = speeds[index]
|
|
|
- let beforeSpeed = 0
|
|
|
- let afterSpeed = 0
|
|
|
- for (let j = 0; j < index; j++) {
|
|
|
- beforeSpeed += speeds[j]
|
|
|
- }
|
|
|
- afterSpeed += beforeSpeed
|
|
|
- afterSpeed += s
|
|
|
+export const getGradualLength = (gradualChange: GradualChange, speed: number, osdm: OpenSheetMusicDisplay) => {
|
|
|
+ const { startMeasureListIndex, endMeasureListIndex, endXmlNoteIndex, startWord } = gradualChange;
|
|
|
+ const measures: SourceMeasure[] = [];
|
|
|
+ for (let index = startMeasureListIndex; index <= endMeasureListIndex; index++) {
|
|
|
+ const measure = osdm.Sheet.SourceMeasures[index];
|
|
|
+ measures.push(measure);
|
|
|
+ }
|
|
|
+ const allNoteDurations: number[] = [];
|
|
|
+ for (const measure of measures) {
|
|
|
+ if (allNoteDurations.length >= endXmlNoteIndex) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ // @ts-ignore
|
|
|
+ measure.VerticalMeasureList[0]?.vfVoices["1"]?.tickables?.forEach((item) =>
|
|
|
+ allNoteDurations.push(noteDuration[item.duration as keyof typeof noteDuration])
|
|
|
+ );
|
|
|
+ }
|
|
|
+ const minDuration = Math.min(...allNoteDurations);
|
|
|
+ const parts = allNoteDurations.map((item) => item / minDuration);
|
|
|
+ const allParts = parts.reduce((total, val) => val + total, 0);
|
|
|
+ // const startMeasure = osdm.Sheet.SourceMeasures[startMeasureListIndex]
|
|
|
+ // const endMeasure = osdm.Sheet.SourceMeasures[endMeasureListIndex]
|
|
|
+ let surplusSpeed = speed / speedInfo[startWord?.toLocaleLowerCase()] || 1;
|
|
|
+ const diffSpeed = speed - surplusSpeed;
|
|
|
+ let useSpeed = 0;
|
|
|
+ const speeds: number[] = parts.map((item) => {
|
|
|
+ const s = ((diffSpeed - useSpeed) * item) / allParts;
|
|
|
+ return s;
|
|
|
+ });
|
|
|
+ // 120 111.9 104.4 96.9
|
|
|
+ // 8.1 7.5 7.2 6.9
|
|
|
+ // 0.6 0.3 0.3
|
|
|
+ const lingerSpeed: number[] = [];
|
|
|
+ for (let index = 0; index < speeds.length; index++) {
|
|
|
+ const s = speeds[index];
|
|
|
+ let beforeSpeed = 0;
|
|
|
+ let afterSpeed = 0;
|
|
|
+ for (let j = 0; j < index; j++) {
|
|
|
+ beforeSpeed += speeds[j];
|
|
|
+ }
|
|
|
+ afterSpeed += beforeSpeed;
|
|
|
+ afterSpeed += s;
|
|
|
|
|
|
- lingerSpeed.push((afterSpeed + beforeSpeed) / 2)
|
|
|
- }
|
|
|
- // console.log(lingerSpeed, speeds[0], speeds, parts, allParts)
|
|
|
- return lingerSpeed
|
|
|
-}
|
|
|
+ lingerSpeed.push((afterSpeed + beforeSpeed) / 2);
|
|
|
+ }
|
|
|
+ // console.log(lingerSpeed, speeds[0], speeds, parts, allParts)
|
|
|
+ return lingerSpeed;
|
|
|
+};
|