vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
218 lines (193 loc) • 5.72 kB
text/typescript
// [VexFlow](https://vexflow.com) - Copyright (c) Mohit Muthanna Cheppudira 2013.
// Co-author: Benjamin W. Bohl
// MIT License
import { Glyph } from './glyph';
import { Stave } from './stave';
import { StaveModifier, StaveModifierPosition } from './stavemodifier';
import { Tables } from './tables';
import { Category } from './typeguard';
import { defined, log } from './util';
export interface ClefType {
point: number;
code: string;
line?: number;
}
// eslint-disable-next-line
function L(...args: any[]) {
if (Clef.DEBUG) log('Vex.Flow.Clef', args);
}
/**
* Clef implements various types of clefs that can be rendered on a stave.
*
* See `tests/clef_tests.ts` for usage examples.
*/
export class Clef extends StaveModifier {
/** To enable logging for this class, set `Vex.Flow.Clef.DEBUG` to `true`. */
static DEBUG: boolean = false;
static get CATEGORY(): string {
return Category.Clef;
}
annotation?: {
code: string;
line: number;
x_shift: number;
point: number;
};
/**
* The attribute `clef` must be a key from
* `Clef.types`
*/
clef: ClefType = Clef.types['treble'];
protected glyph?: Glyph;
protected attachment?: Glyph;
protected size?: string;
protected type?: string;
/**
* Every clef name is associated with a glyph code from the font file
* and a default stave line number.
*/
static get types(): Record<string, ClefType> {
return {
treble: {
code: 'gClef',
line: 3,
point: 0,
},
bass: {
code: 'fClef',
line: 1,
point: 0,
},
alto: {
code: 'cClef',
line: 2,
point: 0,
},
tenor: {
code: 'cClef',
line: 1,
point: 0,
},
percussion: {
code: 'restMaxima',
line: 2,
point: 0,
},
soprano: {
code: 'cClef',
line: 4,
point: 0,
},
'mezzo-soprano': {
code: 'cClef',
line: 3,
point: 0,
},
'baritone-c': {
code: 'cClef',
line: 0,
point: 0,
},
'baritone-f': {
code: 'fClef',
line: 2,
point: 0,
},
subbass: {
code: 'fClef',
line: 0,
point: 0,
},
french: {
code: 'gClef',
line: 4,
point: 0,
},
tab: {
code: '6stringTabClef',
point: 0,
},
};
}
/** Create a new clef. */
constructor(type: string, size?: string, annotation?: string) {
super();
this.setPosition(StaveModifierPosition.BEGIN);
this.setType(type, size, annotation);
this.setWidth(Tables.currentMusicFont().lookupMetric(`clef.${this.size}.width`));
L('Creating clef:', type);
}
/** Set clef type, size and annotation. */
setType(type: string, size?: string, annotation?: string): this {
this.type = type;
this.clef = Clef.types[type];
if (size === undefined) {
this.size = 'default';
} else {
this.size = size;
}
const musicFont = Tables.currentMusicFont();
this.clef.point = musicFont.lookupMetric(`clef.${this.size}.point`, 0);
this.glyph = new Glyph(this.clef.code, this.clef.point, {
category: `clef.${this.clef.code}.${this.size}`,
});
// If an annotation, such as 8va, is specified, add it to the Clef object.
if (annotation !== undefined) {
const code = musicFont.lookupMetric(`clef.annotations.${annotation}.smuflCode`);
const point = musicFont.lookupMetric(`clef.annotations.${annotation}.${this.size}.point`);
const line = musicFont.lookupMetric(`clef.annotations.${annotation}.${this.size}.${this.type}.line`);
const x_shift = musicFont.lookupMetric(`clef.annotations.${annotation}.${this.size}.${this.type}.shiftX`);
this.annotation = { code, point, line, x_shift };
this.attachment = new Glyph(this.annotation.code, this.annotation.point);
this.attachment.metrics.x_max = 0;
this.attachment.setXShift(this.annotation.x_shift);
} else {
this.annotation = undefined;
}
return this;
}
/** Get clef width. */
getWidth(): number {
if (this.type === 'tab') {
defined(this.stave, 'ClefError', "Can't get width without stave.");
}
return this.width;
}
/** Set associated stave. */
setStave(stave: Stave): this {
this.stave = stave;
if (this.type === 'tab') {
const glyph = defined(this.glyph, 'ClefError', "Can't set stave without glyph.");
const numLines = this.stave.getNumLines();
const musicFont = Tables.currentMusicFont();
const point = musicFont.lookupMetric(`clef.lineCount.${numLines}.point`);
const shiftY = musicFont.lookupMetric(`clef.lineCount.${numLines}.shiftY`);
glyph.setPoint(point);
glyph.setYShift(shiftY);
}
return this;
}
/** Render clef. */
draw(): void {
const glyph = defined(this.glyph, 'ClefError', "Can't draw clef without glyph.");
const stave = this.checkStave();
const ctx = stave.checkContext();
this.setRendered();
this.applyStyle(ctx);
ctx.openGroup('clef', this.getAttribute('id'));
glyph.setStave(stave);
glyph.setContext(ctx);
if (this.clef.line !== undefined) {
this.placeGlyphOnLine(glyph, stave, this.clef.line);
}
glyph.renderToStave(this.x);
if (this.annotation !== undefined && this.attachment !== undefined) {
this.placeGlyphOnLine(this.attachment, stave, this.annotation.line);
this.attachment.setStave(stave);
this.attachment.setContext(ctx);
this.attachment.renderToStave(this.x);
}
ctx.closeGroup();
this.restoreStyle(ctx);
}
}