| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- // import { isSpecialMark, isSpeedKeyword, isGradientWords, GRADIENT_SPEED_RESET_TAG } from "./speed-tag"
- // import { isSpecialMark, isSpeedKeyword, isGradientWords, GRADIENT_SPEED_RESET_TAG } from "./speed-tag"
- export class StringUtil {
- public static StringContainsSeparatedWord(str: string, wordRegExString: string, ignoreCase = false): boolean {
- const regExp = new RegExp("( |^)" + wordRegExString + "([ .]|$)", ignoreCase ? "i" : undefined)
- return regExp.test(str)
- }
- }
- export const formatXML = (xml: string): string => {
- if (!xml) return ""
- const xmlParse = new DOMParser().parseFromString(xml, "text/xml")
- const measures: any = xmlParse.getElementsByTagName("measure")
- // const repeats: any = Array.from(xmlParse.querySelectorAll('repeat'))
- // 处理重复小节信息
- // 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 onlyVisible = (xml: string, partIndex: number): string => {
- if (!xml) return ""
- const xmlParse = new DOMParser().parseFromString(xml, "text/xml")
- const partList = xmlParse.getElementsByTagName("part-list")?.[0]?.getElementsByTagName("score-part") || []
- // @typescript-eslint/no-unused-vars
- const partListNames = Array.from(partList).map(item => item.getElementsByTagName("part-name")?.[0].textContent || "")
- const parts: any = xmlParse.getElementsByTagName("part")
- // const firstTimeInfo = parts[0]?.getElementsByTagName('metronome')[0]?.parentElement?.parentElement?.cloneNode(true)
- const part: any = parts[0]
- const firstMeasures = [...part.getElementsByTagName("measure")]
- const metronomes = [...part.getElementsByTagName("metronome")]
- // const words = [...part.getElementsByTagName("words")]
- // const codas = [...part.getElementsByTagName("coda")]
- // const rehearsals = [...part.getElementsByTagName("rehearsal")]
- /** 第一分谱如果是约定的配置分谱则跳过 */
- if (partListNames[0]?.toLocaleUpperCase?.() === "COMMON") {
- partIndex++
- partListNames.shift()
- }
- const visiblePartInfo = partList[partIndex]
- // console.log(visiblePartInfo, partIndex)
- if (visiblePartInfo) {
- const id = visiblePartInfo.getAttribute("id")
- Array.from(parts).forEach((part: any) => {
- if (part && part.getAttribute("id") !== id) {
- part.parentNode?.removeChild(part)
- // 不等于第一行才添加避免重复添加
- } else if (part && part.getAttribute("id") !== "P1") {
- // 速度标记仅保留最后一个
- const metronomeData: {
- [key in string]: Element
- } = {}
- for (let i = 0; i < metronomes.length; i++) {
- const metronome = metronomes[i]
- const metronomeContainer = metronome.parentElement?.parentElement?.parentElement
- if (metronomeContainer) {
- const index = firstMeasures.indexOf(metronomeContainer)
- metronomeData[index] = metronome
- }
- }
- Object.values(metronomeData).forEach(metronome => {
- 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)
- if (parentMeasure && metronomesIndex > -1) {
- const index = firstMeasures.indexOf(parentMeasure)
- const activeMeasure = part.getElementsByTagName("measure")[index]
- setElementNoteBefore(metronomeContainer, parentMeasure, activeMeasure)
- }
- })
- }
- // 最后一个小节的结束线元素不在最后 调整
- if (part && part.getAttribute("id") === id) {
- const barlines = part.getElementsByTagName("barline")
- const lastParent = barlines[barlines.length - 1]?.parentElement
- if (lastParent?.lastElementChild?.tagName !== "barline") {
- const children: any = lastParent?.children || []
- for (const el of children) {
- if (el.tagName === "barline") {
- // 将结束线元素放到最后
- lastParent?.appendChild(el)
- break
- }
- }
- }
- }
- })
- Array.from(partList).forEach(part => {
- if (part && part.getAttribute("id") !== id) {
- part.parentNode?.removeChild(part)
- }
- })
- // 处理装饰音问题
- // const notes = xmlParse.getElementsByTagName("note")
- // const getNextvNoteDuration = (i: number) => {
- // let nextNote = notes[i + 1]
- // // 可能存在多个装饰音问题,取下一个非装饰音时值
- // for (let index = i index < notes.length index++) {
- // const note = notes[index]
- // if (!note.getElementsByTagName("grace")?.length) {
- // nextNote = note
- // break
- // }
- // }
- // const nextNoteDuration = nextNote?.getElementsByTagName("duration")[0]
- // return nextNoteDuration
- // }
- // Array.from(notes).forEach((note, i) => {
- // const graces = note.getElementsByTagName("grace")
- // if (graces && graces.length) {
- // // if (i !== 0) {
- // // note.appendChild(getNextvNoteDuration(i)?.cloneNode(true))
- // // }
- // }
- // })
- }
- // console.log(xmlParse)
- return new XMLSerializer().serializeToString(appoggianceFormate(xmlParse))
- }
- // 倚音后连音线
- export const appoggianceFormate = (xmlParse: Document): Document => {
- if (!xmlParse) return xmlParse
- const graces: any = xmlParse.querySelectorAll("grace")
- if (!graces.length) return xmlParse
- const getNextElement = (el: HTMLElement): HTMLElement => {
- if (el.querySelector("grace")) {
- return getNextElement(el?.nextElementSibling as HTMLElement)
- }
- return el
- }
- for (const grace of graces) {
- const notations = grace.parentElement?.querySelector("notations")
- if (notations && notations.querySelectorAll("slur").length > 1) {
- const nextEle: Element = getNextElement(grace.parentElement?.nextElementSibling as HTMLElement)
- if (nextEle && nextEle.querySelectorAll("slur").length > 0) {
- const slurNumber = Array.from(nextEle.querySelector("notations")?.children || []).map((el: Element) => {
- return el.getAttribute("number")
- })
- const slurs = notations.querySelectorAll("slur")
- for (const nota of slurs) {
- if (!slurNumber.includes(nota.getAttribute("number"))) {
- nextEle.querySelector("notations")?.appendChild(nota)
- }
- }
- }
- }
- }
- return xmlParse
- }
- /**
- * 添加第一分谱信息至当前分谱
- * @param ele 需要插入的元素
- * @param fitstParent 合奏谱第一个分谱
- * @param parent 需要添加的分谱
- */
- const setElementNoteBefore = (ele: Element, fitstParent: Element, parent?: Element | null) => {
- let noteIndex = 0
- if (!fitstParent) {
- return
- }
- for (let index = 0; index < fitstParent.childNodes.length; index++) {
- const element = fitstParent.childNodes[index]
- if (element.nodeName === "note") {
- noteIndex++
- }
- if (element === ele) {
- break
- }
- }
- if (noteIndex === 0 && parent) {
- parent.insertBefore(ele, parent.childNodes[0])
- return
- }
- if (parent && parent.childNodes.length > 0) {
- let noteIndex2 = 0
- const notes = Array.from(parent.childNodes).filter(child => child.nodeName === "note")
- const lastNote = notes[notes.length - 1]
- if (noteIndex >= notes.length && lastNote) {
- parent.insertBefore(ele, parent.childNodes[Array.from(parent.childNodes).indexOf(lastNote)])
- return
- }
- for (let index = 0; index < notes.length; index++) {
- const element = notes[index]
- if (element.nodeName === "note") {
- noteIndex2 = noteIndex2 + 1
- if (noteIndex2 === noteIndex) {
- parent.insertBefore(ele, element)
- break
- }
- }
- }
- }
- // console.log(noteIndex, parent)
- }
- /**
- * 检查传入文字是否为重复关键词
- * @param text 总谱xml
- * @returns 是否是重复关键词
- */
- export const isRepeatWord = (text: string): boolean => {
- if (text) {
- const innerText = text.toLocaleLowerCase()
- const dsRegEx = "d\\s?\\.s\\."
- const dcRegEx = "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 false
- }
- /** 从xml中获取自定义信息,并删除多余的字符串 */
- export const getCustomInfo = (xml: string): any => {
- const data = {
- showSpeed: true,
- parsedXML: xml
- }
- const xmlParse = new DOMParser().parseFromString(xml, "text/xml")
- const words: any = xmlParse.getElementsByTagName("words")
- for (const word of words) {
- if (word && word.textContent?.trim() === "隐藏速度") {
- data.showSpeed = false
- word.textContent = ""
- }
- if (word && word.textContent?.trim() === "@") {
- word.textContent = "segno"
- }
- }
- data.parsedXML = new XMLSerializer().serializeToString(xmlParse)
- return data
- }
|