UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

415 lines (414 loc) 20.9 kB
import { VexFlowTests } from './vexflow_test_helpers.js'; import { ModifierPosition } from '../src.js'; import { Accidental } from '../src/accidental.js'; import { Annotation } from '../src/annotation.js'; import { Articulation } from '../src/articulation.js'; import { Dot } from '../src/dot.js'; import { Formatter } from '../src/formatter.js'; const GraceNoteTests = { Start() { QUnit.module('Grace Notes'); const run = VexFlowTests.runTests; run('Grace Note Basic', basic); run('With Articulation and Annotation on Parent Note', graceNoteModifiers); run('Grace Note Basic with Slurs', basicSlurred); run('Grace Note Stem', stem); run('Grace Note Stem with Beams 1', stemWithBeamed, { keys1: ['g/4'], stemDirection1: 1, keys2: ['d/5'], stemDirection2: -1, }); run('Grace Note Stem with Beams 2', stemWithBeamed, { keys1: ['a/3'], stemDirection1: 1, keys2: ['a/5'], stemDirection2: -1, }); run('Grace Note Stem with Beams 3', stemWithBeamed, { keys1: ['c/4'], stemDirection1: 1, keys2: ['c/6'], stemDirection2: -1, }); run('Grace Note Slash', slash); run('Grace Note Slash with Beams', slashWithBeams); run('Grace Notes Multiple Voices', multipleVoices); run('Grace Notes Multiple Voices Multiple Draws', multipleVoicesMultipleDraws); }, }; function basic(options) { const f = VexFlowTests.makeFactory(options, 700, 130); const stave = f.Stave({ x: 10, y: 10, width: 650 }); const gracenotes = [ { keys: ['e/4'], duration: '32' }, { keys: ['f/4'], duration: '32' }, { keys: ['g/4'], duration: '32' }, { keys: ['a/4'], duration: '32' }, ].map(f.GraceNote.bind(f)); const gracenotes1 = [{ keys: ['b/4'], duration: '8', slash: false }].map(f.GraceNote.bind(f)); const gracenotes2 = [{ keys: ['b/4'], duration: '8', slash: true }].map(f.GraceNote.bind(f)); const gracenotes3 = [ { keys: ['e/4'], duration: '8' }, { keys: ['f/4'], duration: '16' }, { keys: ['e/4', 'g/4'], duration: '8' }, { keys: ['a/4'], duration: '32' }, { keys: ['b/4'], duration: '32' }, ].map(f.GraceNote.bind(f)); const gracenotes4 = [ { keys: ['g/4'], duration: '8' }, { keys: ['g/4'], duration: '16' }, { keys: ['g/4'], duration: '16' }, ].map(f.GraceNote.bind(f)); gracenotes[1].addModifier(f.Accidental({ type: '##' }), 0); gracenotes3[3].addModifier(f.Accidental({ type: 'bb' }), 0); Dot.buildAndAttach([gracenotes4[0]], { all: true }); const notes = [ f .StaveNote({ keys: ['b/4'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes }).beamNotes(), 0), f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.Accidental({ type: '#' }), 0) .addModifier(f.GraceNoteGroup({ notes: gracenotes1 }).beamNotes(), 0), f .StaveNote({ keys: ['c/5', 'd/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes2 }).beamNotes(), 0), f .StaveNote({ keys: ['a/4'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes3 }).beamNotes(), 0), f .StaveNote({ keys: ['a/4'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes4 }).beamNotes().setPosition(ModifierPosition.RIGHT), 0), ]; const voice = f.Voice().setStrict(false).addTickables(notes); new Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true, 'GraceNoteBasic'); } function graceNoteModifiers(options) { const f = VexFlowTests.makeFactory(options, 700, 130); const stave = f.Stave({ x: 10, y: 10, width: 650 }); const gracenotes = [{ keys: ['b/4'], duration: '8', slash: false }].map(f.GraceNote.bind(f)); const notes = [ f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes }), 0), f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes }), 0) .addModifier(new Articulation('a-').setPosition(3), 0), f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes }), 0) .addModifier(new Articulation('a-').setPosition(3), 0) .addModifier(new Accidental('#')), f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes }), 0) .addModifier(new Articulation('a-').setPosition(3), 0) .addModifier(new Annotation('words')), f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes }), 0) .addModifier(new Articulation('a-').setPosition(3), 0) .addModifier(new Articulation('a>').setPosition(3), 0), f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes }), 0) .addModifier(new Articulation('a-').setPosition(3), 0) .addModifier(new Articulation('a>').setPosition(3), 0) .addModifier(new Articulation('a@a').setPosition(3), 0), ]; const voice = f.Voice().setStrict(false).addTickables(notes); new Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true, 'GraceNoteModifiers'); } function basicSlurred(options) { const f = VexFlowTests.makeFactory(options, 700, 130); const stave = f.Stave({ x: 10, y: 10, width: 650 }); const gracenotes0 = [ { keys: ['e/4'], duration: '32' }, { keys: ['f/4'], duration: '32' }, { keys: ['g/4'], duration: '32' }, { keys: ['a/4'], duration: '32' }, ].map(f.GraceNote.bind(f)); const gracenotes1 = [{ keys: ['b/4'], duration: '8', slash: false }].map(f.GraceNote.bind(f)); const gracenotes2 = [{ keys: ['b/4'], duration: '8', slash: true }].map(f.GraceNote.bind(f)); const gracenotes3 = [ { keys: ['e/4'], duration: '8' }, { keys: ['f/4'], duration: '16' }, { keys: ['e/4', 'g/4'], duration: '8' }, { keys: ['a/4'], duration: '32' }, { keys: ['b/4'], duration: '32' }, ].map(f.GraceNote.bind(f)); const gracenotes4 = [ { keys: ['a/4'], duration: '8' }, { keys: ['a/4'], duration: '16' }, { keys: ['a/4'], duration: '16' }, ].map(f.GraceNote.bind(f)); gracenotes0[1].addModifier(f.Accidental({ type: '#' }), 0); gracenotes3[3].addModifier(f.Accidental({ type: 'b' }), 0); gracenotes3[2].addModifier(f.Accidental({ type: 'n' }), 0); Dot.buildAndAttach([gracenotes4[0]], { all: true }); const notes = [ f .StaveNote({ keys: ['b/4'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes0, slur: true }).beamNotes(), 0), f .StaveNote({ keys: ['c/5'], duration: '4', autoStem: true }) .addModifier(f.Accidental({ type: '#' }), 0) .addModifier(f.GraceNoteGroup({ notes: gracenotes1, slur: true }).beamNotes(), 0), f .StaveNote({ keys: ['c/5', 'd/5'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes2, slur: true }).beamNotes(), 0), f .StaveNote({ keys: ['a/4'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes3, slur: true }).beamNotes(), 0), f .StaveNote({ keys: ['a/4'], duration: '4', autoStem: true }) .addModifier(f.GraceNoteGroup({ notes: gracenotes4, slur: true }).beamNotes(), 0), f.StaveNote({ keys: ['a/4'], duration: '4', autoStem: true }), ]; const voice = f.Voice().setStrict(false).addTickables(notes); new Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true, 'GraceNoteBasic'); } const createNoteForStemTest = (duration, noteBuilder, keys, stemDirection, slash = false) => { const struct = { duration, slash }; struct.stemDirection = stemDirection; struct.keys = keys; return noteBuilder(struct); }; const durationsForStemTest = ['8', '16', '32', '64', '128']; function stem(options) { const f = VexFlowTests.makeFactory(options, 700, 130); const stave = f.Stave({ x: 10, y: 10, width: 650 }); function createNotes(noteBuilder, keys, stemDirection) { return durationsForStemTest.map((duration) => createNoteForStemTest(duration, noteBuilder, keys, stemDirection)); } function createNoteBlock(keys, stemDirection) { const staveNotes = createNotes(f.StaveNote.bind(f), keys, stemDirection); const gracenotes = createNotes(f.GraceNote.bind(f), keys, stemDirection); staveNotes[0].addModifier(f.GraceNoteGroup({ notes: gracenotes }), 0); return staveNotes; } const voice = f.Voice().setStrict(false); voice.addTickables(createNoteBlock(['g/4'], 1)); voice.addTickables(createNoteBlock(['d/5'], -1)); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true, 'GraceNoteStem'); } function stemWithBeamed(options) { const f = VexFlowTests.makeFactory(options, 700, 130); const stave = f.Stave({ x: 10, y: 10, width: 650 }); function createBeamedNotes(noteBuilder, keys, stemDirection, beams, isGrace = false, notesToBeam) { const ret = []; durationsForStemTest.map((duration) => { const n0 = createNoteForStemTest(duration, noteBuilder, keys, stemDirection); const n1 = createNoteForStemTest(duration, noteBuilder, keys, stemDirection); ret.push(n0); ret.push(n1); if (notesToBeam) { notesToBeam.push([n0, n1]); } if (!isGrace) { beams.push(f.Beam({ notes: [n0, n1] })); } }); return ret; } function createBeamedNoteBlock(keys, stemDirection, beams) { const bnotes = createBeamedNotes(f.StaveNote.bind(f), keys, stemDirection, beams); const notesToBeam = []; const gracenotes = createBeamedNotes(f.GraceNote.bind(f), keys, stemDirection, beams, true, notesToBeam); const graceNoteGroup = f.GraceNoteGroup({ notes: gracenotes }); notesToBeam.map(graceNoteGroup.beamNotes.bind(graceNoteGroup)); bnotes[0].addModifier(graceNoteGroup, 0); return bnotes; } const beams = []; const voice = f.Voice().setStrict(false); voice.addTickables(createBeamedNoteBlock(options.params.keys1, options.params.stemDirection1, beams)); voice.addTickables(createBeamedNoteBlock(options.params.keys2, options.params.stemDirection2, beams)); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true, 'GraceNoteStem'); } function slash(options) { const f = VexFlowTests.makeFactory(options, 700, 130); const stave = f.Stave({ x: 10, y: 10, width: 650 }); function createNotes(noteT, keys, stemDirection, slash) { return durationsForStemTest.map((d) => createNoteForStemTest(d, noteT, keys, stemDirection, slash)); } function createNoteBlock(keys, stemDirection) { const notes = [f.StaveNote({ keys: ['f/4'], stemDirection, duration: '16' })]; let graceNotes = createNotes(f.GraceNote.bind(f), keys, stemDirection, true); const duration = '8'; const gns = [ { keys: ['d/4', 'a/4'], stemDirection, duration, slash: true }, { keys: ['d/4', 'a/4'], stemDirection, duration, slash: true }, { keys: ['d/4', 'a/4'], stemDirection, duration, slash: true }, { keys: ['e/4', 'a/4'], stemDirection, duration, slash: true }, { keys: ['e/4', 'a/4'], stemDirection, duration, slash: true }, { keys: ['b/4', 'f/5'], stemDirection, duration, slash: true }, { keys: ['b/4', 'f/5'], stemDirection, duration, slash: true }, { keys: ['b/4', 'f/5'], stemDirection, duration, slash: true }, { keys: ['e/4', 'a/4'], stemDirection, duration, slash: true }, ].map(f.GraceNote.bind(f)); const notesToBeam = []; notesToBeam.push([gns[0], gns[1], gns[2]]); notesToBeam.push([gns[3], gns[4], gns[5]]); notesToBeam.push([gns[6], gns[7], gns[8]]); graceNotes = graceNotes.concat(gns); const graceNoteGroup = f.GraceNoteGroup({ notes: graceNotes }); notesToBeam.forEach((notes) => graceNoteGroup.beamNotes(notes)); notes[0].addModifier(graceNoteGroup, 0); return notes; } const voice = f.Voice().setStrict(false); voice.addTickables(createNoteBlock(['d/4', 'a/4'], 1)); voice.addTickables(createNoteBlock(['d/4', 'a/4'], -1)); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true, 'GraceNoteSlash'); } function slashWithBeams(options) { const f = VexFlowTests.makeFactory(options, 800, 130); const stave = f.Stave({ x: 10, y: 10, width: 750 }); function createNoteBlock(keys, stemDirection) { const notes = [f.StaveNote({ keys: ['f/4'], stemDirection, duration: '16' })]; let allGraceNotes = []; const graceNotesToBeam = []; ['8', '16', '32', '64'].forEach(function (duration) { const graceNotes = [ { keys: ['d/4', 'a/4'], stemDirection, duration, slash: true }, { keys: ['d/4', 'a/4'], stemDirection, duration, slash: false }, { keys: ['e/4', 'a/4'], stemDirection, duration, slash: true }, { keys: ['b/4', 'f/5'], stemDirection, duration, slash: false }, { keys: ['b/4', 'f/5'], stemDirection, duration, slash: true }, { keys: ['e/4', 'a/4'], stemDirection, duration, slash: false }, ].map(f.GraceNote.bind(f)); graceNotesToBeam.push([graceNotes[0], graceNotes[1]]); graceNotesToBeam.push([graceNotes[2], graceNotes[3]]); graceNotesToBeam.push([graceNotes[4], graceNotes[5]]); allGraceNotes = allGraceNotes.concat(graceNotes); }); const graceNoteGroup = f.GraceNoteGroup({ notes: allGraceNotes }); graceNotesToBeam.forEach((g) => graceNoteGroup.beamNotes(g)); notes[0].addModifier(graceNoteGroup, 0); return notes; } const voice = f.Voice().setStrict(false); voice.addTickables(createNoteBlock(['d/4', 'a/4'], 1)); voice.addTickables(createNoteBlock(['d/4', 'a/4'], -1)); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true, 'GraceNoteSlashWithBeams'); } function multipleVoices(options) { const f = VexFlowTests.makeFactory(options, 450, 140); const stave = f.Stave({ x: 10, y: 10, width: 450 }); const notes = [ { keys: ['f/5'], stemDirection: 1, duration: '16' }, { keys: ['f/5'], stemDirection: 1, duration: '16' }, { keys: ['d/5'], stemDirection: 1, duration: '16' }, { keys: ['c/5'], stemDirection: 1, duration: '16' }, { keys: ['c/5'], stemDirection: 1, duration: '16' }, { keys: ['d/5'], stemDirection: 1, duration: '16' }, { keys: ['f/5'], stemDirection: 1, duration: '16' }, { keys: ['e/5'], stemDirection: 1, duration: '16' }, ].map(f.StaveNote.bind(f)); const notes2 = [ { keys: ['f/4'], stemDirection: -1, duration: '16' }, { keys: ['e/4'], stemDirection: -1, duration: '16' }, { keys: ['d/4'], stemDirection: -1, duration: '16' }, { keys: ['c/4'], stemDirection: -1, duration: '16' }, { keys: ['c/4'], stemDirection: -1, duration: '16' }, { keys: ['d/4'], stemDirection: -1, duration: '16' }, { keys: ['f/4'], stemDirection: -1, duration: '16' }, { keys: ['e/4'], stemDirection: -1, duration: '16' }, ].map(f.StaveNote.bind(f)); const gracenotes1 = [{ keys: ['b/4'], stemDirection: 1, duration: '8', slash: true }].map(f.GraceNote.bind(f)); const gracenotes2 = [{ keys: ['f/4'], stemDirection: -1, duration: '8', slash: true }].map(f.GraceNote.bind(f)); const gracenotes3 = [ { keys: ['f/4'], duration: '32', stemDirection: -1 }, { keys: ['e/4'], duration: '32', stemDirection: -1 }, ].map(f.GraceNote.bind(f)); const gracenotes4 = [ { keys: ['f/5'], duration: '32', stemDirection: 1 }, { keys: ['e/5'], duration: '32', stemDirection: 1 }, { keys: ['e/5'], duration: '8', stemDirection: 1 }, ].map(f.GraceNote.bind(f)); gracenotes2[0].setStemDirection(-1); gracenotes2[0].addModifier(f.Accidental({ type: '#' }), 0); notes[1].addModifier(f.GraceNoteGroup({ notes: gracenotes4 }).beamNotes(), 0); notes[3].addModifier(f.GraceNoteGroup({ notes: gracenotes1 }), 0); notes2[1].addModifier(f.GraceNoteGroup({ notes: gracenotes2 }).beamNotes(), 0); notes2[5].addModifier(f.GraceNoteGroup({ notes: gracenotes3 }).beamNotes(), 0); const voice = f.Voice().setStrict(false).addTickables(notes); const voice2 = f.Voice().setStrict(false).addTickables(notes2); f.Beam({ notes: notes.slice(0, 4) }); f.Beam({ notes: notes.slice(4, 8) }); f.Beam({ notes: notes2.slice(0, 4) }); f.Beam({ notes: notes2.slice(4, 8) }); f.Formatter().joinVoices([voice, voice2]).formatToStave([voice, voice2], stave); f.draw(); options.assert.ok(true, 'Sixteenth Test'); } function multipleVoicesMultipleDraws(options) { const f = VexFlowTests.makeFactory(options, 450, 140); const stave = f.Stave({ x: 10, y: 10, width: 450 }); const notes = [ { keys: ['f/5'], stemDirection: 1, duration: '16' }, { keys: ['f/5'], stemDirection: 1, duration: '16' }, { keys: ['d/5'], stemDirection: 1, duration: '16' }, { keys: ['c/5'], stemDirection: 1, duration: '16' }, { keys: ['c/5'], stemDirection: 1, duration: '16' }, { keys: ['d/5'], stemDirection: 1, duration: '16' }, { keys: ['f/5'], stemDirection: 1, duration: '16' }, { keys: ['e/5'], stemDirection: 1, duration: '16' }, ].map(f.StaveNote.bind(f)); const notes2 = [ { keys: ['f/4'], stemDirection: -1, duration: '16' }, { keys: ['e/4'], stemDirection: -1, duration: '16' }, { keys: ['d/4'], stemDirection: -1, duration: '16' }, { keys: ['c/4'], stemDirection: -1, duration: '16' }, { keys: ['c/4'], stemDirection: -1, duration: '16' }, { keys: ['d/4'], stemDirection: -1, duration: '16' }, { keys: ['f/4'], stemDirection: -1, duration: '16' }, { keys: ['e/4'], stemDirection: -1, duration: '16' }, ].map(f.StaveNote.bind(f)); const gracenotes1 = [{ keys: ['b/4'], stemDirection: 1, duration: '8', slash: true }].map(f.GraceNote.bind(f)); const gracenotes2 = [{ keys: ['f/4'], stemDirection: -1, duration: '8', slash: true }].map(f.GraceNote.bind(f)); const gracenotes3 = [ { keys: ['f/4'], duration: '32', stemDirection: -1 }, { keys: ['e/4'], duration: '32', stemDirection: -1 }, ].map(f.GraceNote.bind(f)); const gracenotes4 = [ { keys: ['f/5'], duration: '32', stemDirection: 1 }, { keys: ['e/5'], duration: '32', stemDirection: 1 }, { keys: ['e/5'], duration: '8', stemDirection: 1 }, ].map(f.GraceNote.bind(f)); gracenotes2[0].setStemDirection(-1); gracenotes2[0].addModifier(f.Accidental({ type: '#' }), 0); notes[1].addModifier(f.GraceNoteGroup({ notes: gracenotes4 }).beamNotes(), 0); notes[3].addModifier(f.GraceNoteGroup({ notes: gracenotes1 }), 0); notes2[1].addModifier(f.GraceNoteGroup({ notes: gracenotes2 }).beamNotes(), 0); notes2[5].addModifier(f.GraceNoteGroup({ notes: gracenotes3 }).beamNotes(), 0); const voice = f.Voice().setStrict(false).addTickables(notes); const voice2 = f.Voice().setStrict(false).addTickables(notes2); f.Beam({ notes: notes.slice(0, 4) }); f.Beam({ notes: notes.slice(4, 8) }); f.Beam({ notes: notes2.slice(0, 4) }); f.Beam({ notes: notes2.slice(4, 8) }); f.Formatter().joinVoices([voice, voice2]).formatToStave([voice, voice2], stave); f.draw(); f.draw(); options.assert.ok(true, 'Seventeenth Test'); } VexFlowTests.register(GraceNoteTests); export { GraceNoteTests };