vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
937 lines (936 loc) • 47.3 kB
JavaScript
import { VexFlow } from '../src/vexflow.js';
import { VexFlowTests } from './vexflow_test_helpers.js';
import { Accidental } from '../src/accidental.js';
import { Annotation, AnnotationVerticalJustify } from '../src/annotation.js';
import { Articulation } from '../src/articulation.js';
import { Beam } from '../src/beam.js';
import { Dot } from '../src/dot.js';
import { Formatter } from '../src/formatter.js';
import { Fraction } from '../src/fraction.js';
import { FretHandFinger } from '../src/frethandfinger.js';
import { Modifier } from '../src/modifier.js';
import { ModifierContext } from '../src/modifiercontext.js';
import { Ornament } from '../src/ornament.js';
import { Stave } from '../src/stave.js';
import { StaveNote } from '../src/stavenote.js';
import { Stem } from '../src/stem.js';
import { StringNumber } from '../src/stringnumber.js';
import { Stroke } from '../src/strokes.js';
import { TickContext } from '../src/tickcontext.js';
const StaveNoteTests = {
Start() {
QUnit.module('StaveNote');
QUnit.test('Tick', ticks);
QUnit.test('Tick - New API', ticksNewAPI);
QUnit.test('Stem', stem);
QUnit.test('Automatic Stem Direction', autoStem);
QUnit.test('Stem Extension Pitch', stemExtensionPitch);
QUnit.test('Displacement after calling setStemDirection', setStemDirectionDisplacement);
QUnit.test('StaveLine', staveLine);
QUnit.test('Width', width);
QUnit.test('TickContext', tickContext);
const run = VexFlowTests.runTests;
run('StaveNote Draw - Treble', drawBasic, { clef: 'treble', octaveShift: 0, restKey: 'r/4' });
run('StaveNote BoundingBoxes - Treble', drawBoundingBoxes, { clef: 'treble', octaveShift: 0, restKey: 'r/4' });
run('StaveNote Draw - Alto', drawBasic, { clef: 'alto', octaveShift: -1, restKey: 'r/4' });
run('StaveNote Draw - Tenor', drawBasic, { clef: 'tenor', octaveShift: -1, restKey: 'r/3' });
run('StaveNote Draw - Bass', drawBasic, { clef: 'bass', octaveShift: -2, restKey: 'r/3' });
run('StaveNote Draw - Harmonic And Muted', drawHarmonicAndMuted);
run('StaveNote Draw - Slash', drawSlash);
run('Displacements', displacements);
run('StaveNote Draw - Bass 2', drawBass);
run('StaveNote Draw - Key Styles', drawKeyStyles);
run('StaveNote Draw - StaveNote Stem Styles', drawNoteStemStyles);
run('StaveNote Draw - StaveNote Stem Lengths', drawNoteStemLengths);
run('StaveNote Draw - StaveNote Flag Styles', drawNoteStylesWithFlag);
run('StaveNote Draw - StaveNote Styles', drawNoteStyles);
run('Stave, Ledger Line, Beam, Stem and Flag Styles', drawBeamStyles);
run('Flag and Dot Placement - Stem Up', dotsAndFlagsStemUp);
run('Flag and Dots Placement - Stem Down', dotsAndFlagsStemDown);
run('Beam and Dot Placement - Stem Up', dotsAndBeamsUp);
run('Beam and Dot Placement - Stem Down', dotsAndBeamsDown);
run('No Padding', noPadding);
run('Note Heads Placement - Simple', noteHeadsSimple);
run('Note Heads Placement - Hidden Notes', noteHeadsHidden);
run('Center Aligned Note', centerAlignedRest);
run('Center Aligned Note with Articulation', centerAlignedRestFermata);
run('Center Aligned Note with Annotation', centerAlignedRestAnnotation);
run('Center Aligned Note - Multi Voice', centerAlignedMultiVoice);
run('Center Aligned Note with Multiple Modifiers', centerAlignedNoteMultiModifiers);
VexFlowTests.runSVGTest('Interactive Mouseover StaveNote', drawBasic, {
clef: 'treble',
octaveShift: 0,
restKey: 'r/4',
ui: true,
});
},
};
const staveNote = (struct) => new StaveNote(struct);
function draw(note, stave, context, x, drawBoundingBox = false, addModifierContext = true) {
note.setStave(stave);
if (addModifierContext) {
note.addToModifierContext(new ModifierContext());
}
new TickContext().addTickable(note).preFormat().setX(x);
note.setContext(context).drawWithStyle();
if (drawBoundingBox) {
const bb = note.getBoundingBox();
context.rect(bb.getX(), bb.getY(), bb.getW(), bb.getH());
context.stroke();
}
return note;
}
function ticks(assert) {
const BEAT = (1 * VexFlow.RESOLUTION) / 4;
const tickTests = {
'Breve note': ['1/2', 8.0, 'n'],
'Whole note': ['w', 4.0, 'n'],
'Quarter note': ['q', 1.0, 'n'],
'Dotted half note': ['hd', 3.0, 'n'],
'Doubled-dotted half note': ['hdd', 3.5, 'n'],
'Triple-dotted half note': ['hddd', 3.75, 'n'],
'Dotted half rest': ['hdr', 3.0, 'r'],
'Double-dotted half rest': ['hddr', 3.5, 'r'],
'Triple-dotted half rest': ['hdddr', 3.75, 'r'],
'Dotted harmonic quarter note': ['qdh', 1.5, 'h'],
'Double-dotted harmonic quarter note': ['qddh', 1.75, 'h'],
'Triple-dotted harmonic quarter note': ['qdddh', 1.875, 'h'],
'Dotted muted 8th note': ['8dm', 0.75, 'm'],
'Double-dotted muted 8th note': ['8ddm', 0.875, 'm'],
'Triple-dotted muted 8th note': ['8dddm', 0.9375, 'm'],
};
Object.keys(tickTests).forEach((testName) => {
const testData = tickTests[testName];
const durationString = testData[0];
const expectedBeats = testData[1];
const expectedNoteType = testData[2];
const note = new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: durationString });
assert.equal(note.getTicks().value(), BEAT * expectedBeats, testName + ' must have ' + expectedBeats + ' beats');
assert.equal(note.getNoteType(), expectedNoteType, 'Note type must be ' + expectedNoteType);
});
assert.throws(() => new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '8.7dddm' }), /BadArguments/, "Invalid note duration '8.7' throws BadArguments exception");
assert.throws(() => new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2Z' }), /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
assert.throws(() => new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2dddZ' }), /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
}
function ticksNewAPI(assert) {
const BEAT = (1 * VexFlow.RESOLUTION) / 4;
const tickTests = {
'Breve note': [{ duration: '1/2' }, 8.0, 'n'],
'Whole note': [{ duration: 'w' }, 4.0, 'n'],
'Quarter note': [{ duration: 'q' }, 1.0, 'n'],
'Dotted half note': [{ duration: 'h', dots: 1 }, 3.0, 'n'],
'Doubled-dotted half note': [{ duration: 'h', dots: 2 }, 3.5, 'n'],
'Triple-dotted half note': [{ duration: 'h', dots: 3 }, 3.75, 'n'],
'Dotted half rest': [{ duration: 'h', dots: 1, type: 'r' }, 3.0, 'r'],
'Double-dotted half rest': [{ duration: 'h', dots: 2, type: 'r' }, 3.5, 'r'],
'Triple-dotted half rest': [{ duration: 'h', dots: 3, type: 'r' }, 3.75, 'r'],
'Dotted harmonic quarter note': [{ duration: 'q', dots: 1, type: 'h' }, 1.5, 'h'],
'Double-dotted harmonic quarter note': [{ duration: 'q', dots: 2, type: 'h' }, 1.75, 'h'],
'Triple-dotted harmonic quarter note': [{ duration: 'q', dots: 3, type: 'h' }, 1.875, 'h'],
'Dotted muted 8th note': [{ duration: '8', dots: 1, type: 'm' }, 0.75, 'm'],
'Double-dotted muted 8th note': [{ duration: '8', dots: 2, type: 'm' }, 0.875, 'm'],
'Triple-dotted muted 8th note': [{ duration: '8', dots: 3, type: 'm' }, 0.9375, 'm'],
};
Object.keys(tickTests).forEach(function (testName) {
const testData = tickTests[testName];
const noteData = testData[0];
const expectedBeats = testData[1];
const expectedNoteType = testData[2];
noteData.keys = ['c/4', 'e/4', 'g/4'];
const note = new StaveNote(noteData);
assert.equal(note.getTicks().value(), BEAT * expectedBeats, testName + ' must have ' + expectedBeats + ' beats');
assert.equal(note.getNoteType(), expectedNoteType, 'Note type must be ' + expectedNoteType);
});
assert.throws(() => new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '8.7dddm' }), /BadArguments/, "Invalid note duration '8.7' throws BadArguments exception");
assert.throws(() => new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2Z' }), /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
assert.throws(() => new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2dddZ' }), /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
}
function stem(assert) {
const note = new StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: 'w' });
assert.equal(note.getStemDirection(), Stem.UP, 'Default note has UP stem');
}
function autoStem(assert) {
const testData = [
[['c/5', 'e/5', 'g/5'], Stem.DOWN],
[['e/4', 'g/4', 'c/5'], Stem.UP],
[['c/5'], Stem.DOWN],
[['a/4', 'e/5', 'g/5'], Stem.DOWN],
[['b/4'], Stem.DOWN],
];
testData.forEach((td) => {
const keys = td[0];
const expectedStemDirection = td[1];
const note = new StaveNote({ keys: keys, autoStem: true, duration: '8' });
assert.equal(note.getStemDirection(), expectedStemDirection, 'Stem must be ' + (expectedStemDirection === Stem.UP ? 'up' : 'down'));
});
}
function stemExtensionPitch(assert) {
const testData = [
[['c/5', 'e/5', 'g/5'], 0, 0],
[['e/4', 'g/4', 'c/5'], 0, 0],
[['c/5'], 0, 0],
[['f/3'], 15, 0],
[['f/3'], 15, Stem.UP],
[['f/3'], 0, Stem.DOWN],
[['f/3', 'e/5'], 0, 0],
[['g/6'], 25, 0],
[['g/6'], 25, Stem.DOWN],
[['g/6'], 0, Stem.UP],
];
testData.forEach((td) => {
const keys = td[0];
const expectedStemExtension = td[1];
const overrideStemDirection = td[2];
let note;
if (overrideStemDirection === 0) {
note = new StaveNote({ keys: keys, autoStem: true, duration: '4' });
}
else {
note = new StaveNote({ keys: keys, duration: '4', stemDirection: overrideStemDirection });
}
assert.equal(note.getStemExtension(), expectedStemExtension, 'For ' + keys.toString() + ' StemExtension must be ' + expectedStemExtension);
const stave = new Stave(10, 10, 300, { spacingBetweenLinesPx: 20 });
note.setStave(stave);
assert.equal(note.getStemExtension(), expectedStemExtension * 2, 'For wide staff ' + keys.toString() + ' StemExtension must be ' + expectedStemExtension * 2);
});
}
function setStemDirectionDisplacement(assert) {
function getDisplacements(note) {
return note.noteHeads.map((noteHead) => noteHead.isDisplaced());
}
const stemUpDisplacements = [false, true, false];
const stemDownDisplacements = [true, false, false];
const note = new StaveNote({ keys: ['c/5', 'd/5', 'g/5'], stemDirection: Stem.UP, duration: '4' });
assert.deepEqual(getDisplacements(note), stemUpDisplacements);
note.setStemDirection(Stem.DOWN);
assert.deepEqual(getDisplacements(note), stemDownDisplacements);
note.setStemDirection(Stem.UP);
assert.deepEqual(getDisplacements(note), stemUpDisplacements);
}
function staveLine(assert) {
const stave = new Stave(10, 10, 300);
const note = new StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' });
note.setStave(stave);
const props = note.getKeyProps();
assert.equal(props[0].line, 0, 'C/4 on line 0');
assert.equal(props[1].line, 1, 'E/4 on line 1');
assert.equal(props[2].line, 2.5, 'A/4 on line 2.5');
const ys = note.getYs();
assert.equal(ys.length, 3, 'Chord should be rendered on three lines');
assert.equal(ys[0], 100, 'Line for C/4');
assert.equal(ys[1], 90, 'Line for E/4');
assert.equal(ys[2], 75, 'Line for A/4');
}
function width(assert) {
const note = new StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' });
assert.throws(() => note.getWidth(), /UnformattedNote/, 'Unformatted note should have no width');
}
function tickContext(assert) {
const stave = new Stave(10, 10, 400);
const note = new StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' }).setStave(stave);
new TickContext().addTickable(note).preFormat().setX(10).setPadding(0);
assert.expect(0);
}
function drawBasic(options, contextBuilder) {
const clef = options.params.clef;
const octaveShift = options.params.octaveShift;
const restKey = options.params.restKey;
const ctx = contextBuilder(options.elementId, 700, 180);
const stave = new Stave(10, 30, 750);
stave.setContext(ctx);
stave.addClef(clef);
stave.drawWithStyle();
const lowerKeys = ['c/', 'e/', 'a/'];
const higherKeys = ['c/', 'e/', 'a/'];
for (let k = 0; k < lowerKeys.length; k++) {
lowerKeys[k] = lowerKeys[k] + (4 + octaveShift);
higherKeys[k] = higherKeys[k] + (5 + octaveShift);
}
const restKeys = [restKey];
const noteStructs = [
{ clef: clef, keys: higherKeys, duration: '1/2' },
{ clef: clef, keys: lowerKeys, duration: 'w' },
{ clef: clef, keys: higherKeys, duration: 'h' },
{ clef: clef, keys: lowerKeys, duration: 'q' },
{ clef: clef, keys: higherKeys, duration: '8' },
{ clef: clef, keys: lowerKeys, duration: '16' },
{ clef: clef, keys: higherKeys, duration: '32' },
{ clef: clef, keys: higherKeys, duration: '64' },
{ clef: clef, keys: higherKeys, duration: '128' },
{ clef: clef, keys: lowerKeys, duration: '1/2', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: 'w', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: 'h', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: 'q', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '8', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '16', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '32', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '64', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '128', stemDirection: Stem.DOWN },
{ clef: clef, keys: restKeys, duration: '1/2r' },
{ clef: clef, keys: restKeys, duration: 'wr' },
{ clef: clef, keys: restKeys, duration: 'hr' },
{ clef: clef, keys: restKeys, duration: 'qr' },
{ clef: clef, keys: restKeys, duration: '8r' },
{ clef: clef, keys: restKeys, duration: '16r' },
{ clef: clef, keys: restKeys, duration: '32r' },
{ clef: clef, keys: restKeys, duration: '64r' },
{ clef: clef, keys: restKeys, duration: '128r' },
{ keys: ['x/4'], duration: 'h' },
];
options.assert.expect(noteStructs.length * 2);
const colorDescendants = (parentItem, color) => () => parentItem.querySelectorAll('*').forEach((child) => {
child.setAttribute('fill', color);
child.setAttribute('stroke', color);
});
for (let i = 0; i < noteStructs.length; ++i) {
const note = draw(staveNote(noteStructs[i]), stave, ctx, (i + 1) * 25);
if (options.params.ui) {
const item = note.getSVGElement();
if (item) {
item.addEventListener('mouseover', colorDescendants(item, 'green'), false);
item.addEventListener('mouseout', colorDescendants(item, 'black'), false);
}
}
options.assert.ok(note.getX() > 0, 'Note ' + i + ' has X value');
options.assert.ok(note.getYs().length > 0, 'Note ' + i + ' has Y values');
}
}
function drawBoundingBoxes(options, contextBuilder) {
const clef = options.params.clef;
const octaveShift = options.params.octaveShift;
const restKey = options.params.restKey;
const ctx = contextBuilder(options.elementId, 700, 180);
const stave = new Stave(10, 30, 750);
stave.setContext(ctx);
stave.addClef(clef);
stave.drawWithStyle();
const lowerKeys = ['c/', 'e/', 'a/'];
const higherKeys = ['c/', 'e/', 'a/'];
for (let k = 0; k < lowerKeys.length; k++) {
lowerKeys[k] = lowerKeys[k] + (4 + octaveShift);
higherKeys[k] = higherKeys[k] + (5 + octaveShift);
}
const restKeys = [restKey];
const noteStructs = [
{ clef: clef, keys: higherKeys, duration: '1/2' },
{ clef: clef, keys: lowerKeys, duration: 'w' },
{ clef: clef, keys: higherKeys, duration: 'h' },
{ clef: clef, keys: lowerKeys, duration: 'q' },
{ clef: clef, keys: higherKeys, duration: '8' },
{ clef: clef, keys: lowerKeys, duration: '16' },
{ clef: clef, keys: higherKeys, duration: '32' },
{ clef: clef, keys: higherKeys, duration: '64' },
{ clef: clef, keys: higherKeys, duration: '128' },
{ clef: clef, keys: lowerKeys, duration: '1/2', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: 'w', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: 'h', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: 'q', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '8', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '16', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '32', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '64', stemDirection: Stem.DOWN },
{ clef: clef, keys: lowerKeys, duration: '128' },
{ clef: clef, keys: restKeys, duration: '1/2r' },
{ clef: clef, keys: restKeys, duration: 'wr' },
{ clef: clef, keys: restKeys, duration: 'hr' },
{ clef: clef, keys: restKeys, duration: 'qr' },
{ clef: clef, keys: restKeys, duration: '8r' },
{ clef: clef, keys: restKeys, duration: '16r' },
{ clef: clef, keys: restKeys, duration: '32r' },
{ clef: clef, keys: restKeys, duration: '64r' },
{ clef: clef, keys: restKeys, duration: '128r' },
{ keys: ['x/4'], duration: 'h' },
];
options.assert.expect(noteStructs.length * 2);
const notes = [];
for (let i = 0; i < noteStructs.length; ++i) {
notes.push(staveNote(noteStructs[i]));
}
notes[2].addModifier(new Ornament('tr'), 0);
notes[2].addModifier(new Accidental('b'), 0);
Dot.buildAndAttach([notes[2]], { all: true });
for (let i = 0; i < noteStructs.length; ++i) {
const note = draw(notes[i], stave, ctx, (i + 1) * 25, true, false);
options.assert.ok(note.getX() > 0, 'Note ' + i + ' has X value');
options.assert.ok(note.getYs().length > 0, 'Note ' + i + ' has Y values');
}
}
function drawBass(options, contextBuilder) {
options.assert.expect(40);
const ctx = contextBuilder(options.elementId, 600, 280);
const stave = new Stave(10, 10, 650);
stave.setContext(ctx);
stave.addClef('bass');
stave.drawWithStyle();
const noteStructs = [
{ clef: 'bass', keys: ['c/3', 'e/3', 'a/3'], duration: '1/2' },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: 'w' },
{ clef: 'bass', keys: ['c/3', 'e/3', 'a/3'], duration: 'h' },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: 'q' },
{ clef: 'bass', keys: ['c/3', 'e/3', 'a/3'], duration: '8' },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: '16' },
{ clef: 'bass', keys: ['c/3', 'e/3', 'a/3'], duration: '32' },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: 'h', stemDirection: Stem.DOWN },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: 'q', stemDirection: Stem.DOWN },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: '8', stemDirection: Stem.DOWN },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: '16', stemDirection: Stem.DOWN },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: '32', stemDirection: Stem.DOWN },
{ keys: ['r/4'], duration: '1/2r' },
{ keys: ['r/4'], duration: 'wr' },
{ keys: ['r/4'], duration: 'hr' },
{ keys: ['r/4'], duration: 'qr' },
{ keys: ['r/4'], duration: '8r' },
{ keys: ['r/4'], duration: '16r' },
{ keys: ['r/4'], duration: '32r' },
{ keys: ['x/4'], duration: 'h' },
];
for (let i = 0; i < noteStructs.length; ++i) {
const note = draw(staveNote(noteStructs[i]), stave, ctx, (i + 1) * 25);
options.assert.ok(note.getX() > 0, 'Note ' + i + ' has X value');
options.assert.ok(note.getYs().length > 0, 'Note ' + i + ' has Y values');
}
}
function displacements(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 700, 155);
ctx.scale(0.9, 0.9);
const stave = new Stave(10, 10, 675);
stave.setContext(ctx);
stave.drawWithStyle();
const noteStructs = [
{ keys: ['g/3', 'a/3', 'c/4', 'd/4', 'e/4'], duration: '1/2' },
{ keys: ['g/3', 'a/3', 'c/4', 'd/4', 'e/4'], duration: 'w' },
{ keys: ['d/4', 'e/4', 'f/4'], duration: 'h' },
{ keys: ['f/4', 'g/4', 'a/4', 'b/4'], duration: 'q' },
{ keys: ['e/3', 'b/3', 'c/4', 'e/4', 'f/4', 'g/5', 'a/5'], duration: '8' },
{ keys: ['a/3', 'c/4', 'e/4', 'g/4', 'a/4', 'b/4'], duration: '16' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '32' },
{ keys: ['c/4', 'e/4', 'a/4', 'a/4'], duration: '64' },
{ keys: ['g/3', 'c/4', 'd/4', 'e/4'], duration: 'h', stemDirection: Stem.DOWN },
{ keys: ['d/4', 'e/4', 'f/4'], duration: 'q', stemDirection: Stem.DOWN },
{ keys: ['f/4', 'g/4', 'a/4', 'b/4'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'd/4', 'e/4', 'f/4', 'g/4', 'a/4'], duration: '16', stemDirection: Stem.DOWN },
{ keys: ['b/3', 'c/4', 'e/4', 'a/4', 'b/5', 'c/6', 'e/6'], duration: '32', stemDirection: Stem.DOWN },
{
keys: ['b/3', 'c/4', 'e/4', 'a/4', 'b/5', 'c/6', 'e/6', 'e/6'],
duration: '64',
stemDirection: Stem.DOWN,
},
];
options.assert.expect(noteStructs.length * 2);
for (let i = 0; i < noteStructs.length; ++i) {
const note = draw(staveNote(noteStructs[i]), stave, ctx, (i + 1) * 45);
options.assert.ok(note.getX() > 0, 'Note ' + i + ' has X value');
options.assert.ok(note.getYs().length > 0, 'Note ' + i + ' has Y values');
}
}
function drawHarmonicAndMuted(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 1000, 180);
const stave = new Stave(10, 10, 950);
stave.setContext(ctx);
stave.drawWithStyle();
const noteStructs = [
{ keys: ['c/4', 'e/4', 'a/4'], duration: '1/2h' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'wh' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'hh' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'qh' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '8h' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '16h' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '32h' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '64h' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '128h' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '1/2h', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'wh', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'hh', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'qh', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '8h', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '16h', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '32h', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '64h', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '128h', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '1/2m' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'wm' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'hm' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'qm' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '8m' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '16m' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '32m' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '64m' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '128m' },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '1/2m', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'wm', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'hm', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'qm', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '8m', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '16m', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '32m', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '64m', stemDirection: Stem.DOWN },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '128m', stemDirection: Stem.DOWN },
];
options.assert.expect(noteStructs.length * 2);
for (let i = 0; i < noteStructs.length; ++i) {
const note = draw(staveNote(noteStructs[i]), stave, ctx, i * 25 + 5);
options.assert.ok(note.getX() > 0, 'Note ' + i + ' has X value');
options.assert.ok(note.getYs().length > 0, 'Note ' + i + ' has Y values');
}
}
function drawSlash(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 700, 180);
const stave = new Stave(10, 10, 650);
stave.setContext(ctx);
stave.drawWithStyle();
const notes = [
{ keys: ['b/4'], duration: '1/2s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: 'ws', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: 'hs', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: 'qs', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '16s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '32s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '64s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '128s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '1/2s', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: 'ws', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: 'hs', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: 'qs', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '8s', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '16s', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '32s', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '64s', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '128s', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '8s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8s', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8s', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '8s', stemDirection: Stem.UP },
];
const staveNotes = notes.map((struct) => new StaveNote(struct));
const beam1 = new Beam([staveNotes[16], staveNotes[17]]);
const beam2 = new Beam([staveNotes[18], staveNotes[19]]);
Formatter.FormatAndDraw(ctx, stave, staveNotes, false);
beam1.setContext(ctx).drawWithStyle();
beam2.setContext(ctx).drawWithStyle();
options.assert.ok('Slash Note Heads');
}
function drawKeyStyles(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 300, 280);
ctx.scale(3, 3);
const stave = new Stave(10, 0, 100);
const note = new StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: 'q' })
.setStave(stave)
.addModifier(new Accidental('b'), 1)
.setKeyStyle(1, { shadowBlur: 2, shadowColor: 'blue', fillStyle: 'blue' });
new TickContext().addTickable(note).preFormat().setX(25);
stave.setContext(ctx).drawWithStyle();
note.setContext(ctx).drawWithStyle();
options.assert.ok(note.getX() > 0, 'Note has X value');
options.assert.ok(note.getYs().length > 0, 'Note has Y values');
}
function drawNoteStyles(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 300, 280);
const stave = new Stave(10, 0, 100);
ctx.scale(3, 3);
const note = new StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: '8' })
.setStave(stave)
.addModifier(new Accidental('b'), 1);
note.setStyle({ shadowBlur: 2, shadowColor: 'blue', fillStyle: 'blue', strokeStyle: 'blue' });
new TickContext().addTickable(note).preFormat().setX(25);
stave.setContext(ctx).drawWithStyle();
note.setContext(ctx).drawWithStyle();
options.assert.ok(note.getX() > 0, 'Note has X value');
options.assert.ok(note.getYs().length > 0, 'Note has Y values');
}
function drawNoteStemStyles(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 300, 280);
const stave = new Stave(10, 0, 100);
ctx.scale(3, 3);
const note = new StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: 'q' })
.setStave(stave)
.addModifier(new Accidental('b'), 1);
note.setStemStyle({ shadowBlur: 2, shadowColor: 'blue', fillStyle: 'blue', strokeStyle: 'blue' });
new TickContext().addTickable(note).preFormat().setX(25);
stave.setContext(ctx).drawWithStyle();
note.setContext(ctx).drawWithStyle();
options.assert.ok('Note Stem Style');
}
function drawNoteStemLengths(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 975, 150);
const stave = new Stave(10, 10, 975);
stave.setContext(ctx).drawWithStyle();
const keys = [
'e/3',
'f/3',
'g/3',
'a/3',
'b/3',
'c/4',
'd/4',
'e/4',
'f/4',
'g/4',
'f/5',
'g/5',
'a/5',
'b/5',
'c/6',
'd/6',
'e/6',
'f/6',
'g/6',
'a/6',
];
const notes = [];
let note;
let i;
for (i = 0; i < keys.length; i++) {
let duration = 'q';
if (i % 2 === 1) {
duration = '8';
}
note = new StaveNote({ keys: [keys[i]], duration, autoStem: true }).setStave(stave);
new TickContext().addTickable(note);
note.setContext(ctx);
notes.push(note);
}
const wholeKeys = ['e/3', 'a/3', 'f/5', 'a/5', 'd/6', 'a/6'];
for (i = 0; i < wholeKeys.length; i++) {
note = new StaveNote({ keys: [wholeKeys[i]], duration: 'w' }).setStave(stave);
new TickContext().addTickable(note);
note.setContext(ctx);
notes.push(note);
}
Formatter.FormatAndDraw(ctx, stave, notes);
options.assert.ok('Note Stem Length');
}
function drawNoteStylesWithFlag(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 300, 280);
const stave = new Stave(10, 0, 100);
ctx.scale(3, 3);
const note = new StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: '8' })
.setStave(stave)
.addModifier(new Accidental('b'), 1);
note.setFlagStyle({ shadowBlur: 2, shadowColor: 'blue', fillStyle: 'blue', strokeStyle: 'blue' });
new TickContext().addTickable(note).preFormat().setX(25);
stave.setContext(ctx).drawWithStyle();
note.setContext(ctx).drawWithStyle();
options.assert.ok(note.getX() > 0, 'Note has X value');
options.assert.ok(note.getYs().length > 0, 'Note has Y values');
}
function drawBeamStyles(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 400, 160);
const stave = new Stave(10, 10, 380);
stave.setStyle({ strokeStyle: '#EEAAEE', lineWidth: 3 });
stave.setContext(ctx);
stave.drawWithStyle();
const notes = [
{ keys: ['b/4'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['b/4'], duration: '8', stemDirection: Stem.UP },
{ keys: ['b/4'], duration: '8', stemDirection: Stem.UP },
{ keys: ['d/6'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['c/6', 'd/6'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['d/6', 'e/6'], duration: '8', stemDirection: Stem.DOWN },
{ keys: ['e/6', 'f/6'], duration: '8', stemDirection: Stem.DOWN },
];
const staveNotes = notes.map((note) => new StaveNote(note));
const beam1 = new Beam(staveNotes.slice(0, 2));
const beam2 = new Beam(staveNotes.slice(3, 5));
const beam3 = new Beam(staveNotes.slice(5, 7));
const beam4 = new Beam(staveNotes.slice(7, 9));
beam1.setStyle({ fillStyle: 'blue', strokeStyle: 'blue' });
staveNotes[0].setKeyStyle(0, { fillStyle: 'purple' });
staveNotes[0].setStemStyle({ strokeStyle: 'green' });
staveNotes[1].setStemStyle({ strokeStyle: 'orange' });
staveNotes[1].setKeyStyle(0, { fillStyle: 'darkturquoise' });
staveNotes[5].setStyle({ fillStyle: 'tomato', strokeStyle: 'tomato' });
beam3.setStyle({ shadowBlur: 4, shadowColor: 'blue' });
staveNotes[9].setLedgerLineStyle({ fillStyle: 'lawngreen', strokeStyle: 'lawngreen', lineWidth: 1 });
staveNotes[9].setFlagStyle({ fillStyle: 'orange', strokeStyle: 'orange' });
Formatter.FormatAndDraw(ctx, stave, staveNotes, false);
beam1.setContext(ctx).drawWithStyle();
beam2.setContext(ctx).drawWithStyle();
beam3.setContext(ctx).drawWithStyle();
beam4.setContext(ctx).drawWithStyle();
options.assert.ok('draw beam styles');
}
function dotsAndFlagsStemUp(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 800, 150);
ctx.scale(1.0, 1.0);
const stave = new Stave(10, 10, 975);
const notes = [
staveNote({ keys: ['f/4'], duration: '4', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '8', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '16', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '32', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '64', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '128', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '4', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '8', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '16', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '32' }),
staveNote({ keys: ['g/4'], duration: '64', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '128', stemDirection: Stem.UP }),
];
Dot.buildAndAttach(notes, { all: true });
Dot.buildAndAttach([notes[5], notes[11]], { all: true });
stave.setContext(ctx).drawWithStyle();
for (let i = 0; i < notes.length; ++i) {
draw(notes[i], stave, ctx, i * 65);
}
options.assert.ok(true, 'Full Dot');
}
function dotsAndFlagsStemDown(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 800, 160);
ctx.scale(1.0, 1.0);
const stave = new Stave(10, 10, 975);
const staveNotes = [
staveNote({ keys: ['e/5'], duration: '4', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '8', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '16', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '32', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '64', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '128', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '4', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '8', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '16', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '32', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '64', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '128', stemDirection: Stem.DOWN }),
];
Dot.buildAndAttach(staveNotes, { all: true });
stave.setContext(ctx).drawWithStyle();
for (let i = 0; i < staveNotes.length; ++i) {
draw(staveNotes[i], stave, ctx, i * 65);
}
options.assert.ok(true, 'Full Dot');
}
function dotsAndBeamsUp(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 800, 150);
ctx.scale(1.0, 1.0);
const stave = new Stave(10, 10, 975);
const staveNotes = [
staveNote({ keys: ['f/4'], duration: '8', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '16', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '32', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '64', stemDirection: Stem.UP }),
staveNote({ keys: ['f/4'], duration: '128', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '8', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '16', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '32' }),
staveNote({ keys: ['g/4'], duration: '64', stemDirection: Stem.UP }),
staveNote({ keys: ['g/4'], duration: '128', stemDirection: Stem.UP }),
];
Dot.buildAndAttach(staveNotes, { all: true });
Dot.buildAndAttach([staveNotes[4], staveNotes[9]], { all: true });
const beam = new Beam(staveNotes);
stave.setContext(ctx).drawWithStyle();
for (let i = 0; i < staveNotes.length; ++i) {
draw(staveNotes[i], stave, ctx, i * 65);
}
beam.setContext(ctx).drawWithStyle();
options.assert.ok(true, 'Full Dot');
}
function dotsAndBeamsDown(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 800, 160);
ctx.scale(1.0, 1.0);
const stave = new Stave(10, 10, 975);
const staveNotes = [
staveNote({ keys: ['e/5'], duration: '8', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '16', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '32', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '64', stemDirection: Stem.DOWN }),
staveNote({ keys: ['e/5'], duration: '128', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '8', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '16', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '32', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '64', stemDirection: Stem.DOWN }),
staveNote({ keys: ['d/5'], duration: '128', stemDirection: Stem.DOWN }),
];
Dot.buildAndAttach(staveNotes, { all: true });
const beam = new Beam(staveNotes);
stave.setContext(ctx).drawWithStyle();
for (let i = 0; i < staveNotes.length; ++i) {
draw(staveNotes[i], stave, ctx, i * 65);
}
beam.setContext(ctx).drawWithStyle();
options.assert.ok(true, 'Full Dot');
}
function noteHeadsSimple(options) {
const vf = VexFlowTests.makeFactory(options, 800, 250);
const score = vf.EasyScore();
const system1 = vf.System({ y: 100, x: 50, width: 200 });
system1
.addStave({
voices: [
score.voice([...score.beam(score.notes('a4/8, b4/8', { stem: 'up' })), ...score.notes('a4/q/r, a4/h/r')]),
score.voice(score.notes('g4/w')),
],
})
.addClef('treble')
.addTimeSignature('4/4');
const system2 = vf.System({ y: 100, x: 250, width: 150 });
system2.addStave({
voices: [score.voice(score.notes('b4/h, b4/h/r')), score.voice(score.notes('b4/w'))],
});
const system3 = vf.System({ y: 100, x: 400, width: 150 });
system3.addStave({
voices: [score.voice(score.notes('d5/h, d5/h/r')), score.voice(score.notes('e4/w'))],
});
const system4 = vf.System({ y: 100, x: 550, width: 150 });
system4.addStave({
voices: [
score.voice(score.notes('e4/q, e4/q/r, e4/h/r')),
score.voice(score.notes('e4/8, e4/8/r, e4/q/r, e4/h/r')),
],
});
vf.draw();
options.assert.expect(0);
}
function noPadding(options) {
const vf = VexFlowTests.makeFactory(options, 800, 500);
const score = vf.EasyScore();
function newStave(y, noPadding) {
let system = vf.System({ y, x: 50, width: 200, noPadding });
system
.addStave({
voices: [
score.voice([...score.beam(score.notes('a4/8, b4/8', { stem: 'up' })), ...score.notes('a4/q, a4/h')]),
score.voice(score.notes('g4/w')),
],
})
.addClef('treble')
.addTimeSignature('4/4');
system = vf.System({ y, x: 250, width: 150, noPadding });
system.addStave({
voices: [score.voice(score.notes('b4/h, b4/h')), score.voice(score.notes('b4/w'))],
});
system = vf.System({ y, x: 400, width: 150, noPadding });
system.addStave({
voices: [score.voice(score.notes('d5/h, d5/h')), score.voice(score.notes('e4/w'))],
});
system = vf.System({ y, x: 550, width: 150, noPadding });
system.addStave({
voices: [score.voice(score.notes('e4/q, e4/q, e4/h')), score.voice(score.notes('e4/8, e4/8, e4/q, e4/h'))],
});
}
newStave(100, true);
newStave(200, false);
vf.draw();
options.assert.expect(0);
}
function noteHeadsHidden(options) {
const vf = VexFlowTests.makeFactory(options, 800, 250);
const score = vf.EasyScore();
const system1 = vf.System({ y: 100, x: 50, width: 200 });
const notes1 = score.notes('g4/w');
notes1[0].renderOptions.draw = false;
system1
.addStave({
voices: [
score.voice([...score.beam(score.notes('a4/8, b4/8', { stem: 'up' })), ...score.notes('a4/q/r, a4/h/r')]),
score.voice(notes1),
],
})
.addClef('treble')
.addTimeSignature('4/4');
const system2 = vf.System({ y: 100, x: 250, width: 150 });
const notes2 = score.notes('b4/w');
notes2[0].renderOptions.draw = false;
system2.addStave({
voices: [score.voice(score.notes('b4/h, b4/h/r')), score.voice(notes2)],
});
const system3 = vf.System({ y: 100, x: 400, width: 150 });
system3.addStave({
voices: [score.voice(score.notes('d5/h, d5/h/r')), score.voice(score.notes('e4/w'))],
});
const system4 = vf.System({ y: 100, x: 550, width: 150 });
const notes4 = score.notes('e4/q, e4/q/r, e4/h/r');
notes4[0].renderOptions.draw = false;
notes4[2].renderOptions.draw = false;
system4.addStave({
voices: [score.voice(notes4), score.voice(score.notes('e4/8, e4/8/r, e4/q/r, e4/h/r'))],
});
vf.draw();
options.assert.expect(0);
}
function centerAlignedRest(options) {
const f = VexFlowTests.makeFactory(options, 400, 160);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addClef('treble').addTimeSignature('4/4');
const note = f.StaveNote({ keys: ['b/4'], duration: '1r', alignCenter: true });
const voice = f.Voice().setStrict(false).addTickables([note]);
f.Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true);
}
function centerAlignedRestFermata(options) {
const f = VexFlowTests.makeFactory(options, 400, 160);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addClef('treble').addTimeSignature('4/4');
const note = f
.StaveNote({ keys: ['b/4'], duration: '1r', alignCenter: true })
.addModifier(new Articulation('a@a').setPosition(3), 0);
const voice = f.Voice().setStrict(false).addTickables([note]);
f.Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true);
}
function centerAlignedRestAnnotation(options) {
const f = VexFlowTests.makeFactory(options, 400, 160);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addClef('treble').addTimeSignature('4/4');
const note = f
.StaveNote({ keys: ['b/4'], duration: '1r', alignCenter: true })
.addModifier(new Annotation('Whole measure rest').setVerticalJustification(AnnotationVerticalJustify.TOP), 0);
const voice = f.Voice().setStrict(false).addTickables([note]);
f.Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true);
}
function centerAlignedNoteMultiModifiers(options) {
const f = VexFlowTests.makeFactory(options, 400, 160);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addClef('treble').addTimeSignature('4/4');
function newFinger(num, pos) {
return new FretHandFinger(num).setPosition(pos);
}
const note = f
.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '4', alignCenter: true })
.addModifier(new Annotation('Test').setPosition(3), 0)
.addStroke(0, new Stroke(2))
.addModifier(new Accidental('#'), 1)
.addModifier(newFinger('3', Modifier.Position.LEFT), 0)
.addModifier(newFinger('2', Modifier.Position.LEFT), 2)
.addModifier(newFinger('1', Modifier.Position.RIGHT), 1)
.addModifier(new StringNumber('4').setPosition(Modifier.Position.BELOW), 2);
Dot.buildAndAttach([note], { all: true });
const voice = f.Voice().setStrict(false).addTickables([note]);
f.Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true);
}
function centerAlignedMultiVoice(options) {
const f = VexFlowTests.makeFactory(options, 400, 160);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addClef('treble').addTimeSignature('3/8');
const customDuration = new Fraction(3, 8);
const notes0 = [
f.StaveNote({
keys: ['c/4'],
duration: '1r',
alignCenter: true,
durationOverride: customDuration,
}),
];
const createStaveNote = (struct) => f.StaveNote(struct);
const notes1 = [
{ keys: ['b/4'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
].map(createStaveNote);
notes1[1].addModifier(f.Accidental({ type: '#' }), 0);
f.Beam({ notes: notes1 });
const voice0 = f.Voice({ time: '3/8' }).setStrict(false).addTickables(notes0);
const voice1 = f.Voice({ time: '3/8' }).setStrict(false).addTickables(notes1);
f.Formatter().joinVoices([voice0, voice1]).formatToStave([voice0, voice1], stave);
f.draw();
options.assert.ok(true);
}
VexFlowTests.register(StaveNoteTests);
export { StaveNoteTests };