vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
451 lines (450 loc) • 14.4 kB
JavaScript
import { VexFlowTests } from './vexflow_test_helpers.js';
import { Dot } from '../src/dot.js';
import { Formatter } from '../src/formatter.js';
import { Stem } from '../src/stem.js';
import { Tuplet } from '../src/tuplet.js';
const TupletTests = {
Start() {
QUnit.module('Tuplet');
const run = VexFlowTests.runTests;
run('Simple Tuplet', simple);
run('Beamed Tuplet', beamed);
run('Ratioed Tuplet', ratio);
run('Bottom Tuplet', bottom);
run('Bottom Ratioed Tuplet', bottomRatio);
run('Awkward Tuplet', awkward);
run('Complex Tuplet', complex);
run('Mixed Stem Direction Tuplet', mixedTop);
run('Mixed Stem Direction Bottom Tuplet', mixedBottom);
run('Nested Tuplets', nested);
run('Single Tuplets', single);
},
};
const set = (key) => (value) => (object) => {
object[key] = value;
return object;
};
const setStemDirection = set('stemDirection');
const setStemUp = setStemDirection(Stem.UP);
const setStemDown = setStemDirection(Stem.DOWN);
const setDurationToQuarterNote = set('duration')('4');
function simple(options) {
const f = VexFlowTests.makeFactory(options);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addTimeSignature('3/4');
const notes = [
{ keys: ['g/4'], duration: '4' },
{ keys: ['a/4'], duration: '4' },
{ keys: ['b/4'], duration: '4' },
{ keys: ['b/4'], duration: '8' },
{ keys: ['a/4'], duration: '8' },
{ keys: ['g/4'], duration: '8' },
]
.map(setStemUp)
.map(f.StaveNote.bind(f));
f.Tuplet({ notes: notes.slice(0, 3) });
f.Tuplet({ notes: notes.slice(3, 6) });
const voice = f
.Voice({ time: { numBeats: 3, beatValue: 4 } })
.setStrict(true)
.addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Simple Test');
}
function beamed(options) {
const f = VexFlowTests.makeFactory(options);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addTimeSignature('3/8');
const notes = [
{ keys: ['b/4'], duration: '16' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['g/4'], duration: '16' },
{ keys: ['a/4'], duration: '8' },
{ keys: ['f/4'], duration: '8' },
{ keys: ['a/4'], duration: '8' },
{ keys: ['f/4'], duration: '8' },
{ keys: ['a/4'], duration: '8' },
{ keys: ['f/4'], duration: '8' },
{ keys: ['g/4'], duration: '8' },
]
.map(setStemUp)
.map(f.StaveNote.bind(f));
f.Beam({ notes: notes.slice(0, 3) });
f.Beam({ notes: notes.slice(3, 10) });
f.Tuplet({ notes: notes.slice(0, 3) });
f.Tuplet({ notes: notes.slice(3, 10) });
const voice = f
.Voice({ time: { numBeats: 3, beatValue: 8 } })
.setStrict(true)
.addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Beamed Test');
}
function ratio(options) {
const f = VexFlowTests.makeFactory(options);
const stave = f.Stave({ x: 10, y: 10, width: 350 }).addTimeSignature('4/4');
const notes = [
{ keys: ['f/4'], duration: '4' },
{ keys: ['a/4'], duration: '4' },
{ keys: ['b/4'], duration: '4' },
{ keys: ['g/4'], duration: '8' },
{ keys: ['e/4'], duration: '8' },
{ keys: ['g/4'], duration: '8' },
]
.map(setStemUp)
.map(f.StaveNote.bind(f));
f.Beam({
notes: notes.slice(3, 6),
});
f.Tuplet({
notes: notes.slice(0, 3),
options: {
ratioed: true,
},
});
f.Tuplet({
notes: notes.slice(3, 6),
options: {
ratioed: true,
notesOccupied: 4,
},
});
const voice = f.Voice().setStrict(true).addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Ratioed Test');
}
function bottom(options) {
const f = VexFlowTests.makeFactory(options, 350, 160);
const stave = f.Stave({ x: 10, y: 10 }).addTimeSignature('3/4');
const notes = [
{ keys: ['f/4'], duration: '4' },
{ keys: ['c/4'], duration: '4' },
{ keys: ['g/4'], duration: '4' },
{ keys: ['d/5'], duration: '8' },
{ keys: ['g/3'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
f.Beam({
notes: notes.slice(3, 6),
});
f.Tuplet({
notes: notes.slice(0, 3),
options: { location: Tuplet.LOCATION_BOTTOM },
});
f.Tuplet({
notes: notes.slice(3, 6),
options: { location: Tuplet.LOCATION_BOTTOM },
});
const voice = f
.Voice({ time: { numBeats: 3, beatValue: 4 } })
.setStrict(true)
.addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Bottom Test');
}
function bottomRatio(options) {
const f = VexFlowTests.makeFactory(options, 350, 160);
const stave = f.Stave({ x: 10, y: 10 }).addTimeSignature('5/8');
const notes = [
{ keys: ['f/4'], duration: '4' },
{ keys: ['c/4'], duration: '4' },
{ keys: ['d/4'], duration: '4' },
{ keys: ['d/5'], duration: '8' },
{ keys: ['g/5'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
]
.map(setStemDown)
.map(f.StaveNote.bind(f));
f.Beam({
notes: notes.slice(3, 6),
});
f.Tuplet({
notes: notes.slice(0, 3),
options: {
location: Tuplet.LOCATION_BOTTOM,
ratioed: true,
},
});
f.Tuplet({
notes: notes.slice(3, 6),
options: {
location: Tuplet.LOCATION_BOTTOM,
notesOccupied: 1,
},
});
const voice = f
.Voice({ time: { numBeats: 5, beatValue: 8 } })
.setStrict(true)
.addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Bottom Ratioed Test');
}
function awkward(options) {
const f = VexFlowTests.makeFactory(options, 370, 160);
const stave = f.Stave({ x: 10, y: 10 });
const notes = [
{ keys: ['g/4'], duration: '16' },
{ keys: ['b/4'], duration: '16' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['g/4'], duration: '16' },
{ keys: ['f/4'], duration: '16' },
{ keys: ['e/4'], duration: '16' },
{ keys: ['c/4'], duration: '16' },
{ keys: ['g/4'], duration: '16' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['f/4'], duration: '16' },
{ keys: ['e/4'], duration: '16' },
{ keys: ['c/4'], duration: '8' },
{ keys: ['d/4'], duration: '8' },
{ keys: ['e/4'], duration: '8' },
]
.map(setStemUp)
.map(f.StaveNote.bind(f));
f.Beam({ notes: notes.slice(0, 12) });
f.Tuplet({
notes: notes.slice(0, 12),
options: {
notesOccupied: 142,
ratioed: true,
},
});
f.Tuplet({
notes: notes.slice(12, 15),
options: {
ratioed: true,
},
}).setBracketed(true);
const voice = f.Voice().setStrict(false).addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Awkward Test');
}
function complex(options) {
const f = VexFlowTests.makeFactory(options, 600);
const stave = f.Stave({ x: 10, y: 10 }).addTimeSignature('4/4');
const notes1 = [
{ keys: ['b/4'], duration: '8d' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['g/4'], duration: '8' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['b/4'], duration: '16r' },
{ keys: ['g/4'], duration: '32' },
{ keys: ['f/4'], duration: '32' },
{ keys: ['g/4'], duration: '32' },
{ keys: ['f/4'], duration: '32' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['f/4'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
{ keys: ['a/4'], duration: '8' },
{ keys: ['g/4'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
{ keys: ['a/4'], duration: '8' },
]
.map(setStemUp)
.map(f.StaveNote.bind(f));
Dot.buildAndAttach([notes1[0]], { all: true });
const notes2 = [{ keys: ['c/4'] }, { keys: ['c/4'] }, { keys: ['c/4'] }, { keys: ['c/4'] }]
.map(setDurationToQuarterNote)
.map(setStemDown)
.map(f.StaveNote.bind(f));
f.Beam({ notes: notes1.slice(0, 3) });
f.Beam({ notes: notes1.slice(5, 9) });
f.Beam({ notes: notes1.slice(11, 16) });
f.Tuplet({
notes: notes1.slice(0, 3),
});
f.Tuplet({
notes: notes1.slice(3, 11),
options: {
numNotes: 7,
notesOccupied: 4,
ratioed: false,
},
});
f.Tuplet({
notes: notes1.slice(11, 16),
options: {
notesOccupied: 4,
},
});
const voice1 = f.Voice().setStrict(true).addTickables(notes1);
const voice2 = f.Voice().setStrict(true).addTickables(notes2);
new Formatter().joinVoices([voice1, voice2]).formatToStave([voice1, voice2], stave);
f.draw();
options.assert.ok(true, 'Complex Test');
}
function mixedTop(options) {
const f = VexFlowTests.makeFactory(options);
const stave = f.Stave({ x: 10, y: 10 });
const notes = [
{ keys: ['a/4'], stemDirection: 1 },
{ keys: ['c/6'], stemDirection: -1 },
{ keys: ['a/4'], stemDirection: 1 },
{ keys: ['f/5'], stemDirection: 1 },
{ keys: ['a/4'], stemDirection: -1 },
{ keys: ['c/6'], stemDirection: -1 },
]
.map(setDurationToQuarterNote)
.map(f.StaveNote.bind(f));
f.Tuplet({
notes: notes.slice(0, 2),
options: {
notesOccupied: 3,
},
});
f.Tuplet({
notes: notes.slice(2, 4),
options: {
notesOccupied: 3,
},
});
f.Tuplet({
notes: notes.slice(4, 6),
options: {
notesOccupied: 3,
},
});
const voice = f.Voice().setStrict(false).addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Mixed Stem Direction Tuplet');
}
function mixedBottom(options) {
const f = VexFlowTests.makeFactory(options);
const stave = f.Stave({ x: 10, y: 10 });
const notes = [
{ keys: ['f/3'], stemDirection: 1 },
{ keys: ['a/5'], stemDirection: -1 },
{ keys: ['a/4'], stemDirection: 1 },
{ keys: ['f/3'], stemDirection: 1 },
{ keys: ['a/4'], stemDirection: -1 },
{ keys: ['c/4'], stemDirection: -1 },
]
.map(setDurationToQuarterNote)
.map(f.StaveNote.bind(f));
f.Tuplet({
notes: notes.slice(0, 2),
options: {
notesOccupied: 3,
},
});
f.Tuplet({
notes: notes.slice(2, 4),
options: {
notesOccupied: 3,
},
});
f.Tuplet({
notes: notes.slice(4, 6),
options: {
notesOccupied: 3,
},
});
const voice = f.Voice().setStrict(false).addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Mixed Stem Direction Bottom Tuplet');
}
function nested(options) {
const f = VexFlowTests.makeFactory(options);
const stave = f.Stave({ x: 10, y: 10 }).addTimeSignature('4/4');
const notes = [
{ keys: ['b/4'], duration: '4' },
{ keys: ['a/4'], duration: '4' },
{ keys: ['g/4'], duration: '16' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['f/4'], duration: '16' },
{ keys: ['a/4'], duration: '16' },
{ keys: ['g/4'], duration: '16' },
{ keys: ['b/4'], duration: '2' },
]
.map(setStemUp)
.map(f.StaveNote.bind(f));
f.Beam({
notes: notes.slice(2, 7),
});
f.Tuplet({
notes: notes.slice(0, 7),
options: {
notesOccupied: 2,
numNotes: 3,
},
});
f.Tuplet({
notes: notes.slice(2, 7),
options: {
notesOccupied: 4,
numNotes: 5,
},
});
const voice = f.Voice().setStrict(true).addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Nested Tuplets');
}
function single(options) {
const f = VexFlowTests.makeFactory(options);
const stave = f.Stave({ x: 10, y: 10 }).addTimeSignature('4/4');
const notes = [
{ keys: ['c/4'], duration: '4' },
{ keys: ['d/4'], duration: '8' },
{ keys: ['e/4'], duration: '8' },
{ keys: ['f/4'], duration: '8' },
{ keys: ['g/4'], duration: '8' },
{ keys: ['a/4'], duration: '2' },
{ keys: ['b/4'], duration: '4' },
]
.map(setStemUp)
.map(f.StaveNote.bind(f));
f.Beam({
notes: notes.slice(1, 4),
});
f.Tuplet({
notes: notes.slice(0, -1),
options: {
numNotes: 4,
notesOccupied: 3,
ratioed: true,
bracketed: true,
},
});
f.Tuplet({
notes: notes.slice(0, 1),
options: {
numNotes: 3,
notesOccupied: 2,
ratioed: true,
},
});
f.Tuplet({
notes: notes.slice(1, 4),
options: {
numNotes: 3,
notesOccupied: 2,
},
});
f.Tuplet({
notes: notes.slice(4, 5),
options: {
numNotes: 3,
notesOccupied: 2,
ratioed: true,
bracketed: true,
},
});
const voice = f
.Voice({ time: { numBeats: 4, beatValue: 4 } })
.setStrict(true)
.addTickables(notes);
new Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
options.assert.ok(true, 'Nested Tuplets');
}
VexFlowTests.register(TupletTests);
export { TupletTests };