UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

975 lines (974 loc) 44.8 kB
import { VexFlowTests } from './vexflow_test_helpers.js'; import { Accidental } from '../src/accidental.js'; import { Beam } from '../src/beam.js'; import { Dot } from '../src/dot.js'; import { Flow } from '../src/flow.js'; import { Formatter } from '../src/formatter.js'; import { ModifierContext } from '../src/modifiercontext.js'; import { Note } from '../src/note.js'; import { Stave } from '../src/stave.js'; import { StaveNote } from '../src/stavenote.js'; import { Stem } from '../src/stem.js'; import { TickContext } from '../src/tickcontext.js'; import { TimeSigNote } from '../src/timesignote.js'; import { isAccidental } from '../src/typeguard.js'; import { Voice } from '../src/voice.js'; const AccidentalTests = { Start() { QUnit.module('Accidental'); QUnit.test('Automatic Accidentals - Simple Tests', autoAccidentalWorking); const run = VexFlowTests.runTests; run('Accidental Padding', formatAccidentalSpaces); run('Basic', basic); run('Stem Down', basicStemDown); run('Cautionary Accidental', cautionary); run('Accidental Arrangement Special Cases', specialCases); run('Multi Voice', multiVoice); run('Microtonal', microtonal); run('Microtonal (Iranian)', microtonal_iranian); run('Sagittal', sagittal); run('Automatic Accidentals', automaticAccidentals0); run('Automatic Accidentals - C major scale in Ab', automaticAccidentals1); run('Automatic Accidentals - No Accidentals Necessary', automaticAccidentals2); run('Automatic Accidentals - No Accidentals Necessary (EasyScore)', automaticAccidentals3); run('Automatic Accidentals - Multi Voice Inline', automaticAccidentalsMultiVoiceInline); run('Automatic Accidentals - Multi Voice Offset', automaticAccidentalsMultiVoiceOffset); run('Automatic Accidentals - Key C, Single Octave', automaticAccidentalsCornerCases1); run('Automatic Accidentals - Key C, Two Octaves', automaticAccidentalsCornerCases2); run('Automatic Accidentals - Key C#, Single Octave', automaticAccidentalsCornerCases3); run('Automatic Accidentals - Key C#, Two Octaves', automaticAccidentalsCornerCases4); run('Factory API', factoryAPI); }, }; function hasAccidental(note) { return note.getModifiers().some((modifier) => isAccidental(modifier)); } function makeNewAccid(factory) { return (type) => factory.Accidental({ type }); } function autoAccidentalWorking(assert) { const createStaveNote = (noteStruct) => new StaveNote(noteStruct); let notes = [ { keys: ['bb/4'], duration: '4' }, { keys: ['bb/4'], duration: '4' }, { keys: ['g#/4'], duration: '4' }, { keys: ['g/4'], duration: '4' }, { keys: ['b/4'], duration: '4' }, { keys: ['b/4'], duration: '4' }, { keys: ['a#/4'], duration: '4' }, { keys: ['g#/4'], duration: '4' }, ].map(createStaveNote); let voice = new Voice().setMode(Voice.Mode.SOFT).addTickables(notes); Accidental.applyAccidentals([voice], 'F'); assert.equal(hasAccidental(notes[0]), false, 'No flat because of key signature'); assert.equal(hasAccidental(notes[1]), false, 'No flat because of key signature'); assert.equal(hasAccidental(notes[2]), true, 'Added a sharp'); assert.equal(hasAccidental(notes[3]), true, 'Back to natural'); assert.equal(hasAccidental(notes[4]), true, 'Back to natural'); assert.equal(hasAccidental(notes[5]), false, 'Natural remembered'); assert.equal(hasAccidental(notes[6]), true, 'Added sharp'); assert.equal(hasAccidental(notes[7]), true, 'Added sharp'); notes = [ { keys: ['e#/4'], duration: '4' }, { keys: ['cb/4'], duration: '4' }, { keys: ['fb/4'], duration: '4' }, { keys: ['b#/4'], duration: '4' }, { keys: ['b#/4'], duration: '4' }, { keys: ['cb/5'], duration: '4' }, { keys: ['fb/5'], duration: '4' }, { keys: ['e#/4'], duration: '4' }, ].map(createStaveNote); voice = new Voice().setMode(Voice.Mode.SOFT).addTickables(notes); Accidental.applyAccidentals([voice], 'A'); assert.equal(hasAccidental(notes[0]), true, 'Added sharp'); assert.equal(hasAccidental(notes[1]), true, 'Added flat'); assert.equal(hasAccidental(notes[2]), true, 'Added flat'); assert.equal(hasAccidental(notes[3]), true, 'Added sharp'); assert.equal(hasAccidental(notes[4]), false, 'Sharp remembered'); assert.equal(hasAccidental(notes[5]), true, 'Added flat(different octave)'); assert.equal(hasAccidental(notes[6]), true, 'Added flat(different octave)'); assert.equal(hasAccidental(notes[7]), false, 'sharp remembered'); notes = [ { keys: ['c/4'], duration: '4' }, { keys: ['cb/4'], duration: '4' }, { keys: ['cb/4'], duration: '4' }, { keys: ['c#/4'], duration: '4' }, { keys: ['c#/4'], duration: '4' }, { keys: ['cbb/4'], duration: '4' }, { keys: ['cbb/4'], duration: '4' }, { keys: ['c##/4'], duration: '4' }, { keys: ['c##/4'], duration: '4' }, { keys: ['c/4'], duration: '4' }, { keys: ['c/4'], duration: '4' }, ].map(createStaveNote); voice = new Voice().setMode(Voice.Mode.SOFT).addTickables(notes); Accidental.applyAccidentals([voice], 'C'); assert.equal(hasAccidental(notes[0]), false, 'No accidental'); assert.equal(hasAccidental(notes[1]), true, 'Added flat'); assert.equal(hasAccidental(notes[2]), false, 'Flat remembered'); assert.equal(hasAccidental(notes[3]), true, 'Sharp added'); assert.equal(hasAccidental(notes[4]), false, 'Sharp remembered'); assert.equal(hasAccidental(notes[5]), true, 'Added doubled flat'); assert.equal(hasAccidental(notes[6]), false, 'Double flat remembered'); assert.equal(hasAccidental(notes[7]), true, 'Added double sharp'); assert.equal(hasAccidental(notes[8]), false, 'Double sharp rememberd'); assert.equal(hasAccidental(notes[9]), true, 'Added natural'); assert.equal(hasAccidental(notes[10]), false, 'Natural remembered'); } function formatAccidentalSpaces(options) { const f = VexFlowTests.makeFactory(options, 750, 280); const context = f.getContext(); const softmaxFactor = 100; const notes = [ new StaveNote({ keys: ['e##/5'], duration: '8d', }).addModifier(new Accidental('##'), 0), new StaveNote({ keys: ['b/4'], duration: '16', }).addModifier(new Accidental('b'), 0), new StaveNote({ keys: ['f/3'], duration: '8', }), new StaveNote({ keys: ['a/3'], duration: '16', }), new StaveNote({ keys: ['e/4', 'g/4'], duration: '16', }) .addModifier(new Accidental('bb'), 0) .addModifier(new Accidental('bb'), 1), new StaveNote({ keys: ['d/4'], duration: '16', }), new StaveNote({ keys: ['e/4', 'g/4'], duration: '16', }) .addModifier(new Accidental('#'), 0) .addModifier(new Accidental('#'), 1), new StaveNote({ keys: ['g/4'], duration: '32', }), new StaveNote({ keys: ['a/4'], duration: '32', }), new StaveNote({ keys: ['g/4'], duration: '16', }), new StaveNote({ keys: ['d/4'], duration: 'q', }), ]; Dot.buildAndAttach([notes[0]], { all: true }); const beams = Beam.generateBeams(notes); const voice = new Voice({ num_beats: 4, beat_value: 4, }); voice.addTickables(notes); const formatter = new Formatter({ softmaxFactor }).joinVoices([voice]); const width = formatter.preCalculateMinTotalWidth([voice]); const stave = new Stave(10, 40, width + 20); stave.setContext(context).draw(); formatter.format([voice], width); voice.draw(context, stave); beams.forEach((b) => b.setContext(context).draw()); notes.forEach((note) => Note.plotMetrics(context, note, 30)); VexFlowTests.plotLegendForNoteWidth(context, 300, 150); options.assert.ok(true); } function basic(options) { const f = VexFlowTests.makeFactory(options, 700, 240); const accid = makeNewAccid(f); f.Stave({ x: 10, y: 10, width: 550 }); const notes = [ f .StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '1' }) .addModifier(accid('b'), 0) .addModifier(accid('#'), 1), f .StaveNote({ keys: ['e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5', 'd/4'], duration: '2' }) .addModifier(accid('##'), 6) .addModifier(accid('n'), 0) .addModifier(accid('bb'), 1) .addModifier(accid('b'), 2) .addModifier(accid('#'), 3) .addModifier(accid('n'), 4) .addModifier(accid('bb'), 5), f .StaveNote({ keys: ['g/5', 'f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5'], duration: '16' }) .addModifier(accid('n'), 1) .addModifier(accid('#'), 2) .addModifier(accid('#'), 3) .addModifier(accid('b'), 4) .addModifier(accid('bb'), 5) .addModifier(accid('##'), 6) .addModifier(accid('#'), 0), f .StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: '1' }) .addModifier(accid('#'), 0) .addModifier(accid('##').setAsCautionary(), 1) .addModifier(accid('#').setAsCautionary(), 2) .addModifier(accid('b'), 3) .addModifier(accid('bb').setAsCautionary(), 4) .addModifier(accid('b').setAsCautionary(), 5), ]; Formatter.SimpleFormat(notes, 10, { paddingBetween: 45 }); notes.forEach((note, index) => { Note.plotMetrics(f.getContext(), note, 140); options.assert.ok(note.getModifiersByType('Accidental').length > 0, 'Note ' + index + ' has accidentals'); note.getModifiersByType('Accidental').forEach((accid, index) => { options.assert.ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width'); }); }); f.draw(); VexFlowTests.plotLegendForNoteWidth(f.getContext(), 480, 140); options.assert.ok(true, 'Full Accidental'); } function cautionary(options) { const staveCount = 12; const scale = 0.85; const staveWidth = 840; let i = 0; let j = 0; const f = VexFlowTests.makeFactory(options, staveWidth + 10, 175 * staveCount + 10); f.getContext().scale(scale, scale); const accids = Object.keys(Flow.accidentalMap).filter((accid) => accid !== '{' && accid !== '}'); const mod = Math.round(accids.length / staveCount); for (i = 0; i < staveCount; ++i) { const stave = f.Stave({ x: 0, y: 10 + 200 * i, width: staveWidth / scale }); const score = f.EasyScore(); const rowMap = []; for (j = 0; j < mod && j + i * staveCount < accids.length; ++j) { rowMap.push(accids[j + i * staveCount]); } const notes = rowMap.map((accidType) => f .StaveNote({ keys: ['a/4'], duration: '4', stem_direction: Stem.UP }) .addModifier(f.Accidental({ type: accidType }), 0)); const voice = score.voice(notes, { time: rowMap.length + '/4' }); voice.getTickables().forEach((tickable) => { tickable .getModifiers() .filter((modifier) => modifier.getAttribute('type') === Accidental.CATEGORY) .forEach((accid) => accid.setAsCautionary()); }); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); } options.assert.ok(true, 'Must successfully render cautionary accidentals'); } function specialCases(options) { const f = VexFlowTests.makeFactory(options, 700, 240); const accid = makeNewAccid(f); f.Stave({ x: 10, y: 10, width: 550 }); const notes = [ f .StaveNote({ keys: ['f/4', 'd/5'], duration: '1' }) .addModifier(accid('#'), 0) .addModifier(accid('b'), 1), f .StaveNote({ keys: ['c/4', 'g/4'], duration: '2' }) .addModifier(accid('##'), 0) .addModifier(accid('##'), 1), f .StaveNote({ keys: ['b/3', 'd/4', 'f/4'], duration: '16' }) .addModifier(accid('#'), 0) .addModifier(accid('#'), 1) .addModifier(accid('##'), 2), f .StaveNote({ keys: ['g/4', 'a/4', 'c/5', 'e/5'], duration: '16' }) .addModifier(accid('b'), 0) .addModifier(accid('b'), 1) .addModifier(accid('n'), 3), f .StaveNote({ keys: ['e/4', 'g/4', 'b/4', 'c/5'], duration: '4' }) .addModifier(accid('b').setAsCautionary(), 0) .addModifier(accid('b').setAsCautionary(), 1) .addModifier(accid('bb'), 2) .addModifier(accid('b'), 3), f .StaveNote({ keys: ['b/3', 'e/4', 'a/4', 'd/5', 'g/5'], duration: '8' }) .addModifier(accid('bb'), 0) .addModifier(accid('b').setAsCautionary(), 1) .addModifier(accid('n').setAsCautionary(), 2) .addModifier(accid('#'), 3) .addModifier(accid('n').setAsCautionary(), 4), ]; Formatter.SimpleFormat(notes, 0, { paddingBetween: 20 }); notes.forEach((note, index) => { Note.plotMetrics(f.getContext(), note, 140); options.assert.ok(note.getModifiersByType('Accidental').length > 0, 'Note ' + index + ' has accidentals'); note.getModifiersByType('Accidental').forEach((accid, index) => { options.assert.ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width'); }); }); f.draw(); VexFlowTests.plotLegendForNoteWidth(f.getContext(), 480, 140); options.assert.ok(true, 'Full Accidental'); } function basicStemDown(options) { const f = VexFlowTests.makeFactory(options, 700, 240); const accid = makeNewAccid(f); f.Stave({ x: 10, y: 10, width: 550 }); const notes = [ f .StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w', stem_direction: -1 }) .addModifier(accid('b'), 0) .addModifier(accid('#'), 1), f .StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: '2', stem_direction: -1 }) .addModifier(accid('##'), 0) .addModifier(accid('n'), 1) .addModifier(accid('bb'), 2) .addModifier(accid('b'), 3) .addModifier(accid('#'), 4) .addModifier(accid('n'), 5) .addModifier(accid('bb'), 6), f .StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16', stem_direction: -1 }) .addModifier(accid('n'), 0) .addModifier(accid('#'), 1) .addModifier(accid('#'), 2) .addModifier(accid('b'), 3) .addModifier(accid('bb'), 4) .addModifier(accid('##'), 5) .addModifier(accid('#'), 6), ]; Formatter.SimpleFormat(notes, 0, { paddingBetween: 30 }); notes.forEach((note, noteIndex) => { Note.plotMetrics(f.getContext(), note, 140); options.assert.ok(note.getModifiersByType('Accidental').length > 0, 'Note ' + noteIndex + ' has accidentals'); note.getModifiersByType('Accidental').forEach((accid, accidIndex) => { options.assert.ok(accid.getWidth() > 0, 'Accidental ' + accidIndex + ' has set width'); }); }); f.draw(); VexFlowTests.plotLegendForNoteWidth(f.getContext(), 480, 140); options.assert.ok(true, 'Full Accidental'); } function multiVoice(options) { function showNotes(note1, note2, stave, ctx, x) { const modifierContext = new ModifierContext(); note1.addToModifierContext(modifierContext); note2.addToModifierContext(modifierContext); new TickContext().addTickable(note1).addTickable(note2).preFormat().setX(x); note1.setContext(ctx).draw(); note2.setContext(ctx).draw(); Note.plotMetrics(ctx, note1, 180); Note.plotMetrics(ctx, note2, 15); } const f = VexFlowTests.makeFactory(options, 460, 250); const accid = makeNewAccid(f); const stave = f.Stave({ x: 10, y: 45, width: 420 }); const ctx = f.getContext(); stave.draw(); let note1 = f .StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '2', stem_direction: -1 }) .addModifier(accid('b'), 0) .addModifier(accid('n'), 1) .addModifier(accid('#'), 2) .setStave(stave); let note2 = f .StaveNote({ keys: ['d/5', 'a/5', 'b/5'], duration: '2', stem_direction: 1 }) .addModifier(accid('b'), 0) .addModifier(accid('bb'), 1) .addModifier(accid('##'), 2) .setStave(stave); showNotes(note1, note2, stave, ctx, 60); note1 = f .StaveNote({ keys: ['c/4', 'e/4', 'c/5'], duration: '2', stem_direction: -1 }) .addModifier(accid('b'), 0) .addModifier(accid('n'), 1) .addModifier(accid('#'), 2) .setStave(stave); note2 = f .StaveNote({ keys: ['d/5', 'a/5', 'b/5'], duration: '4', stem_direction: 1 }) .addModifier(accid('b'), 0) .setStave(stave); showNotes(note1, note2, stave, ctx, 150); note1 = f .StaveNote({ keys: ['d/4', 'c/5', 'd/5'], duration: '2', stem_direction: -1 }) .addModifier(accid('b'), 0) .addModifier(accid('n'), 1) .addModifier(accid('#'), 2) .setStave(stave); note2 = f .StaveNote({ keys: ['d/5', 'a/5', 'b/5'], duration: '4', stem_direction: 1 }) .addModifier(accid('b'), 0) .setStave(stave); showNotes(note1, note2, stave, ctx, 250); VexFlowTests.plotLegendForNoteWidth(ctx, 350, 150); options.assert.ok(true, 'Full Accidental'); } function microtonal(options) { const f = VexFlowTests.makeFactory(options, 700, 240); const accid = makeNewAccid(f); const ctx = f.getContext(); f.Stave({ x: 10, y: 10, width: 650 }); const notes = [ f .StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '1' }) .addModifier(accid('db'), 0) .addModifier(accid('d'), 1), f .StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: '2' }) .addModifier(accid('bbs'), 0) .addModifier(accid('++'), 1) .addModifier(accid('+'), 2) .addModifier(accid('d'), 3) .addModifier(accid('db'), 4) .addModifier(accid('+'), 5) .addModifier(accid('##'), 6), f .StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16' }) .addModifier(accid('++'), 0) .addModifier(accid('bbs'), 1) .addModifier(accid('+'), 2) .addModifier(accid('b'), 3) .addModifier(accid('db'), 4) .addModifier(accid('##'), 5) .addModifier(accid('#'), 6), f .StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: '1' }) .addModifier(accid('#'), 0) .addModifier(accid('db').setAsCautionary(), 1) .addModifier(accid('bbs').setAsCautionary(), 2) .addModifier(accid('b'), 3) .addModifier(accid('++').setAsCautionary(), 4) .addModifier(accid('d').setAsCautionary(), 5), f .StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'd/5', 'g/5'], duration: '16' }) .addModifier(accid('++-'), 0) .addModifier(accid('+-'), 1) .addModifier(accid('bs'), 2) .addModifier(accid('bss'), 3) .addModifier(accid('afhf'), 4) .addModifier(accid('ashs'), 5), ]; Formatter.SimpleFormat(notes, 0, { paddingBetween: 35 }); notes.forEach((note, index) => { Note.plotMetrics(f.getContext(), note, 140); options.assert.ok(note.getModifiersByType('Accidental').length > 0, 'Note ' + index + ' has accidentals'); note.getModifiersByType('Accidental').forEach((accid, index) => { options.assert.ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width'); }); }); f.draw(); VexFlowTests.plotLegendForNoteWidth(ctx, 580, 140); options.assert.ok(true, 'Microtonal Accidental'); } function microtonal_iranian(options) { const f = VexFlowTests.makeFactory(options, 700, 240); const accid = makeNewAccid(f); const ctx = f.getContext(); f.Stave({ x: 10, y: 10, width: 650 }); const notes = [ f .StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: '1' }) .addModifier(accid('k'), 0) .addModifier(accid('o'), 1), f .StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: '2' }) .addModifier(accid('b'), 0) .addModifier(accid('k'), 1) .addModifier(accid('n'), 2) .addModifier(accid('o'), 3) .addModifier(accid('#'), 4) .addModifier(accid('bb'), 5) .addModifier(accid('##'), 6), f .StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16' }) .addModifier(accid('o'), 0) .addModifier(accid('k'), 1) .addModifier(accid('n'), 2) .addModifier(accid('b'), 3) .addModifier(accid('bb'), 4) .addModifier(accid('##'), 5) .addModifier(accid('#'), 6), f .StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: '1' }) .addModifier(accid('#'), 0) .addModifier(accid('o').setAsCautionary(), 1) .addModifier(accid('n').setAsCautionary(), 2) .addModifier(accid('b'), 3) .addModifier(accid('k').setAsCautionary(), 4), f .StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4'], duration: '16' }) .addModifier(accid('k'), 0) .addModifier(accid('k'), 1) .addModifier(accid('k'), 2) .addModifier(accid('k'), 3), ]; Formatter.SimpleFormat(notes, 0, { paddingBetween: 35 }); notes.forEach((note, index) => { Note.plotMetrics(f.getContext(), note, 140); options.assert.ok(note.getModifiersByType('Accidental').length > 0, 'Note ' + index + ' has accidentals'); note.getModifiersByType('Accidental').forEach((accid, index) => { options.assert.ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width'); }); }); f.draw(); VexFlowTests.plotLegendForNoteWidth(ctx, 580, 140); options.assert.ok(true, 'Microtonal Accidental (Iranian)'); } function sagittal(options) { const f = VexFlowTests.makeFactory(options, 700, 240); const accid = makeNewAccid(f); const ctx = f.getContext(); f.Stave({ x: 10, y: 10, width: 650 }); const notes = [ f .StaveNote({ keys: ['d/4', 'f/4', 'b/4', 'b/4'], duration: '4' }) .addModifier(accid('accSagittal11MediumDiesisUp'), 1) .addModifier(accid('accSagittal5CommaDown'), 2) .addModifier(accid('b'), 3) .addModifier(accid('accSagittal7CommaDown'), 3), f .StaveNote({ keys: ['d/4', 'f/4', 'a/4', 'b/4'], duration: '4' }) .addModifier(accid('accSagittal35LargeDiesisDown'), 2), f.StaveNote({ keys: ['c/4', 'e/4', 'g/4', 'c/5'], duration: '8' }).addModifier(accid('accSagittal5CommaDown'), 1), f .StaveNote({ keys: ['c/4', 'e/4', 'g/4', 'b/4'], duration: '8' }) .addModifier(accid('b'), 1) .addModifier(accid('accSagittal7CommaDown'), 1) .addModifier(accid('accSagittal11LargeDiesisDown'), 3), f .StaveNote({ keys: ['d/4', 'f/4', 'b/4', 'b/4'], duration: '4' }) .addModifier(accid('accSagittal11MediumDiesisUp'), 1) .addModifier(accid('accSagittal5CommaDown'), 2) .addModifier(accid('accSagittalFlat7CDown'), 3), f .StaveNote({ keys: ['d/4', 'f/4', 'a/4', 'b/4'], duration: '4' }) .addModifier(accid('accSagittal35LargeDiesisDown'), 2), f.StaveNote({ keys: ['c/4', 'e/4', 'g/4', 'c/5'], duration: '8' }).addModifier(accid('accSagittal5CommaDown'), 1), f .StaveNote({ keys: ['c/4', 'e/4', 'g/4', 'b/4'], duration: '8' }) .addModifier(accid('accSagittalFlat7CDown'), 1) .addModifier(accid('accSagittal11LargeDiesisDown'), 3), ]; f.StaveTie({ from: notes[0], to: notes[1], first_indices: [0, 1], last_indices: [0, 1], }); f.StaveTie({ from: notes[0], to: notes[1], first_indices: [3], last_indices: [3], options: { direction: Stem.DOWN, }, }); f.StaveTie({ from: notes[4], to: notes[5], first_indices: [0, 1], last_indices: [0, 1], }); f.StaveTie({ from: notes[4], to: notes[5], first_indices: [3], last_indices: [3], options: { direction: Stem.DOWN, }, }); f.Beam({ notes: notes.slice(2, 4) }); f.Beam({ notes: notes.slice(6, 8) }); Formatter.SimpleFormat(notes); notes.forEach((note, index) => { Note.plotMetrics(f.getContext(), note, 140); options.assert.ok(note.getModifiersByType('Accidental').length > 0, 'Note ' + index + ' has accidentals'); note.getModifiersByType('Accidental').forEach((accid, index) => { options.assert.ok(accid.getWidth() > 0, 'Accidental ' + index + ' has set width'); }); }); f.draw(); VexFlowTests.plotLegendForNoteWidth(ctx, 580, 140); options.assert.ok(true, 'Sagittal'); } function automaticAccidentals0(options) { const f = VexFlowTests.makeFactory(options, 700, 200); const stave = f.Stave(); const notes = [ { keys: ['c/4', 'c/5'], duration: '4' }, { keys: ['c#/4', 'c#/5'], duration: '4' }, { keys: ['c#/4', 'c#/5'], duration: '4' }, { keys: ['c##/4', 'c##/5'], duration: '4' }, { keys: ['c##/4', 'c##/5'], duration: '4' }, { keys: ['c/4', 'c/5'], duration: '4' }, { keys: ['cn/4', 'cn/5'], duration: '4' }, { keys: ['cbb/4', 'cbb/5'], duration: '4' }, { keys: ['cbb/4', 'cbb/5'], duration: '4' }, { keys: ['cb/4', 'cb/5'], duration: '4' }, { keys: ['cb/4', 'cb/5'], duration: '4' }, { keys: ['c/4', 'c/5'], duration: '4' }, ].map(f.StaveNote.bind(f)); const gracenotes = [{ keys: ['d#/4'], duration: '16', slash: true }].map(f.GraceNote.bind(f)); notes[0].addModifier(f.GraceNoteGroup({ notes: gracenotes }).beamNotes(), 0); const voice = f .Voice() .setMode(Voice.Mode.SOFT) .addTickable(new TimeSigNote('12/4').setStave(stave)) .addTickables(notes); Accidental.applyAccidentals([voice], 'C'); new Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true); } function automaticAccidentals1(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('Ab'); const notes = [ { keys: ['c/4'], duration: '4' }, { keys: ['d/4'], duration: '4' }, { keys: ['e/4'], duration: '4' }, { keys: ['f/4'], duration: '4' }, { keys: ['g/4'], duration: '4' }, { keys: ['a/4'], duration: '4' }, { keys: ['b/4'], duration: '4' }, { keys: ['c/5'], duration: '4' }, ].map(f.StaveNote.bind(f)); const voice = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes); Accidental.applyAccidentals([voice], 'Ab'); new Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true); } function automaticAccidentals2(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('A'); const notes = [ { keys: ['a/4'], duration: '4' }, { keys: ['b/4'], duration: '4' }, { keys: ['c#/5'], duration: '4' }, { keys: ['d/5'], duration: '4' }, { keys: ['e/5'], duration: '4' }, { keys: ['f#/5'], duration: '4' }, { keys: ['g#/5'], duration: '4' }, { keys: ['a/5'], duration: '4' }, ].map(f.StaveNote.bind(f)); const voice = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes); Accidental.applyAccidentals([voice], 'A'); new Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true); } function automaticAccidentals3(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('A'); const score = f.EasyScore(); score.set({ time: '8/4' }); const notes = score.notes('A4/q, B4/q, C#5/q, D5/q, E5/q,F#5/q, G#5/q, A5/q', { stem: 'UP' }); const voice = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes); Accidental.applyAccidentals([voice], 'A'); new Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); options.assert.ok(true); } function automaticAccidentalsMultiVoiceInline(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('Ab'); const notes0 = [ { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['d/4'], duration: '4', stem_direction: -1 }, { keys: ['e/4'], duration: '4', stem_direction: -1 }, { keys: ['f/4'], duration: '4', stem_direction: -1 }, { keys: ['g/4'], duration: '4', stem_direction: -1 }, { keys: ['a/4'], duration: '4', stem_direction: -1 }, { keys: ['b/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, ].map(f.StaveNote.bind(f)); const notes1 = [ { keys: ['c/5'], duration: '4' }, { keys: ['d/5'], duration: '4' }, { keys: ['e/5'], duration: '4' }, { keys: ['f/5'], duration: '4' }, { keys: ['g/5'], duration: '4' }, { keys: ['a/5'], duration: '4' }, { keys: ['b/5'], duration: '4' }, { keys: ['c/6'], duration: '4' }, ].map(f.StaveNote.bind(f)); const voice0 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes0); const voice1 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes1); Accidental.applyAccidentals([voice0, voice1], 'Ab'); options.assert.equal(hasAccidental(notes0[0]), false); options.assert.equal(hasAccidental(notes0[1]), true); options.assert.equal(hasAccidental(notes0[2]), true); options.assert.equal(hasAccidental(notes0[3]), false); options.assert.equal(hasAccidental(notes0[4]), false); options.assert.equal(hasAccidental(notes0[5]), true); options.assert.equal(hasAccidental(notes0[6]), true); options.assert.equal(hasAccidental(notes0[7]), false); options.assert.equal(hasAccidental(notes1[0]), false); options.assert.equal(hasAccidental(notes1[1]), true); options.assert.equal(hasAccidental(notes1[2]), true); options.assert.equal(hasAccidental(notes1[3]), false); options.assert.equal(hasAccidental(notes1[4]), false); options.assert.equal(hasAccidental(notes1[5]), true); options.assert.equal(hasAccidental(notes1[6]), true); options.assert.equal(hasAccidental(notes1[7]), false); new Formatter().joinVoices([voice0, voice1]).formatToStave([voice0, voice1], stave); f.draw(); options.assert.ok(true); } function automaticAccidentalsMultiVoiceOffset(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('Cb'); const notes0 = [ { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['d/4'], duration: '4', stem_direction: -1 }, { keys: ['e/4'], duration: '4', stem_direction: -1 }, { keys: ['f/4'], duration: '4', stem_direction: -1 }, { keys: ['g/4'], duration: '4', stem_direction: -1 }, { keys: ['a/4'], duration: '4', stem_direction: -1 }, { keys: ['b/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, ].map(f.StaveNote.bind(f)); const notes1 = [ { keys: ['c/5'], duration: '8' }, { keys: ['c/5'], duration: '4' }, { keys: ['d/5'], duration: '4' }, { keys: ['e/5'], duration: '4' }, { keys: ['f/5'], duration: '4' }, { keys: ['g/5'], duration: '4' }, { keys: ['a/5'], duration: '4' }, { keys: ['b/5'], duration: '4' }, { keys: ['c/6'], duration: '4' }, ].map(f.StaveNote.bind(f)); const voice0 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes0); const voice1 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes1); Accidental.applyAccidentals([voice0, voice1], 'Cb'); options.assert.equal(hasAccidental(notes0[0]), true); options.assert.equal(hasAccidental(notes0[1]), true); options.assert.equal(hasAccidental(notes0[2]), true); options.assert.equal(hasAccidental(notes0[3]), true); options.assert.equal(hasAccidental(notes0[4]), true); options.assert.equal(hasAccidental(notes0[5]), true); options.assert.equal(hasAccidental(notes0[6]), true); options.assert.equal(hasAccidental(notes0[7]), false, 'Natural Remembered'); options.assert.equal(hasAccidental(notes1[0]), true); options.assert.equal(hasAccidental(notes1[1]), false); options.assert.equal(hasAccidental(notes1[2]), true); options.assert.equal(hasAccidental(notes1[3]), true); options.assert.equal(hasAccidental(notes1[4]), true); options.assert.equal(hasAccidental(notes1[5]), true); options.assert.equal(hasAccidental(notes1[6]), true); options.assert.equal(hasAccidental(notes1[7]), true); new Formatter().joinVoices([voice0, voice1]).formatToStave([voice0, voice1], stave); f.draw(); options.assert.ok(true); } function automaticAccidentalsCornerCases1(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('C'); const notes0 = [ { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, ].map(f.StaveNote.bind(f)); const voice0 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes0); Accidental.applyAccidentals([voice0], 'C'); options.assert.equal(hasAccidental(notes0[0]), false); options.assert.equal(hasAccidental(notes0[1]), true); options.assert.equal(hasAccidental(notes0[2]), false); options.assert.equal(hasAccidental(notes0[3]), true); options.assert.equal(hasAccidental(notes0[4]), false); options.assert.equal(hasAccidental(notes0[5]), true); options.assert.equal(hasAccidental(notes0[6]), false); options.assert.equal(hasAccidental(notes0[7]), true); options.assert.equal(hasAccidental(notes0[8]), false); new Formatter().joinVoices([voice0]).formatToStave([voice0], stave); f.draw(); options.assert.ok(true); } function automaticAccidentalsCornerCases2(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('C'); const notes0 = [ { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/5'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/5'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, ].map(f.StaveNote.bind(f)); const voice0 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes0); Accidental.applyAccidentals([voice0], 'C'); options.assert.equal(hasAccidental(notes0[0]), false); options.assert.equal(hasAccidental(notes0[2]), true); options.assert.equal(hasAccidental(notes0[4]), false); options.assert.equal(hasAccidental(notes0[6]), true); options.assert.equal(hasAccidental(notes0[8]), false); options.assert.equal(hasAccidental(notes0[10]), true); options.assert.equal(hasAccidental(notes0[12]), false); options.assert.equal(hasAccidental(notes0[14]), true); options.assert.equal(hasAccidental(notes0[16]), false); options.assert.equal(hasAccidental(notes0[1]), false); options.assert.equal(hasAccidental(notes0[3]), true); options.assert.equal(hasAccidental(notes0[5]), false); options.assert.equal(hasAccidental(notes0[7]), true); options.assert.equal(hasAccidental(notes0[9]), false); options.assert.equal(hasAccidental(notes0[11]), true); options.assert.equal(hasAccidental(notes0[13]), false); options.assert.equal(hasAccidental(notes0[15]), true); options.assert.equal(hasAccidental(notes0[17]), false); new Formatter().joinVoices([voice0]).formatToStave([voice0], stave); f.draw(); options.assert.ok(true); } function automaticAccidentalsCornerCases3(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('C#'); const notes0 = [ { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, ].map(f.StaveNote.bind(f)); const voice0 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes0); Accidental.applyAccidentals([voice0], 'C#'); options.assert.equal(hasAccidental(notes0[0]), true); options.assert.equal(hasAccidental(notes0[1]), true); options.assert.equal(hasAccidental(notes0[2]), false); options.assert.equal(hasAccidental(notes0[3]), true); options.assert.equal(hasAccidental(notes0[4]), false); options.assert.equal(hasAccidental(notes0[5]), true); options.assert.equal(hasAccidental(notes0[6]), false); options.assert.equal(hasAccidental(notes0[7]), true); options.assert.equal(hasAccidental(notes0[8]), false); new Formatter().joinVoices([voice0]).formatToStave([voice0], stave); f.draw(); options.assert.ok(true); } function automaticAccidentalsCornerCases4(options) { const f = VexFlowTests.makeFactory(options, 700, 150); const stave = f.Stave().addKeySignature('C#'); const notes0 = [ { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/5'], duration: '4', stem_direction: -1 }, { keys: ['c#/4'], duration: '4', stem_direction: -1 }, { keys: ['c#/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/5'], duration: '4', stem_direction: -1 }, { keys: ['cb/4'], duration: '4', stem_direction: -1 }, { keys: ['cb/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, { keys: ['c/4'], duration: '4', stem_direction: -1 }, { keys: ['c/5'], duration: '4', stem_direction: -1 }, ].map(f.StaveNote.bind(f)); const voice0 = f.Voice().setMode(Voice.Mode.SOFT).addTickables(notes0); Accidental.applyAccidentals([voice0], 'C#'); options.assert.equal(hasAccidental(notes0[0]), true); options.assert.equal(hasAccidental(notes0[2]), true); options.assert.equal(hasAccidental(notes0[4]), false); options.assert.equal(hasAccidental(notes0[6]), true); options.assert.equal(hasAccidental(notes0[8]), false); options.assert.equal(hasAccidental(notes0[10]), true); options.assert.equal(hasAccidental(notes0[12]), false); options.assert.equal(hasAccidental(notes0[14]), true); options.assert.equal(hasAccidental(notes0[16]), false); options.assert.equal(hasAccidental(notes0[1]), true); options.assert.equal(hasAccidental(notes0[3]), true); options.assert.equal(hasAccidental(notes0[5]), false); options.assert.equal(hasAccidental(notes0[7]), true); options.assert.equal(hasAccidental(notes0[9]), false); options.assert.equal(hasAccidental(notes0[11]), true); options.assert.equal(hasAccidental(notes0[13]), false); options.assert.equal(hasAccidental(notes0[15]), true); options.assert.equal(hasAccidental(notes0[17]), false); new Formatter().joinVoices([voice0]).formatToStave([voice0], stave); f.draw(); options.assert.ok(true); } function factoryAPI(options) { const f = VexFlowTests.makeFactory(options, 700, 240); f.Stave({ x: 10, y: 10, width: 550 }); const accid = makeNewAccid(f); const notes = [ f .StaveNote({ keys: ['c/4', 'e/4', 'a/4'], duration: 'w' }) .addModifier(accid('b'), 0) .addModifier(accid('#'), 1), f .StaveNote({ keys: ['d/4', 'e/4', 'f/4', 'a/4', 'c/5', 'e/5', 'g/5'], duration: 'h' }) .addModifier(accid('##'), 0) .addModifier(accid('n'), 1) .addModifier(accid('bb'), 2) .addModifier(accid('b'), 3) .addModifier(accid('#'), 4) .addModifier(accid('n'), 5) .addModifier(accid('bb'), 6), f .StaveNote({ keys: ['f/4', 'g/4', 'a/4', 'b/4', 'c/5', 'e/5', 'g/5'], duration: '16' }) .addModifier(accid('n'), 0) .addModifier(accid('#'), 1) .addModifier(accid('#'), 2) .addModifier(accid('b'), 3) .addModifier(accid('bb'), 4) .addModifier(accid('##'), 5) .addModifier(accid('#'), 6), f .StaveNote({ keys: ['a/3', 'c/4', 'e/4', 'b/4', 'd/5', 'g/5'], duration: 'w' }) .addModifier(accid('#'), 0) .addModifier(accid('##').setAsCautionary(), 1) .addModifier(accid('#').setAsCautionary(), 2) .addModifier(accid('b'), 3) .addModifier(accid('bb').setAsCautionary(), 4) .addModifier(accid('b').setAsCautionary(), 5), ]; Formatter.SimpleFormat(notes); notes.forEach((n, i) => { options.assert.ok(n.getModifiersByType('Accidental').length > 0, 'Note ' + i + ' has accidentals'); n.getModifiersByType('Accidental').forEach((accid, i) => { options.assert.ok(accid.getWidth() > 0, 'Accidental ' + i + ' has set width'); }); }); f.draw(); options.assert.ok(true, 'Factory API'); } VexFlowTests.register(AccidentalTests); export { AccidentalTests };