UNPKG

@stringsync/vexml

Version:

MusicXML to Vexflow

171 lines (170 loc) 6.28 kB
import { Beam } from './beam'; import { ACCIDENTAL_TYPES, NOTEHEADS, NOTE_TYPES, STEMS } from './enums'; import { Notations } from './notations'; import { Lyric } from './lyric'; import { TimeModification } from './timemodification'; /** * Contains graphical and musical information about a note. * * https://www.w3.org/2021/06/musicxml40/musicxml-reference/elements/note/ */ export class Note { element; constructor(element) { this.element = element; } /** Returns the stem of the note or null when missing or invalid. */ getStem() { return this.element.first('stem')?.content().enum(STEMS) ?? null; } /** Returns the type of note or null when missing or invalid. */ getType() { return this.element.first('type')?.content().enum(NOTE_TYPES) ?? null; } /** Returns the duration of the note. Defaults to 4 */ getDuration() { return this.element.first('duration')?.content().int() ?? 4; } /** Returns how many dots are on the note. */ getDotCount() { return this.element.all('dot').length; } /** Returns the lyrics associated with the note. */ getLyrics() { return this.element.all('lyric').map((element) => new Lyric(element)); } /** Whether or not the note is a grace note. */ isGrace() { return this.element.all('grace').length > 0; } /** Whether the note has a grace note. */ hasGrace() { const element = this.element.previous('note'); if (!element) { return false; } return new Note(element).isGrace(); } /** Returns the notes that are part of this note's grace note. */ getGraceNotes() { const notes = new Array(); let sibling = this.element.previous('note'); while (sibling) { const note = new Note(sibling); if (!note.isGrace()) { break; } notes.push(note); sibling = sibling.previous('note'); } return notes.reverse(); } /** Whether or not the note has a glash slash. */ hasGraceSlash() { return this.element.first('grace')?.attr('slash').str() === 'yes'; } /** Returns the notations of the note. */ getNotations() { return this.element.all('notations').map((element) => new Notations(element)); } /** Returns the voice this note belongs to. */ getVoice() { return this.element.first('voice')?.content().str() ?? null; } /** Returns the stave the note belongs to. */ getStaveNumber() { return this.element.first('staff')?.content().int() ?? null; } /** Returns the step of the note. Defaults to 'C'. */ getStep() { return this.element.first('step')?.content().str() ?? 'C'; } /** Returns the octave of the note. Defaults to 4. */ getOctave() { return this.element.first('octave')?.content().int() ?? 4; } /** * Returns the step and octave of the rest in the format `${step}/${octave}`. * * Defaults to null. This is intended to only be used for notes that contain a <rest> element. */ getRestDisplayPitch() { const step = this.getRestDisplayStep(); const octave = this.getRestDisplayOctave(); return typeof step === 'string' && typeof octave === 'number' ? `${step}/${octave}` : null; } getRestDisplayStep() { return this.element.first('display-step')?.content().str() ?? null; } getRestDisplayOctave() { return this.element.first('display-octave')?.content().int() ?? null; } /** Returns the accidental type of the note. Defaults to null. */ getAccidentalType() { return this.element.first('accidental')?.content().enum(ACCIDENTAL_TYPES) ?? null; } /** Returns the alteration of the note. Defaults to null. */ getAlter() { return this.element.first('alter')?.content().float() ?? null; } /** Whether or not the accidental is cautionary. Defaults to false. */ hasAccidentalCautionary() { return this.element.first('accidental')?.attr('cautionary').str() === 'yes'; } /** Returns the notehead of the note. */ getNotehead() { return this.element.first('notehead')?.content().enum(NOTEHEADS) ?? null; } /** Whether or not the note is the first note of a chord. */ isChordHead() { if (this.isChordTail()) { return false; } const sibling = this.element.next('note'); if (!sibling) { return false; } const note = new Note(sibling); // The next note has to be part of a chord tail, otherwise there would only one note in the chord. return note.isChordTail() && note.isGrace() === this.isGrace(); } /** Whether or not the note is part of a chord and *not* the first note of the chord. */ isChordTail() { return this.element.all('chord').length > 0; } /** Returns the rest of the notes in the chord iff the current note is a chord head. Defaults to an empty array. */ getChordTail() { const tail = new Array(); if (!this.isChordHead()) { return tail; } const isGrace = this.isGrace(); let sibling = this.element.next('note'); while (sibling) { const note = new Note(sibling); if (!note.isChordTail() || note.isGrace() !== isGrace) { break; } tail.push(note); sibling = sibling.next('note'); } return tail; } /** Returns whether or not the note is a rest. */ isRest() { return this.element.all('rest').length > 0; } /** Returns the beams of the note. */ getBeams() { return this.element.all('beam').map((element) => new Beam(element)); } /** Returns the time modification of the note. Defaults to null. */ getTimeModification() { const element = this.element.first('time-modification'); return element ? new TimeModification(element) : null; } /** Whether to print the object. Defaults to true. */ printObject() { return this.element.attr('print-object').withDefault('yes').str() !== 'no'; } }