UNPKG

musicxml2json-piano

Version:
190 lines (156 loc) 4.38 kB
import { has, isArray, isEmpty } from 'lodash' import { calculateRelativePosition, noteToMidi } from '@/utils' import { Articulation, Beam, Clef, Dot, Notations, Note as NoteT, NoteData, NoteType, NoteView, NoteXML, Slur, Stem, Tied, Time, TimeModification, Tuplet } from '@/types' type PropsType = { id: string staff?: Clef xmlData?: NoteXML } interface NoteInterface extends NoteT { appendData(data: NoteData): void getData(noteXML: NoteXML, staff: Clef): NoteData | null } export default class Note implements NoteInterface { public beam: Beam[] | null = null public data: NoteData[] | null = null public dot: Dot | null = null public id: string public name: string = '' public notations: Notations = { articulations: null, slur: null, tied: null, tuplet: null } public stem: Stem | null = null public time: Time | null = null public timeModification: TimeModification | null = null public type: NoteType public view: NoteView constructor ({ id, xmlData }: PropsType) { this.id = id if (xmlData) { this.beam = this.getBeam(xmlData) this.dot = this.getDot(xmlData) this.notations = this.getNotations(xmlData) this.stem = this.getStem(xmlData) this.timeModification = this.getTimeModification(xmlData) this.type = this.getType(xmlData) this.view = this.getView(xmlData) } else { this.view = 'rest' this.type = 'whole' } } private getArticulations(noteXML): Articulation | null { if (has(noteXML, 'notations.articulations.staccato')) { // 跳音 return 'staccato' } if (has(noteXML, 'notations.articulations.accent')) { // 重音 return 'accent' } return null } private getBeam(noteXML: NoteXML): Beam[] | null { const beam = noteXML.beam if (!beam) return null return isArray(beam) ? beam.map(item => item['#text']) : [beam['#text']] } getData(noteXML: NoteXML, staff: Clef): NoteData | null { const { pitch } = noteXML if (isEmpty(pitch)) { return null } // 临时标记 let accidental = null if (has(noteXML, 'accidental')) { accidental = noteXML.accidental } // 计算音符位置 const { step, octave, alter } = pitch const position = calculateRelativePosition(step, octave, staff) || 0 return { accidental, legerLine: position < -1 || position > 5, midiCode: noteToMidi(`${step}${alter ? '#' : ''}`, octave), pitch: { step, octave, alter: alter || 0 }, position } } private getDot(noteXML: NoteXML): Dot | null { if (!has(noteXML, 'dot')) { return null } const { dot } = noteXML if (isArray(dot)) { return dot.length >= 2 ? 'doubleDot' : 'dot' } return 'dot' } private getView(noteXML: NoteXML): NoteView { return has(noteXML, 'rest') ? 'rest' : 'single' } private getNotations(noteXML: NoteXML): Notations { return { articulations: this.getArticulations(noteXML), slur: this.getSlur(noteXML), tied: this.getTied(noteXML), tuplet: this.getTuplet(noteXML) } } private getSlur(noteXML: NoteXML): Slur { return noteXML.notations?.slur?._type ?? null } private getStem(noteXML: NoteXML): Stem { return noteXML.stem ?? null } private getTied(noteXML: NoteXML): Tied | null { const tied = noteXML.notations?.tied if (!tied) return null return isArray(tied) ? tied[0]._type : tied._type } private getTimeModification(noteXML: NoteXML): TimeModification | null { const timeMod = noteXML['time-modification'] if (!timeMod) return null const { 'actual-notes': actualNotes, 'normal-notes': normalNotes } = timeMod return actualNotes && normalNotes ? { actualNotes, normalNotes } : null } private getTuplet(noteXML: NoteXML): Tuplet | null { const tuplet = noteXML.notations?.tuplet if (!tuplet) return null return isArray(tuplet) ? tuplet[0]._type : tuplet._type } private getType(noteXML: NoteXML): NoteType { return noteXML.type } appendData(data: NoteData) { if (!this.data) { this.data = [] } this.data?.push(data) } appendTime(start: number, duration: number) { this.time = { start, duration, end: start + duration } } }