vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
261 lines (260 loc) • 14.5 kB
JavaScript
import { VexFlow } from '../src/vexflow.js';
import { VexFlowTests } from './vexflow_test_helpers.js';
import { Beam } from '../src/beam.js';
import { Dot } from '../src/dot.js';
import { Formatter } from '../src/formatter.js';
import { Stave } from '../src/stave.js';
import { StaveNote } from '../src/stavenote.js';
import { Tuplet } from '../src/tuplet.js';
import { Voice } from '../src/voice.js';
const RestsTests = {
Start() {
QUnit.module('Rests');
const run = VexFlowTests.runTests;
run('Outside Stave', ledgerRest);
run('Dotted', basic);
run('Auto Align - Beamed Notes Stems Up', beamsUp);
run('Auto Align - Beamed Notes Stems Down', beamsDown);
run('Auto Align - Tuplets Stems Up', tupletsUp);
run('Auto Align - Tuplets Stems Down', tupletsDown);
run('Auto Align - Single Voice (Default)', singleVoiceDefaultAlignment);
run('Auto Align - Single Voice (Align All)', singleVoiceAlignAll);
run('Auto Align - Multi Voice', multiVoice);
},
};
function setupContext(options, contextBuilder, width = 350, height = 150) {
const context = contextBuilder(options.elementId, width, height);
context.scale(0.9, 0.9);
context.font = '10pt Arial';
const stave = new Stave(10, 30, width).addClef('treble').addTimeSignature('4/4').setContext(context).drawWithStyle();
return { context, stave };
}
function basic(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 700);
const notes = [
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: 'wr' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: 'hr' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: '4r' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: '8r' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: '16r' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: '32r' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: '64r' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: '128r' }),
];
Dot.buildAndAttach(notes, { all: true });
Formatter.FormatAndDraw(context, stave, notes);
options.assert.ok(true, 'Dotted Rest Test');
}
function ledgerRest(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 700);
const notes = [
new StaveNote({ keys: ['a/5'], stemDirection: 1, duration: 'wr' }),
new StaveNote({ keys: ['c/6'], stemDirection: 1, duration: 'hr' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: 'hr' }),
new StaveNote({ keys: ['a/3'], stemDirection: 1, duration: 'wr' }),
new StaveNote({ keys: ['f/3'], stemDirection: 1, duration: 'hr' }),
new StaveNote({ keys: ['b/4'], stemDirection: 1, duration: 'wr' }),
];
Formatter.FormatAndDraw(context, stave, notes);
options.assert.ok(true, 'Leger/Ledger Rest Test');
}
const note = (noteStruct) => new StaveNote(noteStruct);
function beamsUp(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 600, 160);
const notes = [
note({ keys: ['e/5'], stemDirection: 1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '8r' }),
note({ keys: ['b/5'], stemDirection: 1, duration: '8' }),
note({ keys: ['c/5'], stemDirection: 1, duration: '8' }),
note({ keys: ['b/4', 'd/5', 'a/5'], stemDirection: 1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '8r' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '8r' }),
note({ keys: ['c/4'], stemDirection: 1, duration: '8' }),
note({ keys: ['b/4', 'd/5', 'a/5'], stemDirection: 1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '8r' }),
note({ keys: ['c/4'], stemDirection: 1, duration: '8' }),
];
const beam1 = new Beam(notes.slice(0, 4));
const beam2 = new Beam(notes.slice(4, 8));
const beam3 = new Beam(notes.slice(8, 12));
Formatter.FormatAndDraw(context, stave, notes);
beam1.setContext(context).drawWithStyle();
beam2.setContext(context).drawWithStyle();
beam3.setContext(context).drawWithStyle();
options.assert.ok(true, 'Auto Align Rests - Beams Up Test');
}
function beamsDown(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 600, 160);
const notes = [
note({ keys: ['a/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['b/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['c/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4', 'd/5', 'a/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['e/4'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4', 'd/5', 'a/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['e/4'], stemDirection: -1, duration: '8' }),
];
const beam1 = new Beam(notes.slice(0, 4));
const beam2 = new Beam(notes.slice(4, 8));
const beam3 = new Beam(notes.slice(8, 12));
Formatter.FormatAndDraw(context, stave, notes);
beam1.setContext(context).drawWithStyle();
beam2.setContext(context).drawWithStyle();
beam3.setContext(context).drawWithStyle();
options.assert.ok(true, 'Auto Align Rests - Beams Down Test');
}
function tupletsUp(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 600, 160);
const notes = [
note({ keys: ['b/4'], stemDirection: 1, duration: '4' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '4' }),
note({ keys: ['a/5'], stemDirection: 1, duration: '4r' }),
note({ keys: ['a/5'], stemDirection: 1, duration: '4r' }),
note({ keys: ['g/5'], stemDirection: 1, duration: '4r' }),
note({ keys: ['b/5'], stemDirection: 1, duration: '4' }),
note({ keys: ['a/5'], stemDirection: 1, duration: '4' }),
note({ keys: ['g/5'], stemDirection: 1, duration: '4r' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '4' }),
note({ keys: ['a/5'], stemDirection: 1, duration: '4' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '4r' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '4r' }),
];
const tuplet1 = new Tuplet(notes.slice(0, 3)).setTupletLocation(Tuplet.LOCATION_TOP);
const tuplet2 = new Tuplet(notes.slice(3, 6)).setTupletLocation(Tuplet.LOCATION_TOP);
const tuplet3 = new Tuplet(notes.slice(6, 9)).setTupletLocation(Tuplet.LOCATION_TOP);
const tuplet4 = new Tuplet(notes.slice(9, 12)).setTupletLocation(Tuplet.LOCATION_TOP);
Formatter.FormatAndDraw(context, stave, notes);
tuplet1.setContext(context).drawWithStyle();
tuplet2.setContext(context).drawWithStyle();
tuplet3.setContext(context).drawWithStyle();
tuplet4.setContext(context).drawWithStyle();
options.assert.ok(true, 'Auto Align Rests - Tuplets Stem Up Test');
}
function tupletsDown(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 600, 160);
const notes = [
note({ keys: ['a/5'], stemDirection: -1, duration: '8r' }),
note({ keys: ['g/5'], stemDirection: -1, duration: '8r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8' }),
note({ keys: ['a/5'], stemDirection: -1, duration: '8r' }),
note({ keys: ['g/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['a/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['g/5'], stemDirection: -1, duration: '8r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8' }),
note({ keys: ['a/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['g/5'], stemDirection: -1, duration: '8r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
];
const beam1 = new Beam(notes.slice(0, 3));
const beam2 = new Beam(notes.slice(3, 6));
const beam3 = new Beam(notes.slice(6, 9));
const beam4 = new Beam(notes.slice(9, 12));
const tuplet1 = new Tuplet(notes.slice(0, 3)).setTupletLocation(Tuplet.LOCATION_BOTTOM);
const tuplet2 = new Tuplet(notes.slice(3, 6)).setTupletLocation(Tuplet.LOCATION_BOTTOM);
const tuplet3 = new Tuplet(notes.slice(6, 9)).setTupletLocation(Tuplet.LOCATION_BOTTOM);
const tuplet4 = new Tuplet(notes.slice(9, 12)).setTupletLocation(Tuplet.LOCATION_BOTTOM);
Formatter.FormatAndDraw(context, stave, notes);
tuplet1.setContext(context).drawWithStyle();
tuplet2.setContext(context).drawWithStyle();
tuplet3.setContext(context).drawWithStyle();
tuplet4.setContext(context).drawWithStyle();
beam1.setContext(context).drawWithStyle();
beam2.setContext(context).drawWithStyle();
beam3.setContext(context).drawWithStyle();
beam4.setContext(context).drawWithStyle();
options.assert.ok(true, 'Auto Align Rests - Tuplets Stem Down Test');
}
function singleVoiceDefaultAlignment(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 600, 160);
const notes = [
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
note({ keys: ['f/4'], stemDirection: -1, duration: '4' }),
note({ keys: ['e/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['a/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8' }),
note({ keys: ['e/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['a/5'], stemDirection: 1, duration: '4' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '4r' }),
note({ keys: ['b/5'], stemDirection: 1, duration: '4' }),
note({ keys: ['d/5'], stemDirection: -1, duration: '4' }),
note({ keys: ['g/5'], stemDirection: -1, duration: '4' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
];
const beam = new Beam(notes.slice(5, 9));
const tuplet = new Tuplet(notes.slice(9, 12)).setTupletLocation(Tuplet.LOCATION_TOP);
Formatter.FormatAndDraw(context, stave, notes);
tuplet.setContext(context).drawWithStyle();
beam.setContext(context).drawWithStyle();
options.assert.ok(true, 'Auto Align Rests - Default Test');
}
function singleVoiceAlignAll(options, contextBuilder) {
const { context, stave } = setupContext(options, contextBuilder, 600, 160);
const notes = [
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
note({ keys: ['f/4'], stemDirection: -1, duration: '4' }),
note({ keys: ['e/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['a/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '8' }),
note({ keys: ['e/5'], stemDirection: -1, duration: '8' }),
note({ keys: ['a/5'], stemDirection: 1, duration: '4' }),
note({ keys: ['b/4'], stemDirection: 1, duration: '4r' }),
note({ keys: ['b/5'], stemDirection: 1, duration: '4' }),
note({ keys: ['d/5'], stemDirection: -1, duration: '4' }),
note({ keys: ['g/5'], stemDirection: -1, duration: '4' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
note({ keys: ['b/4'], stemDirection: -1, duration: '4r' }),
];
const beam = new Beam(notes.slice(5, 9));
const tuplet = new Tuplet(notes.slice(9, 12)).setTupletLocation(Tuplet.LOCATION_TOP);
Formatter.FormatAndDraw(context, stave, notes, { alignRests: true });
tuplet.setContext(context).drawWithStyle();
beam.setContext(context).drawWithStyle();
options.assert.ok(true, 'Auto Align Rests - Align All Test');
}
function multiVoice(options, contextBuilder) {
const ctx = contextBuilder(options.elementId, 600, 200);
const stave = new Stave(50, 10, 500).addClef('treble').setContext(ctx).addTimeSignature('4/4').drawWithStyle();
const noteOnStave = (noteStruct) => new StaveNote(noteStruct).setStave(stave);
const notes1 = [
noteOnStave({ keys: ['c/4', 'e/4', 'g/4'], duration: '4' }),
noteOnStave({ keys: ['b/4'], duration: '4r' }),
noteOnStave({ keys: ['c/4', 'd/4', 'a/4'], duration: '4' }),
noteOnStave({ keys: ['b/4'], duration: '4r' }),
];
const notes2 = [
noteOnStave({ keys: ['e/3'], stemDirection: -1, duration: '8' }),
noteOnStave({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
noteOnStave({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
noteOnStave({ keys: ['e/3'], stemDirection: -1, duration: '8' }),
noteOnStave({ keys: ['e/3'], stemDirection: -1, duration: '8' }),
noteOnStave({ keys: ['b/4'], stemDirection: -1, duration: '8r' }),
noteOnStave({ keys: ['e/3'], stemDirection: -1, duration: '8' }),
noteOnStave({ keys: ['e/3'], stemDirection: -1, duration: '8' }),
];
const voice1 = new Voice(VexFlow.TIME4_4).addTickables(notes1);
const voice2 = new Voice(VexFlow.TIME4_4).addTickables(notes2);
new Formatter().joinVoices([voice1, voice2]).formatToStave([voice1, voice2], stave, { alignRests: true });
const beam2_1 = new Beam(notes2.slice(0, 4));
const beam2_2 = new Beam(notes2.slice(4, 8));
voice2.draw(ctx);
voice1.draw(ctx);
beam2_1.setContext(ctx).drawWithStyle();
beam2_2.setContext(ctx).drawWithStyle();
options.assert.ok(true, 'Strokes Test Multi Voice');
}
VexFlowTests.register(RestsTests);
export { RestsTests };