vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
150 lines (149 loc) • 7.69 kB
JavaScript
import { VexFlowTests } from './vexflow_test_helpers.js';
import { BarNote } from '../src/barnote.js';
import { Note } from '../src/note.js';
import { BarlineType } from '../src/stavebarline.js';
const NoteSubGroupTests = {
Start() {
QUnit.module('NoteSubGroup');
const run = VexFlowTests.runTests;
run('Basic - ClefNote, TimeSigNote and BarNote', basic);
run('Multi Voice', multiVoiceSingleDraw);
run('Multi Voice Multiple Draws', multiVoiceDoubleDraw);
run('Multi Staff', multiStaff);
},
};
function createShortcuts(f) {
return {
createStaveNote: (noteStruct) => f.StaveNote(noteStruct),
addAccidental: (note, accid) => note.addModifier(f.Accidental({ type: accid }), 0),
addSubGroup: (note, subNotes) => note.addModifier(f.NoteSubGroup({ notes: subNotes }), 0),
};
}
function basic(options) {
const f = VexFlowTests.makeFactory(options, 750, 200);
const ctx = f.getContext();
const stave = f.Stave({ width: 600 }).addClef('treble');
const { createStaveNote, addAccidental, addSubGroup } = createShortcuts(f);
const notes = [
{ keys: ['f/5'], stemDirection: -1, duration: '4' },
{ keys: ['d/4'], stemDirection: -1, duration: '4', clef: 'bass' },
{ keys: ['g/4'], stemDirection: -1, duration: '4', clef: 'alto' },
{ keys: ['a/4'], stemDirection: -1, duration: '4', clef: 'alto' },
{ keys: ['c/4'], stemDirection: -1, duration: '4', clef: 'tenor' },
{ keys: ['c/3'], stemDirection: +1, duration: '4', clef: 'tenor' },
{ keys: ['d/4'], stemDirection: -1, duration: '4', clef: 'tenor' },
{ keys: ['f/4'], stemDirection: -1, duration: '4', clef: 'tenor' },
].map(createStaveNote);
addAccidental(notes[1], '#');
addAccidental(notes[2], 'n');
addSubGroup(notes[1], [f.ClefNote({ type: 'bass', options: { size: 'small' } })]);
addSubGroup(notes[2], [f.ClefNote({ type: 'alto', options: { size: 'small' } })]);
addSubGroup(notes[4], [f.ClefNote({ type: 'tenor', options: { size: 'small' } }), new BarNote()]);
addSubGroup(notes[5], [f.TimeSigNote({ time: '6/8' })]);
addSubGroup(notes[6], [new BarNote(BarlineType.REPEAT_BEGIN)]);
addAccidental(notes[4], 'b');
addAccidental(notes[6], 'bb');
const voice = f.Voice().setStrict(false).addTickables(notes);
f.Formatter().joinVoices([voice]).formatToStave([voice], stave);
f.draw();
notes.forEach((note) => Note.plotMetrics(ctx, note, 150));
VexFlowTests.plotLegendForNoteWidth(ctx, 620, 120);
options.assert.ok(true, 'all pass');
}
function multiVoiceSingleDraw(options) {
multiVoiceHelper(options, 1);
}
function multiVoiceDoubleDraw(options) {
multiVoiceHelper(options, 2);
}
function multiVoiceHelper(options, numDraws) {
const f = VexFlowTests.makeFactory(options, 550, 200);
const ctx = f.getContext();
const stave = f.Stave().addClef('treble');
const { createStaveNote, addAccidental, addSubGroup } = createShortcuts(f);
const notes1 = [
{ keys: ['f/5'], stemDirection: 1, duration: '4' },
{ keys: ['d/4'], stemDirection: 1, duration: '4', clef: 'bass' },
{ keys: ['c/5'], stemDirection: 1, duration: '4', clef: 'alto' },
{ keys: ['c/5'], stemDirection: 1, duration: '4', clef: 'soprano' },
].map(createStaveNote);
const notes2 = [
{ keys: ['c/4'], stemDirection: -1, duration: '4' },
{ keys: ['c/3'], stemDirection: -1, duration: '4', clef: 'bass' },
{ keys: ['d/4'], stemDirection: -1, duration: '4', clef: 'alto' },
{ keys: ['f/4'], stemDirection: -1, duration: '4', clef: 'soprano' },
].map(createStaveNote);
addAccidental(notes1[1], '#');
addSubGroup(notes1[1], [
f.ClefNote({ type: 'bass', options: { size: 'small' } }),
new BarNote(BarlineType.REPEAT_BEGIN),
f.TimeSigNote({ time: '3/4' }),
]);
addSubGroup(notes2[2], [
f.ClefNote({ type: 'alto', options: { size: 'small' } }),
f.TimeSigNote({ time: '9/8' }),
new BarNote(BarlineType.DOUBLE),
]);
addSubGroup(notes1[3], [f.ClefNote({ type: 'soprano', options: { size: 'small' } })]);
addAccidental(notes1[2], 'b');
addAccidental(notes2[3], '#');
const voices = [f.Voice().addTickables(notes1), f.Voice().addTickables(notes2)];
f.Formatter().joinVoices(voices).formatToStave(voices, stave);
for (let i = 0; i < numDraws; i++) {
f.draw();
}
notes1.forEach((note) => Note.plotMetrics(ctx, note, 150));
options.assert.ok(true, 'all pass');
}
function multiStaff(options) {
const f = VexFlowTests.makeFactory(options, 550, 400);
const { createStaveNote, addAccidental, addSubGroup } = createShortcuts(f);
const stave1 = f.Stave({ x: 15, y: 30, width: 500 }).setClef('treble');
const notes1 = [
{ keys: ['f/5'], stemDirection: 1, duration: '4' },
{ keys: ['d/4'], stemDirection: 1, duration: '4', clef: 'bass' },
{ keys: ['c/5'], stemDirection: 1, duration: '4', clef: 'alto' },
{ keys: ['c/5'], stemDirection: 1, duration: '4', clef: 'soprano' },
].map(createStaveNote);
const notes2 = [
{ keys: ['c/4'], stemDirection: -1, duration: '4' },
{ keys: ['c/3'], stemDirection: -1, duration: '4', clef: 'bass' },
{ keys: ['d/4'], stemDirection: -1, duration: '4', clef: 'alto' },
{ keys: ['f/4'], stemDirection: -1, duration: '4', clef: 'soprano' },
].map(createStaveNote);
const stave2 = f.Stave({ x: 15, y: 150, width: 500 }).setClef('bass');
const notes3 = [
{ keys: ['e/3'], duration: '8', stemDirection: -1, clef: 'bass' },
{ keys: ['g/4'], duration: '8', stemDirection: 1, clef: 'treble' },
{ keys: ['d/4'], duration: '8', stemDirection: 1, clef: 'treble' },
{ keys: ['f/4'], duration: '8', stemDirection: 1, clef: 'treble' },
{ keys: ['c/4'], duration: '8', stemDirection: 1, clef: 'treble' },
{ keys: ['g/3'], duration: '8', stemDirection: -1, clef: 'bass' },
{ keys: ['d/3'], duration: '8', stemDirection: -1, clef: 'bass' },
{ keys: ['f/3'], duration: '8', stemDirection: -1, clef: 'bass' },
].map(createStaveNote);
f.StaveConnector({ topStave: stave1, bottomStave: stave2, type: 'brace' });
f.StaveConnector({ topStave: stave1, bottomStave: stave2, type: 'singleLeft' });
f.StaveConnector({ topStave: stave1, bottomStave: stave2, type: 'singleRight' });
f.Beam({ notes: notes3.slice(1, 4) });
f.Beam({ notes: notes3.slice(5) });
addAccidental(notes1[1], '#');
addSubGroup(notes1[1], [f.ClefNote({ type: 'bass', options: { size: 'small' } }), f.TimeSigNote({ time: '3/4' })]);
addSubGroup(notes2[2], [f.ClefNote({ type: 'alto', options: { size: 'small' } }), f.TimeSigNote({ time: '9/8' })]);
addSubGroup(notes1[3], [f.ClefNote({ type: 'soprano', options: { size: 'small' } })]);
addSubGroup(notes3[1], [f.ClefNote({ type: 'treble', options: { size: 'small' } })]);
addSubGroup(notes3[5], [f.ClefNote({ type: 'bass', options: { size: 'small' } })]);
addAccidental(notes3[0], '#');
addAccidental(notes3[3], 'b');
addAccidental(notes3[5], '#');
addAccidental(notes1[2], 'b');
addAccidental(notes2[3], '#');
const voice1 = f.Voice().addTickables(notes1);
const voice2 = f.Voice().addTickables(notes2);
const voice3 = f.Voice().addTickables(notes3);
f.Formatter().joinVoices([voice1, voice2]).joinVoices([voice3]).formatToStave([voice1, voice2, voice3], stave1);
f.draw();
options.assert.ok(true, 'all pass');
}
VexFlowTests.register(NoteSubGroupTests);
export { NoteSubGroupTests };