vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature
1,029 lines (829 loc) • 41.8 kB
JavaScript
/**
* VexFlow - StaveNote Tests
* Copyright Mohit Muthanna 2010 <mohit@muthanna.com>
*/
VF.Test.StaveNote = (function() {
var StaveNote = {
Start: function() {
var runTests = VF.Test.runTests;
QUnit.module('StaveNote');
test('Tick', StaveNote.ticks);
test('Tick - New API', StaveNote.ticksNewApi);
test('Stem', StaveNote.stem);
test('Automatic Stem Direction', StaveNote.autoStem);
test('Displacement after calling setStemDirection', StaveNote.setStemDirectionDisplacement);
test('StaveLine', StaveNote.staveLine);
test('Width', StaveNote.width);
test('TickContext', StaveNote.tickContext);
VF.Test.runUITests('Interactive Mouseover StaveNote', StaveNote.draw, { clef: 'treble', octaveShift: 0, restKey: 'r/4', ui: true });
runTests('StaveNote Draw - Treble', StaveNote.draw, { clef: 'treble', octaveShift: 0, restKey: 'r/4' });
runTests('StaveNote BoundingBoxes - Treble', StaveNote.drawBoundingBoxes, { clef: 'treble', octaveShift: 0, restKey: 'r/4' });
runTests('StaveNote Draw - Alto', StaveNote.draw, { clef: 'alto', octaveShift: -1, restKey: 'r/4' });
runTests('StaveNote Draw - Tenor', StaveNote.draw, { clef: 'tenor', octaveShift: -1, restKey: 'r/3' });
runTests('StaveNote Draw - Bass', StaveNote.draw, { clef: 'bass', octaveShift: -2, restKey: 'r/3' });
runTests('StaveNote Draw - Harmonic And Muted', StaveNote.drawHarmonicAndMuted);
runTests('StaveNote Draw - Slash', StaveNote.drawSlash);
runTests('Displacements', StaveNote.displacements);
runTests('StaveNote Draw - Bass 2', StaveNote.drawBass);
runTests('StaveNote Draw - Key Styles', StaveNote.drawKeyStyles);
runTests('StaveNote Draw - StaveNote Stem Styles', StaveNote.drawNoteStemStyles);
runTests('StaveNote Draw - StaveNote Flag Styles', StaveNote.drawNoteStylesWithFlag);
runTests('StaveNote Draw - StaveNote Styles', StaveNote.drawNoteStyles);
runTests('Stave, Ledger Line, Beam, Stem and Flag Styles', StaveNote.drawBeamStyles);
runTests('Flag and Dot Placement - Stem Up', StaveNote.dotsAndFlagsStemUp);
runTests('Flag and Dots Placement - Stem Down', StaveNote.dotsAndFlagsStemDown);
runTests('Beam and Dot Placement - Stem Up', StaveNote.dotsAndBeamsUp);
runTests('Beam and Dot Placement - Stem Down', StaveNote.dotsAndBeamsDown);
runTests('Center Aligned Note', StaveNote.centerAlignedRest);
runTests('Center Aligned Note with Articulation', StaveNote.centerAlignedRestFermata);
runTests('Center Aligned Note with Annotation', StaveNote.centerAlignedRestAnnotation);
runTests('Center Aligned Note - Multi Voice', StaveNote.centerAlignedMultiVoice);
runTests('Center Aligned Note with Multiple Modifiers', StaveNote.centerAlignedNoteMultiModifiers);
},
ticks: function() {
var BEAT = 1 * VF.RESOLUTION / 4;
var tickTests = {
// Key value pairs of `testName: [durationString, expectedBeats, expectedNoteType]`
'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(function(testName) {
var testData = tickTests[testName];
var durationString = testData[0];
var expectedBeats = testData[1];
var expectedNoteType = testData[2];
var note = new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: durationString });
equal(note.getTicks().value(), BEAT * expectedBeats, testName + ' must have ' + expectedBeats + ' beats');
equal(note.getNoteType(), expectedNoteType, 'Note type must be ' + expectedNoteType);
});
throws(function() {
return new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '8.7dddm' });
}, /BadArguments/, "Invalid note duration '8.7' throws BadArguments exception");
throws(function() {
return new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2Z' });
}, /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
throws(function() {
return new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2dddZ' });
}, /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
},
ticksNewApi: function() {
var BEAT = 1 * VF.RESOLUTION / 4;
// Key value pairs of `testName: [noteData, expectedBeats, expectedNoteType]`
var 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) {
var testData = tickTests[testName];
var noteData = testData[0];
var expectedBeats = testData[1];
var expectedNoteType = testData[2];
noteData.keys = ['c/4', 'e/4', 'g/4'];
var note = new VF.StaveNote(noteData);
equal(note.getTicks().value(), BEAT * expectedBeats, testName + ' must have ' + expectedBeats + ' beats');
equal(note.getNoteType(), expectedNoteType, 'Note type must be ' + expectedNoteType);
});
throws(function() {
return new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '8.7dddm' });
}, /BadArguments/, "Invalid note duration '8.7' throws BadArguments exception");
throws(function() {
return new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2Z' });
}, /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
throws(function() {
return new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '2dddZ' });
}, /BadArguments/, "Invalid note type 'Z' throws BadArguments exception");
},
stem: function() {
var note = new VF.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: 'w' });
equal(note.getStemDirection(), VF.StaveNote.STEM_UP, 'Default note has UP stem');
},
autoStem: function() {
[
// [keys, expectedStemDirection]
[['c/5', 'e/5', 'g/5'], VF.StaveNote.STEM_DOWN],
[['e/4', 'g/4', 'c/5'], VF.StaveNote.STEM_UP],
[['c/5'], VF.StaveNote.STEM_DOWN],
[['a/4', 'e/5', 'g/5'], VF.StaveNote.STEM_DOWN],
[['b/4'], VF.StaveNote.STEM_DOWN],
]
.forEach(function(testData) {
var keys = testData[0];
var expectedStemDirection = testData[1];
var note = new VF.StaveNote({ keys: keys, auto_stem: true, duration: '8' });
equal(note.getStemDirection(), expectedStemDirection, 'Stem must be' + (expectedStemDirection === VF.StaveNote.STEM_UP ? 'up' : 'down'));
});
},
setStemDirectionDisplacement: function() {
function getDisplacements(note) {
return note.note_heads.map(function(notehead) {
return notehead.isDisplaced();
});
}
var stemUpDisplacements = [false, true, false];
var stemDownDisplacements = [true, false, false];
var note = new VF.StaveNote({ keys: ['c/5', 'd/5', 'g/5'], stem_direction: VF.Stem.UP, duration: '4' });
deepEqual(getDisplacements(note), stemUpDisplacements);
note.setStemDirection(VF.Stem.DOWN);
deepEqual(getDisplacements(note), stemDownDisplacements);
note.setStemDirection(VF.Stem.UP);
deepEqual(getDisplacements(note), stemUpDisplacements);
},
staveLine: function() {
var stave = new VF.Stave(10, 10, 300);
var note = new VF.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' });
note.setStave(stave);
var props = note.getKeyProps();
equal(props[0].line, 0, 'C/4 on line 0');
equal(props[1].line, 1, 'E/4 on line 1');
equal(props[2].line, 2.5, 'A/4 on line 2.5');
var ys = note.getYs();
equal(ys.length, 3, 'Chord should be rendered on three lines');
equal(ys[0], 100, 'Line for C/4');
equal(ys[1], 90, 'Line for E/4');
equal(ys[2], 75, 'Line for A/4');
},
width: function() {
var note = new VF.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' });
throws(function() {
note.getWidth();
}, /UnformattedNote/, 'Unformatted note should have no width');
},
tickContext: function() {
var stave = new VF.Stave(10, 10, 400);
var note = new VF.StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' }).setStave(stave);
new VF.TickContext()
.addTickable(note)
.preFormat()
.setX(10)
.setPadding(0);
expect(0);
},
showNote: function(note_struct, stave, ctx, x, drawBoundingBox) {
var note = new VF.StaveNote(note_struct).setStave(stave);
new VF.TickContext()
.addTickable(note)
.preFormat()
.setX(x);
note.setContext(ctx).draw();
if (drawBoundingBox) note.getBoundingBox().draw(ctx);
return note;
},
draw: function(options, contextBuilder) {
var clef = options.params.clef;
var octaveShift = options.params.octaveShift;
var restKey = options.params.restKey;
var ctx = new contextBuilder(options.elementId, 700, 180);
var stave = new VF.Stave(10, 30, 750);
stave.setContext(ctx);
stave.addClef(clef);
stave.draw();
var lowerKeys = ['c/', 'e/', 'a/'];
var higherKeys = ['c/', 'e/', 'a/'];
for (var k = 0; k < lowerKeys.length; k++) {
lowerKeys[k] = lowerKeys[k] + (4 + octaveShift);
higherKeys[k] = higherKeys[k] + (5 + octaveShift);
}
var restKeys = [restKey];
var showNote = VF.Test.StaveNote.showNote;
var notes = [
{ 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', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: 'w', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: 'h', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: 'q', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '8', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '16', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '32', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '64', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '128', stem_direction: -1 },
{ 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' },
];
expect(notes.length * 2);
function colorDescendants(color) {
return function() {
Vex.forEach($(this).find('*'), function(child) {
child.setAttribute('fill', color);
child.setAttribute('stroke', color);
});
};
}
for (var i = 0; i < notes.length; ++i) {
var note = notes[i];
var staveNote = showNote(note, stave, ctx, (i + 1) * 25);
// If this is an interactivity test, then attempt to attach mouseover
// and mouseout handlers to the notes.
if (options.params.ui) {
var item = staveNote.getAttribute('el');
item.addEventListener('mouseover', colorDescendants('green'), false);
item.addEventListener('mouseout', colorDescendants('black'), false);
}
ok(staveNote.getX() > 0, 'Note ' + i + ' has X value');
ok(staveNote.getYs().length > 0, 'Note ' + i + ' has Y values');
}
},
drawBoundingBoxes: function(options, contextBuilder) {
var clef = options.params.clef;
var octaveShift = options.params.octaveShift;
var restKey = options.params.restKey;
var ctx = new contextBuilder(options.elementId, 700, 180);
var stave = new VF.Stave(10, 30, 750);
stave.setContext(ctx);
stave.addClef(clef);
stave.draw();
var lowerKeys = ['c/', 'e/', 'a/'];
var higherKeys = ['c/', 'e/', 'a/'];
for (var k = 0; k < lowerKeys.length; k++) {
lowerKeys[k] = lowerKeys[k] + (4 + octaveShift);
higherKeys[k] = higherKeys[k] + (5 + octaveShift);
}
var restKeys = [restKey];
var showNote = VF.Test.StaveNote.showNote;
var notes = [
{ 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', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: 'w', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: 'h', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: 'q', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '8', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '16', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '32', stem_direction: -1 },
{ clef: clef, keys: lowerKeys, duration: '64', stem_direction: -1 },
{ 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' },
];
expect(notes.length * 2);
for (var i = 0; i < notes.length; ++i) {
var note = notes[i];
var staveNote = showNote(note, stave, ctx, (i + 1) * 25, true);
ok(staveNote.getX() > 0, 'Note ' + i + ' has X value');
ok(staveNote.getYs().length > 0, 'Note ' + i + ' has Y values');
}
},
drawBass: function(options, contextBuilder) {
expect(40);
var ctx = new contextBuilder(options.elementId, 600, 280);
var stave = new VF.Stave(10, 10, 650);
stave.setContext(ctx);
stave.addClef('bass');
stave.draw();
var showNote = VF.Test.StaveNote.showNote;
var notes = [
{ 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', stem_direction: -1 },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: 'q', stem_direction: -1 },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: '8', stem_direction: -1 },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: '16', stem_direction: -1 },
{ clef: 'bass', keys: ['c/2', 'e/2', 'a/2'], duration: '32', stem_direction: -1 },
{ 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 (var i = 0; i < notes.length; ++i) {
var note = notes[i];
var staveNote = showNote(note, stave, ctx, (i + 1) * 25);
ok(staveNote.getX() > 0, 'Note ' + i + ' has X value');
ok(staveNote.getYs().length > 0, 'Note ' + i + ' has Y values');
}
},
displacements: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 700, 140);
ctx.scale(0.9, 0.9);
ctx.fillStyle = '#221';
ctx.strokeStyle = '#221';
var stave = new VF.Stave(10, 10, 650);
stave.setContext(ctx);
stave.draw();
var showNote = VF.Test.StaveNote.showNote;
var notes = [
{ 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', stem_direction: -1 },
{ keys: ['d/4', 'e/4', 'f/4'], duration: 'q', stem_direction: -1 },
{ keys: ['f/4', 'g/4', 'a/4', 'b/4'], duration: '8', stem_direction: -1 },
{ keys: ['c/4', 'd/4', 'e/4', 'f/4', 'g/4', 'a/4'], duration: '16', stem_direction: -1 },
{ keys: ['b/3', 'c/4', 'e/4', 'a/4', 'b/5', 'c/6', 'e/6'], duration: '32', stem_direction: -1 },
{ keys: ['b/3', 'c/4', 'e/4', 'a/4', 'b/5', 'c/6', 'e/6', 'e/6'], duration: '64', stem_direction: -1 },
];
expect(notes.length * 2);
for (var i = 0; i < notes.length; ++i) {
var note = notes[i];
var staveNote = showNote(note, stave, ctx, (i + 1) * 45);
ok(staveNote.getX() > 0, 'Note ' + i + ' has X value');
ok(staveNote.getYs().length > 0, 'Note ' + i + ' has Y values');
}
},
drawHarmonicAndMuted: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 1000, 180);
var stave = new VF.Stave(10, 10, 950);
stave.setContext(ctx);
stave.draw();
var showNote = VF.Test.StaveNote.showNote;
var notes = [
{ 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', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'wh', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'hh', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'qh', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '8h', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '16h', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '32h', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '64h', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '128h', stem_direction: -1 },
{ 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', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'wm', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'hm', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: 'qm', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '8m', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '16m', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '32m', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '64m', stem_direction: -1 },
{ keys: ['c/4', 'e/4', 'a/4'], duration: '128m', stem_direction: -1 },
];
expect(notes.length * 2);
for (var i = 0; i < notes.length; ++i) {
var note = notes[i];
var staveNote = showNote(note, stave, ctx, (i * 25) + 5);
ok(staveNote.getX() > 0, 'Note ' + i + ' has X value');
ok(staveNote.getYs().length > 0, 'Note ' + i + ' has Y values');
}
},
drawSlash: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 700, 180);
var stave = new VF.Stave(10, 10, 650);
stave.setContext(ctx);
stave.draw();
var notes = [
{ keys: ['b/4'], duration: '1/2s', stem_direction: -1 },
{ keys: ['b/4'], duration: 'ws', stem_direction: -1 },
{ keys: ['b/4'], duration: 'hs', stem_direction: -1 },
{ keys: ['b/4'], duration: 'qs', stem_direction: -1 },
{ keys: ['b/4'], duration: '8s', stem_direction: -1 },
{ keys: ['b/4'], duration: '16s', stem_direction: -1 },
{ keys: ['b/4'], duration: '32s', stem_direction: -1 },
{ keys: ['b/4'], duration: '64s', stem_direction: -1 },
{ keys: ['b/4'], duration: '128s', stem_direction: -1 },
{ keys: ['b/4'], duration: '1/2s', stem_direction: 1 },
{ keys: ['b/4'], duration: 'ws', stem_direction: 1 },
{ keys: ['b/4'], duration: 'hs', stem_direction: 1 },
{ keys: ['b/4'], duration: 'qs', stem_direction: 1 },
{ keys: ['b/4'], duration: '8s', stem_direction: 1 },
{ keys: ['b/4'], duration: '16s', stem_direction: 1 },
{ keys: ['b/4'], duration: '32s', stem_direction: 1 },
{ keys: ['b/4'], duration: '64s', stem_direction: 1 },
{ keys: ['b/4'], duration: '128s', stem_direction: 1 },
// Beam
{ keys: ['b/4'], duration: '8s', stem_direction: -1 },
{ keys: ['b/4'], duration: '8s', stem_direction: -1 },
{ keys: ['b/4'], duration: '8s', stem_direction: 1 },
{ keys: ['b/4'], duration: '8s', stem_direction: 1 },
];
var stave_notes = notes.map(function(note) { return new VF.StaveNote(note); });
var beam1 = new VF.Beam([stave_notes[16], stave_notes[17]]);
var beam2 = new VF.Beam([stave_notes[18], stave_notes[19]]);
VF.Formatter.FormatAndDraw(ctx, stave, stave_notes, false);
beam1.setContext(ctx).draw();
beam2.setContext(ctx).draw();
ok('Slash Note Heads');
},
drawKeyStyles: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 300, 280);
ctx.scale(3, 3);
var stave = new VF.Stave(10, 0, 100);
var note = new VF.StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: 'q' })
.setStave(stave)
.addAccidental(1, new VF.Accidental('b'))
.setKeyStyle(1, { shadowBlur: 15, shadowColor: 'blue', fillStyle: 'blue' });
new VF.TickContext()
.addTickable(note)
.preFormat()
.setX(25);
stave.setContext(ctx).draw();
note.setContext(ctx).draw();
ok(note.getX() > 0, 'Note has X value');
ok(note.getYs().length > 0, 'Note has Y values');
},
drawNoteStyles: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 300, 280);
var stave = new VF.Stave(10, 0, 100);
ctx.scale(3, 3);
var note = new VF.StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: '8' })
.setStave(stave)
.addAccidental(1, new VF.Accidental('b'));
note.setStyle({ shadowBlur: 15, shadowColor: 'blue', fillStyle: 'blue', strokeStyle: 'blue' });
new VF.TickContext()
.addTickable(note)
.preFormat()
.setX(25);
stave.setContext(ctx).draw();
note.setContext(ctx).draw();
ok(note.getX() > 0, 'Note has X value');
ok(note.getYs().length > 0, 'Note has Y values');
},
drawNoteStemStyles: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 300, 280);
var stave = new VF.Stave(10, 0, 100);
ctx.scale(3, 3);
var note = new VF.StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: 'q' })
.setStave(stave)
.addAccidental(1, new VF.Accidental('b'));
note.setStemStyle({ shadowBlur: 15, shadowColor: 'blue', fillStyle: 'blue', strokeStyle: 'blue' });
new VF.TickContext()
.addTickable(note)
.preFormat()
.setX(25);
stave.setContext(ctx).draw();
note.setContext(ctx).draw();
ok('Note Stem Style');
},
drawNoteStylesWithFlag: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 300, 280);
var stave = new VF.Stave(10, 0, 100);
ctx.scale(3, 3);
var note = new VF.StaveNote({ keys: ['g/4', 'bb/4', 'd/5'], duration: '8' })
.setStave(stave)
.addAccidental(1, new VF.Accidental('b'));
note.setFlagStyle({ shadowBlur: 15, shadowColor: 'blue', fillStyle: 'blue', strokeStyle: 'blue' });
new VF.TickContext()
.addTickable(note)
.preFormat()
.setX(25);
stave.setContext(ctx).draw();
note.setContext(ctx).draw();
ok(note.getX() > 0, 'Note has X value');
ok(note.getYs().length > 0, 'Note has Y values');
},
drawBeamStyles: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 400, 160);
var stave = new VF.Stave(10, 10, 380);
stave.setStyle({
strokeStyle: '#EEAAEE',
lineWidth: '3',
});
stave.setContext(ctx);
stave.draw();
var notes = [
// beam1
{ keys: ['b/4'], duration: '8', stem_direction: -1 },
{ keys: ['b/4'], duration: '8', stem_direction: -1 },
// should be unstyled...
{ keys: ['b/4'], duration: '8', stem_direction: -1 },
// beam2 should also be unstyled
{ keys: ['b/4'], duration: '8', stem_direction: -1 },
{ keys: ['b/4'], duration: '8', stem_direction: -1 },
// beam3
{ keys: ['b/4'], duration: '8', stem_direction: 1 },
{ keys: ['b/4'], duration: '8', stem_direction: 1 },
// beam4
{ keys: ['d/6'], duration: '8', stem_direction: -1 },
{ keys: ['c/6', 'd/6'], duration: '8', stem_direction: -1 },
// unbeamed
{ keys: ['d/6', 'e/6'], duration: '8', stem_direction: -1 },
// unbeamed, unstyled
{ keys: ['e/6', 'f/6'], duration: '8', stem_direction: -1 },
];
var staveNotes = notes.map(function(note) { return new VF.StaveNote(note); });
var beam1 = new VF.Beam(staveNotes.slice(0, 2));
var beam2 = new VF.Beam(staveNotes.slice(3, 5));
var beam3 = new VF.Beam(staveNotes.slice(5, 7));
var beam4 = new VF.Beam(staveNotes.slice(7, 9));
// stem, key, ledger, flag; beam.setStyle
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: 20,
shadowColor: 'blue',
});
staveNotes[9].setLedgerLineStyle({ fillStyle: 'lawngreen', strokeStyle: 'lawngreen', lineWidth: 1 });
staveNotes[9].setFlagStyle({ fillStyle: 'orange', strokeStyle: 'orange' });
VF.Formatter.FormatAndDraw(ctx, stave, staveNotes, false);
beam1.setContext(ctx).draw();
beam2.setContext(ctx).draw();
beam3.setContext(ctx).draw();
beam4.setContext(ctx).draw();
ok('draw beam styles');
},
renderNote: function(note, stave, ctx, x) {
note.setStave(stave);
var mc = new VF.ModifierContext();
note.addToModifierContext(mc);
new VF.TickContext()
.addTickable(note)
.preFormat()
.setX(x);
note.setContext(ctx).draw();
ctx.save();
return note;
},
dotsAndFlagsStemUp: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 800, 150);
ctx.scale(1.0, 1.0);
ctx.setFillStyle('#221');
ctx.setStrokeStyle('#221');
var stave = new VF.Stave(10, 10, 975);
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
var notes = [
newNote({ keys: ['f/4'], duration: '4', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '8', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '16', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '32', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '64', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '128', stem_direction: 1 }).addDotToAll().addDotToAll(),
newNote({ keys: ['g/4'], duration: '4', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '8', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '16', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '32' }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '64', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '128', stem_direction: 1 }).addDotToAll().addDotToAll(),
];
stave.setContext(ctx).draw();
for (var i = 0; i < notes.length; ++i) {
VF.Test.StaveNote.renderNote(notes[i], stave, ctx, (i * 65));
}
ok(true, 'Full Dot');
},
dotsAndFlagsStemDown: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 800, 160);
ctx.scale(1.0, 1.0);
ctx.setFillStyle('#221');
ctx.setStrokeStyle('#221');
var stave = new VF.Stave(10, 10, 975);
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
var notes = [
newNote({ keys: ['e/5'], duration: '4', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '8', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '16', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '32', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '64', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '128', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '4', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '8', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '16', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '32', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '64', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '128', stem_direction: -1 }).addDotToAll(),
];
stave.setContext(ctx).draw();
for (var i = 0; i < notes.length; ++i) {
VF.Test.StaveNote.renderNote(notes[i], stave, ctx, (i * 65));
}
ok(true, 'Full Dot');
},
dotsAndBeamsUp: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 800, 150);
ctx.scale(1.0, 1.0);
ctx.setFillStyle('#221');
ctx.setStrokeStyle('#221');
var stave = new VF.Stave(10, 10, 975);
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
var notes = [
newNote({ keys: ['f/4'], duration: '8', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '16', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '32', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '64', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['f/4'], duration: '128', stem_direction: 1 }).addDotToAll().addDotToAll(),
newNote({ keys: ['g/4'], duration: '8', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '16', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '32' }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '64', stem_direction: 1 }).addDotToAll(),
newNote({ keys: ['g/4'], duration: '128', stem_direction: 1 }).addDotToAll().addDotToAll(),
];
var beam = new VF.Beam(notes);
stave.setContext(ctx).draw();
for (var i = 0; i < notes.length; ++i) {
VF.Test.StaveNote.renderNote(notes[i], stave, ctx, (i * 65));
}
beam.setContext(ctx).draw();
ok(true, 'Full Dot');
},
dotsAndBeamsDown: function(options, contextBuilder) {
var ctx = new contextBuilder(options.elementId, 800, 160);
ctx.scale(1.0, 1.0);
ctx.setFillStyle('#221');
ctx.setStrokeStyle('#221');
var stave = new VF.Stave(10, 10, 975);
function newNote(note_struct) { return new VF.StaveNote(note_struct); }
var notes = [
newNote({ keys: ['e/5'], duration: '8', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '16', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '32', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '64', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['e/5'], duration: '128', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '8', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '16', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '32', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '64', stem_direction: -1 }).addDotToAll(),
newNote({ keys: ['d/5'], duration: '128', stem_direction: -1 }).addDotToAll(),
];
var beam = new VF.Beam(notes);
stave.setContext(ctx).draw();
for (var i = 0; i < notes.length; ++i) {
VF.Test.StaveNote.renderNote(notes[i], stave, ctx, (i * 65));
}
beam.setContext(ctx).draw();
ok(true, 'Full Dot');
},
centerAlignedRest: function(options) {
var vf = VF.Test.makeFactory(options, 400, 160);
var stave = vf.Stave({ x: 10, y: 10, width: 350 })
.addClef('treble')
.addTimeSignature('4/4');
var note = vf.StaveNote({ keys: ['b/4'], duration: '1r', align_center: true });
var voice = vf.Voice()
.setStrict(false)
.addTickables([note]);
vf.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true);
},
centerAlignedRestFermata: function(options) {
var vf = VF.Test.makeFactory(options, 400, 160);
var stave = vf.Stave({ x: 10, y: 10, width: 350 })
.addClef('treble')
.addTimeSignature('4/4');
var note = vf.StaveNote({ keys: ['b/4'], duration: '1r', align_center: true })
.addArticulation(0, new VF.Articulation('a@a').setPosition(3));
var voice = vf.Voice()
.setStrict(false)
.addTickables([note]);
vf.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true);
},
centerAlignedRestAnnotation: function(options) {
var vf = VF.Test.makeFactory(options, 400, 160);
var stave = vf.Stave({ x: 10, y: 10, width: 350 })
.addClef('treble')
.addTimeSignature('4/4');
var note = vf.StaveNote({ keys: ['b/4'], duration: '1r', align_center: true })
.addAnnotation(0, new VF.Annotation('Whole measure rest').setPosition(3));
var voice = vf.Voice()
.setStrict(false)
.addTickables([note]);
vf.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true);
},
centerAlignedNoteMultiModifiers: function(options) {
var vf = VF.Test.makeFactory(options, 400, 160);
var stave = vf.Stave({ x: 10, y: 10, width: 350 })
.addClef('treble')
.addTimeSignature('4/4');
function newFinger(num, pos) { return new VF.FretHandFinger(num).setPosition(pos); }
function newStringNumber(num, pos) { return new VF.StringNumber(num).setPosition(pos); }
var note = vf.StaveNote({ keys: ['c/4', 'e/4', 'g/4'], duration: '4', align_center: true })
.addAnnotation(0, new VF.Annotation('Test').setPosition(3))
.addStroke(0, new VF.Stroke(2))
.addAccidental(1, new VF.Accidental('#'))
.addModifier(0, newFinger('3', VF.Modifier.Position.LEFT))
.addModifier(2, newFinger('2', VF.Modifier.Position.LEFT))
.addModifier(1, newFinger('1', VF.Modifier.Position.RIGHT))
.addModifier(2, newStringNumber('4', VF.Modifier.Position.BELOW))
.addDotToAll();
var voice = vf.Voice()
.setStrict(false)
.addTickables([note]);
vf.Formatter()
.joinVoices([voice])
.formatToStave([voice], stave);
vf.draw();
ok(true);
},
centerAlignedMultiVoice: function(options) {
var vf = VF.Test.makeFactory(options, 400, 160);
var stave = vf.Stave({ x: 10, y: 10, width: 350 })
.addClef('treble')
.addTimeSignature('3/8');
// Create custom duration
var custom_duration = new VF.Fraction(3, 8);
var notes0 = [
{ keys: ['c/4'], duration: '1r', align_center: true, duration_override: custom_duration },
].map(vf.StaveNote.bind(vf));
var notes1 = [
{ keys: ['b/4'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
{ keys: ['b/4'], duration: '8' },
].map(vf.StaveNote.bind(vf));
notes1[1].addAccidental(0, vf.Accidental({ type: '#' }));
vf.Beam({ notes: notes1 });
var voice0 = vf.Voice({ time: '3/8' })
.setStrict(false)
.addTickables(notes0);
var voice1 = vf.Voice({ time: '3/8' })
.setStrict(false)
.addTickables(notes1);
vf.Formatter()
.joinVoices([voice0, voice1])
.formatToStave([voice0, voice1], stave);
vf.draw();
ok(true);
},
};
return StaveNote;
})();