UNPKG

vexflow

Version:

A JavaScript library for rendering music notation and guitar tablature.

406 lines (405 loc) 18.4 kB
import { VexFlowTests } from './vexflow_test_helpers.js'; import { Accidental } from '../src/accidental.js'; import { ChordSymbol } from '../src/chordsymbol.js'; import { Element } from '../src/element.js'; import { Formatter } from '../src/formatter.js'; import { Ornament } from '../src/ornament.js'; import { Stave } from '../src/stave.js'; import { StaveNote } from '../src/stavenote.js'; const ChordSymbolTests = { Start() { QUnit.module('ChordSymbol'); const run = VexFlowTests.runTests; run('Bounding Box', withModifiers, { drawBoundingBox: true }); run('Chord Symbol With Modifiers', withModifiers, { drawBoundingBox: false }); run('Chord Symbol Font Size Tests', fontSize); run('Chord Symbol Kerning Tests', kern); run('Top Chord Symbols', top); run('Top Chord Symbols Justified', topJustify); run('Bottom Chord Symbols', bottom); run('Bottom Stem Down Chord Symbols', bottomStemDown); run('Double Bottom Chord Symbols', doubleBottom); run('Wide Chord Symbols', wide); }, }; const superscript = { symbolModifier: ChordSymbol.symbolModifiers.SUPERSCRIPT }; const subscript = { symbolModifier: ChordSymbol.symbolModifiers.SUBSCRIPT }; const note = (factory, keys, duration, chordSymbol) => factory.StaveNote({ keys, duration }).addModifier(chordSymbol, 0); function getGlyphWidth(charCode) { const el = new Element(); el.setText(String.fromCharCode(charCode)); return el.getWidth(); } function withModifiers(options) { const f = VexFlowTests.makeFactory(options, 750, 580); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chords, y) { const notes = [ note(f, ['c/4'], 'q', chords[0]).addModifier(new Ornament('doit')), note(f, ['c/4'], 'q', chords[1]), note(f, ['c/4'], 'q', chords[2]).addModifier(new Ornament('fall')), note(f, ['c/4'], 'q', chords[3]), ]; const score = f.EasyScore(); const voice = score.voice(notes, { time: '4/4' }); const formatter = f.Formatter(); formatter.joinVoices([voice]); const voiceW = formatter.preCalculateMinTotalWidth([voice]); const staffW = voiceW + Stave.defaultPadding + getGlyphWidth(0xe050); formatter.format([voice], voiceW); const staff = f.Stave({ x: 10, y, width: staffW }).addClef('treble').drawWithStyle(); voice.draw(ctx, staff); } let chords = []; chords.push(f .ChordSymbol({ fontSize: 10 }) .addText('F7') .addGlyph('leftParenTall') .addGlyphOrText('b9', superscript) .addGlyphOrText('#11', subscript) .addGlyph('rightParenTall')); chords.push(f.ChordSymbol({ fontSize: 12 }).addText('F7').addGlyphOrText('b9', superscript).addGlyphOrText('#11', subscript)); chords.push(f .ChordSymbol({ fontSize: 14 }) .addText('F7') .addGlyph('leftParenTall') .addGlyphOrText('add 3', superscript) .addGlyphOrText('omit 9', subscript) .addGlyph('rightParenTall')); chords.push(f .ChordSymbol({ fontSize: 16 }) .addText('F7') .addGlyph('leftParenTall') .addGlyphOrText('b9', superscript) .addGlyphOrText('#11', subscript) .addGlyph('rightParenTall')); draw(chords, 40); if (options.params.drawBoundingBox === true) { chords.forEach((element) => VexFlowTests.drawBoundingBox(f.getContext(), element)); } chords = []; chords.push(f .ChordSymbol({ fontSize: 10 }) .setFontSize(10) .addText('F7') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript)); chords.push(f.ChordSymbol({ fontSize: 12 }).addText('F7').addGlyphOrText('#11', superscript).addGlyphOrText('b9', subscript)); chords.push(f.ChordSymbol({ fontSize: 14 }).addText('F7').addGlyphOrText('#11', superscript).addGlyphOrText('b9', subscript)); chords.push(f .ChordSymbol({ fontSize: 16 }) .setFontSize(16) .addText('F7') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript)); draw(chords, 140); if (options.params.drawBoundingBox === true) { chords.forEach((element) => VexFlowTests.drawBoundingBox(f.getContext(), element)); } chords = [ f.ChordSymbol({ fontSize: 10 }).addGlyphOrText('Ab').addGlyphOrText('7(#11b9)', superscript), f.ChordSymbol({ fontSize: 14 }).addGlyphOrText('C#').addGlyphOrText('7(#11b9)', superscript), f.ChordSymbol({ fontSize: 16 }).addGlyphOrText('Ab').addGlyphOrText('7(#11b9)', superscript), f.ChordSymbol({ fontSize: 18 }).addGlyphOrText('C#').addGlyphOrText('7(#11b9)', superscript), ]; draw(chords, 240); if (options.params.drawBoundingBox === true) { chords.forEach((element) => VexFlowTests.drawBoundingBox(f.getContext(), element)); } options.assert.ok(true, 'Font Size Chord Symbol'); } function fontSize(options) { const f = VexFlowTests.makeFactory(options, 750, 580); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chords, y) { const stave = f.Stave({ x: 10, y, width: 450 }).addClef('treble'); const notes = [ note(f, ['c/4'], 'q', chords[0]), note(f, ['c/4'], 'q', chords[1]), note(f, ['c/4'], 'q', chords[2]), note(f, ['c/4'], 'q', chords[3]), ]; const score = f.EasyScore(); const voice = score.voice(notes, { time: '4/4' }); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); } let chords = []; chords.push(f .ChordSymbol({ fontSize: 10 }) .addText('F7') .addGlyph('leftParenTall') .addGlyphOrText('b9', superscript) .addGlyphOrText('#11', subscript) .addGlyph('rightParenTall')); chords.push(f.ChordSymbol({ fontSize: 12 }).addText('F7').addGlyphOrText('b9', superscript).addGlyphOrText('#11', subscript)); chords.push(f .ChordSymbol({ fontSize: 14 }) .addText('F7') .addGlyph('leftParenTall') .addGlyphOrText('add 3', superscript) .addGlyphOrText('omit 9', subscript) .addGlyph('rightParenTall')); chords.push(f .ChordSymbol({ fontSize: 16 }) .addText('F7') .addGlyph('leftParenTall') .addGlyphOrText('b9', superscript) .addGlyphOrText('#11', subscript) .addGlyph('rightParenTall')); draw(chords, 40); chords = []; chords.push(f .ChordSymbol({ fontSize: 10 }) .setFontSize(10) .addText('F7') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript)); chords.push(f.ChordSymbol({ fontSize: 12 }).addText('F7').addGlyphOrText('#11', superscript).addGlyphOrText('b9', subscript)); chords.push(f.ChordSymbol({ fontSize: 14 }).addText('F7').addGlyphOrText('#11', superscript).addGlyphOrText('b9', subscript)); chords.push(f .ChordSymbol({ fontSize: 16 }) .setFontSize(16) .addText('F7') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript)); draw(chords, 140); chords = [ f.ChordSymbol({ fontSize: 10 }).addGlyphOrText('Ab').addGlyphOrText('7(#11b9)', superscript), f.ChordSymbol({ fontSize: 14 }).addGlyphOrText('C#').addGlyphOrText('7(#11b9)', superscript), f.ChordSymbol({ fontSize: 16 }).addGlyphOrText('Ab').addGlyphOrText('7(#11b9)', superscript), f.ChordSymbol({ fontSize: 18 }).addGlyphOrText('C#').addGlyphOrText('7(#11b9)', superscript), ]; draw(chords, 240); options.assert.ok(true, 'Font Size Chord Symbol'); } function kern(options) { const f = VexFlowTests.makeFactory(options, 650 * 1.5, 650); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chords, y) { const stave = f.Stave({ x: 10, y, width: 450 }).addClef('treble').setContext(ctx).drawWithStyle(); const notes = [ note(f, ['C/4'], 'q', chords[0]), note(f, ['C/4'], 'q', chords[1]), note(f, ['C/4'], 'q', chords[2]), note(f, ['C/4'], 'q', chords[3]), ]; const score = f.EasyScore(); const voice = score.voice(notes, { time: '4/4' }); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); } let chords = [ f.ChordSymbol().addText('A').addGlyphSuperscript('dim'), f.ChordSymbol().addText('A').addGlyphSuperscript('dim'), f.ChordSymbol({ hJustify: 'left' }).addText('C').addGlyph('halfDiminished', superscript), f.ChordSymbol().addText('D').addGlyph('halfDiminished', superscript), ]; draw(chords, 10); chords = [ f.ChordSymbol().addText('A').addGlyphSuperscript('dim'), f.ChordSymbol().addText('A').addGlyphSuperscript('dim'), f.ChordSymbol().addText('A').addGlyphSuperscript('+').addTextSuperscript('5'), f.ChordSymbol().addText('G').addGlyphSuperscript('+').addTextSuperscript('5'), ]; draw(chords, 110); chords = [ f.ChordSymbol().addText('A').addGlyph('-'), f.ChordSymbol().addText('E').addGlyph('-'), f.ChordSymbol().addText('A').addGlyphOrText('(#11)', superscript), f.ChordSymbol().addText('E').addGlyphOrText('(#9)', superscript), ]; draw(chords, 210); chords = [ f.ChordSymbol().addGlyphOrText('F/B').addGlyphOrText('b', superscript), f.ChordSymbol().addText('E').addGlyphOrText('V/V'), f.ChordSymbol().addText('A').addGlyphOrText('(#11)', superscript), f.ChordSymbol().addText('E').addGlyphOrText('(#9)', superscript), ]; draw(chords, 310); options.assert.ok(true, 'Chord Symbol Kerning Tests'); } function top(options) { const f = VexFlowTests.makeFactory(options, 650 * 1.5, 650); const ctx = f.getContext(); ctx.scale(1.5, 1.5); const note = (factory, keys, duration, chordSymbol, direction) => factory.StaveNote({ keys, duration, stemDirection: direction }).addModifier(chordSymbol, 0); function draw(c1, c2, y) { const stave = f.Stave({ x: 10, y, width: 450 }).addClef('treble').setContext(ctx).drawWithStyle(); const notes = [ note(f, ['e/4', 'a/4', 'd/5'], 'h', c1, 1).addModifier(new Accidental('b'), 0), note(f, ['c/5', 'e/5', 'c/6'], 'h', c2, -1), ]; const score = f.EasyScore(); const voice = score.voice(notes, { time: '4/4' }); f.Formatter().joinVoices([voice]).formatToStave([voice], stave); f.draw(); } let chord1 = f.ChordSymbol().addText('F7').setHorizontal('left').addGlyphOrText('(#11b9)', superscript); let chord2 = f.ChordSymbol().addText('C').setHorizontal('left').addGlyphSuperscript('majorSeventh'); draw(chord1, chord2, 40); chord1 = f .ChordSymbol() .addText('F7') .addTextSuperscript('(') .addGlyphOrText('#11b9', superscript) .addTextSuperscript(')'); chord2 = f.ChordSymbol().addText('C').setHorizontal('left').addTextSuperscript('Maj.'); draw(chord1, chord2, 140); chord1 = f .ChordSymbol() .addText('F7') .setHorizontal('left') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript); chord2 = f.ChordSymbol().addText('C').addTextSuperscript('sus4'); draw(chord1, chord2, 240); options.assert.ok(true, 'Top Chord Symbol'); } function topJustify(options) { const f = VexFlowTests.makeFactory(options, 500 * 1.5, 680); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chord1, chord2, y) { const stave = new Stave(10, y, 450).addClef('treble').setContext(ctx).drawWithStyle(); const notes = [ note(f, ['e/4', 'a/4', 'd/5'], 'h', chord1).addModifier(new Accidental('b'), 0), note(f, ['c/4', 'e/4', 'B/4'], 'h', chord2), ]; Formatter.FormatAndDraw(ctx, stave, notes); } let chord1 = f.ChordSymbol().addText('F7').setHorizontal('left').addGlyphOrText('(#11b9)', superscript); let chord2 = f.ChordSymbol({ hJustify: 'left' }).addText('C').addGlyphSuperscript('majorSeventh'); draw(chord1, chord2, 40); chord1 = f .ChordSymbol({ hJustify: 'center' }) .addText('F7') .setHorizontal('left') .addGlyphOrText('(#11b9)', superscript); chord2 = f.ChordSymbol({ hJustify: 'center' }).addText('C').addTextSuperscript('Maj.'); draw(chord1, chord2, 140); chord1 = f .ChordSymbol({ hJustify: 'right' }) .addText('F7') .setHorizontal('left') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript); chord2 = f.ChordSymbol({ hJustify: 'right' }).addText('C').addTextSuperscript('Maj.'); draw(chord1, chord2, 240); chord1 = f .ChordSymbol({ hJustify: 'left' }) .addText('F7') .setHorizontal('left') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript); chord2 = f.ChordSymbol({ hJustify: 'centerStem' }).addText('C').addTextSuperscript('Maj.'); draw(chord1, chord2, 340); options.assert.ok(true, 'Top Chord Justified'); } function bottom(options) { const f = VexFlowTests.makeFactory(options, 600 * 1.5, 230); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chords, y) { const stave = new Stave(10, y, 400).addClef('treble').setContext(ctx).drawWithStyle(); const notes = [ note(f, ['c/4', 'f/4', 'a/4'], 'q', chords[0]), note(f, ['c/4', 'e/4', 'b/4'], 'q', chords[1]).addModifier(new Accidental('b'), 2), note(f, ['c/4', 'e/4', 'g/4'], 'q', chords[2]), note(f, ['c/4', 'f/4', 'a/4'], 'q', chords[3]).addModifier(new Accidental('#'), 1), ]; Formatter.FormatAndDraw(ctx, stave, notes); } const chords = [ f.ChordSymbol({ vJustify: 'bottom' }).addText('I').addTextSuperscript('6').addTextSubscript('4'), f.ChordSymbol({ vJustify: 'bottom' }).addGlyphOrText('V'), f.ChordSymbol({ vJustify: 'bottom' }).addLine(), f.ChordSymbol({ vJustify: 'bottom' }).addGlyphOrText('V/V'), ]; draw(chords, 10); options.assert.ok(true, 'Bottom Chord Symbol'); } function bottomStemDown(options) { const f = VexFlowTests.makeFactory(options, 600 * 1.5, 330); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chords, y) { const note = (keys, duration, chordSymbol) => new StaveNote({ keys, duration, stemDirection: -1 }).addModifier(chordSymbol, 0); const stave = new Stave(10, y, 400).addClef('treble').setContext(ctx).drawWithStyle(); const notes = [ note(['c/4', 'f/4', 'a/4'], 'q', chords[0]), note(['c/4', 'e/4', 'b/4'], 'q', chords[1]).addModifier(new Accidental('b'), 2), note(['c/4', 'e/4', 'g/4'], 'q', chords[2]), note(['c/4', 'f/4', 'a/4'], 'q', chords[3]).addModifier(new Accidental('#'), 1), ]; Formatter.FormatAndDraw(ctx, stave, notes); } const chords = [ f.ChordSymbol({ vJustify: 'bottom' }).addGlyphOrText('F'), f.ChordSymbol({ vJustify: 'bottom' }).addGlyphOrText('C7'), f.ChordSymbol({ vJustify: 'bottom' }).addLine(), f.ChordSymbol({ vJustify: 'bottom' }).addText('A').addGlyphSuperscript('dim'), ]; draw(chords, 10); options.assert.ok(true, 'Bottom Stem Down Chord Symbol'); } function doubleBottom(options) { const f = VexFlowTests.makeFactory(options, 600 * 1.5, 260); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chords, chords2, y) { const note = (keys, duration, chordSymbol1, chordSymbol2) => new StaveNote({ keys, duration }).addModifier(chordSymbol1, 0).addModifier(chordSymbol2, 0); const stave = f.Stave({ x: 10, y, width: 450 }).addClef('treble').setContext(ctx).drawWithStyle(); const notes = [ note(['c/4', 'f/4', 'a/4'], 'q', chords[0], chords2[0]), note(['c/4', 'e/4', 'b/4'], 'q', chords[1], chords2[1]).addModifier(f.Accidental({ type: 'b' }), 2), note(['c/4', 'e/4', 'g/4'], 'q', chords[2], chords2[2]), note(['c/4', 'f/4', 'a/4'], 'q', chords[3], chords2[3]).addModifier(f.Accidental({ type: '#' }), 1), ]; Formatter.FormatAndDraw(ctx, stave, notes); } const chords1 = [ f.ChordSymbol({ vJustify: 'bottom' }).addText('I').addTextSuperscript('6').addTextSubscript('4'), f.ChordSymbol({ vJustify: 'bottom' }).addGlyphOrText('V'), f.ChordSymbol({ vJustify: 'bottom' }).addLine(), f.ChordSymbol({ vJustify: 'bottom' }).addGlyphOrText('V/V'), ]; const chords2 = [ f.ChordSymbol({ vJustify: 'bottom' }).addText('T'), f.ChordSymbol({ vJustify: 'bottom' }).addText('D'), f.ChordSymbol({ vJustify: 'bottom' }).addText('D'), f.ChordSymbol({ vJustify: 'bottom' }).addText('SD'), ]; draw(chords1, chords2, 10); options.assert.ok(true, '2 Bottom Chord Symbol'); } function wide(options) { const f = VexFlowTests.makeFactory(options, 300 * 1.5, 680); const ctx = f.getContext(); ctx.scale(1.5, 1.5); function draw(chord1, chord2, y) { const stave = new Stave(10, y, 250).addClef('treble').setContext(ctx).drawWithStyle(); const notes = [ note(f, ['e/4', 'a/4', 'd/5'], 'h', chord1).addModifier(new Accidental('b'), 0), note(f, ['c/4', 'e/4', 'B/4'], 'h', chord2), ]; Formatter.FormatAndDraw(ctx, stave, notes); } let chord1 = f.ChordSymbol().addText('FrogToad7').setHorizontal('left').addGlyphOrText('(#11b9)', superscript); let chord2 = f.ChordSymbol({ hJustify: 'left' }).addText('CToShiningC').addGlyphSuperscript('majorSeventh'); draw(chord1, chord2, 40); chord1 = f .ChordSymbol({ hJustify: 'right' }) .addText('FrogToad7') .setHorizontal('left') .addGlyphOrText('#11', superscript) .addGlyphOrText('b9', subscript); chord2 = f.ChordSymbol({ hJustify: 'left' }).addText('CToShiningC').addTextSuperscript('Maj.'); draw(chord1, chord2, 240); options.assert.ok(true, 'Wide Chord Symbols'); } VexFlowTests.register(ChordSymbolTests); export { ChordSymbolTests };