UNPKG

music21j-port

Version:

A toolkit for computer-aided musicology, Javascript version

404 lines (383 loc) 10.8 kB
/** * music21j -- Javascript reimplementation of Core music21p features. * music21/clef -- Clef objects * * Copyright (c) 2013-19, Michael Scott Cuthbert and cuthbertLab * Based on music21 (=music21p), Copyright (c) 2006–19, Michael Scott Cuthbert and cuthbertLab * */ import { base } from './base.js'; import { pitch } from './pitch.js'; /** * Clef module, see {@link music21.clef} for namespace * * @exports music21/clef */ /** * Clef related objects and properties * * @namespace music21.clef * @memberof music21 * @requires music21/base * @requires music21/pitch */ export const clef = {}; /* music21.Clef must be defined before Stream since Stream subclasses call new music21.Clef... */ // TODO: Fix to newest Vexflow format... clef.lowestLines = { treble: 31, soprano: 29, 'mezzo-soprano': 27, alto: 25, tenor: 23, bass: 19, percussion: 31, }; clef.nameToLine = { treble: 2, soprano: 1, 'mezzo-soprano': 2, alto: 3, tenor: 4, bass: 4, percussion: 3, }; clef.nameToSign = { treble: 'G', soprano: 'C', 'mezzo-soprano': 'C', alto: 'C', tenor: 'C', bass: 'F', percussion: 'percussion', }; /** * Clefname can be one of * "treble", "bass", "soprano", "mezzo-soprano", "alto", "tenor", "percussion" * * @class Clef * @memberof music21.clef * @extends music21.base.Music21Object * @param {string} name - clef name * @param {int} [octaveChange=0] - ottava * @property {string} [name] * @property {int} lowestLine - diatonicNoteNum (C4 = 29) for the lowest line (in a five-line staff) * @property {int} lowestLineTrebleOffset - difference between the first line of this staff and the first line in treble clef * @property {int} octaveChange */ export class Clef extends base.Music21Object { constructor(name, octaveChange) { super(); this.classSortOrder = 0; this.sign = undefined; this.line = 1; if (name !== undefined) { name = name.toLowerCase(); this.name = name; this.lowestLine = clef.lowestLines[name]; this.sign = clef.nameToSign[name]; this.line = clef.nameToLine[name] || 1; this.lowestLineTrebleOffset = clef.lowestLines.treble - this.lowestLine; } else { this.name = undefined; this.lowestLine = clef.lowestLines.treble; this.lowestLineTrebleOffset = 0; } if (octaveChange === undefined) { this.octaveChange = 0; } else { this.octaveChange = octaveChange; this.lowestLine += 7 * octaveChange; this.lowestLineTrebleOffset -= 7 * octaveChange; } } stringInfo() { return ''; } /** * returns a new pitch object if the clef name is not Treble * designed so it would look the same as it would in treble clef. * for instance, bass-clef 2nd-space C# becomes treble clef 2nd-space A# * used for Vex.Flow which requires all pitches to be input as if they * are in treble clef. * * @param {music21.pitch.Pitch} p * @returns {music21.pitch.Pitch} new pitch */ convertPitchToTreble(p) { if (this.lowestLine === undefined) { console.log('no first line defined for clef', this.name, this); return p; // error } const lowestLineDifference = this.lowestLineTrebleOffset; const tempPitch = new pitch.Pitch(p.step); tempPitch.octave = p.octave; tempPitch.diatonicNoteNum += lowestLineDifference; tempPitch.accidental = p.accidental; return tempPitch; } } clef.Clef = Clef; /** * A TrebleClef (same as new music21.clef.Clef('treble')) * * @class TrebleClef * @memberof music21.clef * @extends music21.clef.Clef */ export class TrebleClef extends Clef { constructor() { super('treble'); this.sign = 'G'; this.line = 2; } } clef.TrebleClef = TrebleClef; /** * A TrebleClef down an octave (same as new music21.clef.Clef('treble', -1)) * * Unlike music21p, currently not a subclass of TrebleClef. * * @class Treble8vbClef * @memberof music21.clef * @extends music21.clef.Clef */ export class Treble8vbClef extends Clef { constructor() { super('treble', -1); } } clef.Treble8vbClef = Treble8vbClef; /** * A TrebleClef up an octave (same as new music21.clef.Clef('treble', 1)) * * @class Treble8vaClef * @memberof music21.clef * @extends music21.clef.Clef */ export class Treble8vaClef extends Clef { constructor() { super('treble', 1); } } clef.Treble8vaClef = Treble8vaClef; /** * A BassClef (same as new music21.clef.Clef('bass')) * * @class BassClef * @memberof music21.clef * @extends music21.clef.Clef */ export class BassClef extends Clef { constructor() { super('bass'); this.sign = 'F'; this.line = 4; } } clef.BassClef = BassClef; /** * A BassClef down an octave (same as new music21.clef.Clef('bass', -1)) * * @class Bass8vbClef * @memberof music21.clef * @extends music21.clef.Clef */ export class Bass8vbClef extends Clef { constructor() { super('bass', -1); this.sign = 'F'; this.line = 4; } } clef.Bass8vbClef = Bass8vbClef; /** * An AltoClef (same as new music21.clef.Clef('alto')) * * @class AltoClef * @memberof music21.clef * @extends music21.clef.Clef */ export class AltoClef extends Clef { constructor() { super('alto'); this.sign = 'C'; this.line = 3; } } clef.AltoClef = AltoClef; /** * A Tenor Clef (same as new music21.clef.Clef('tenor')) * * @class TenorClef * @memberof music21.clef * @extends music21.clef.Clef */ export class TenorClef extends Clef { constructor() { super('tenor'); this.sign = 'C'; this.line = 4; } } clef.TenorClef = TenorClef; /** * A Soprano Clef (same as new music21.clef.Clef('soprano')) * * @class SopranoClef * @memberof music21.clef * @extends music21.clef.Clef */ export class SopranoClef extends Clef { constructor() { super('soprano'); this.sign = 'C'; this.line = 1; } } clef.SopranoClef = SopranoClef; /** * A Mezzo-Soprano Clef (same as new music21.clef.Clef('mezzo-soprano')) * * @class MezzoSopranoClef * @memberof music21.clef * @extends music21.clef.Clef */ export class MezzoSopranoClef extends Clef { constructor() { super('mezzo-soprano'); this.sign = 'C'; this.line = 2; } } clef.MezzoSopranoClef = MezzoSopranoClef; /** * A Percussion Clef (same as new music21.clef.Clef('percussion')) * * First line is treated as if it's treble clef. Not available as "bestClef" * * @class PercussionClef * @memberof music21.clef * @extends music21.clef.Clef */ export class PercussionClef extends Clef { constructor() { super('percussion'); this.sign = 'percussion'; this.line = 3; } } clef.PercussionClef = PercussionClef; /** * Looks at the pitches in a Stream and returns the best clef * of Treble and Bass * * @function music21.clef.bestClef * @memberof music21.clef * @param {music21.stream.Stream} st * @param {Object} [options] * @param {boolean} [options.recurse=true] * @returns {music21.clef.Clef} */ clef.bestClef = function bestClef(st, { recurse=true }={}) { // console.log('calling flat on stream: ', st.elements.length, st.classes[st.classes.length - 1]); let stFlat; if (recurse) { stFlat = st.flat; } else { stFlat = st; } let totalNotes = 0; let totalPitch = 0.0; for (let i = 0; i < stFlat.length; i++) { const el = stFlat.get(i); if (el.pitch !== undefined) { totalNotes += 1; totalPitch += el.pitch.diatonicNoteNum; } else if (el.pitches !== undefined) { for (let j = 0; j < el.pitches.length; j++) { totalNotes += 1; totalPitch += el.pitches[j].diatonicNoteNum; } } } let averageHeight; if (totalNotes === 0) { averageHeight = 29; } else { averageHeight = totalPitch / totalNotes; } // console.log('bestClef: average height', averageHeight); if (averageHeight > 28) { // 29 = c4 return new clef.TrebleClef(); } else { return new clef.BassClef(); } }; clef.clefFromString = function clefFromString(clefString, octaveShift) { if (octaveShift === undefined) { octaveShift = 0; } const xnStr = clefString.trim(); let thisType; let lineNum; if (xnStr.toLowerCase() === 'percussion') { return new clef.PercussionClef(clefString, octaveShift); } // todo: tab, none, jianpu if (xnStr.length === 2) { thisType = xnStr[0].toUpperCase(); lineNum = parseInt(xnStr[1]); } else if (xnStr.length === 1) { thisType = xnStr[0].toUpperCase(); if (thisType === 'G') { lineNum = 2; } else if (thisType === 'F') { lineNum = 4; } else if (thisType === 'C') { lineNum = 3; } else { lineNum = 0; } } else if (xnStr.length > 2) { // try to get any clef in the module const searchLower = xnStr.toLowerCase(); for (const clefKey of Object.keys(clef)) { // TODO(msc): fix when we get rid of clef = {} if (!clefKey.includes('Clef')) { continue; } const clefLower = clefKey.toLowerCase(); const potentialClass = clef[clefKey]; if (typeof potentialClass !== 'function') { continue; } if (clefLower !== searchLower && clefLower !== searchLower + 'clef') { continue; } return new potentialClass(); } } const arrayEqual = (a, b) => a.length === b.length && a.every((el, ix) => el === b[ix]); const params = [thisType, lineNum, octaveShift]; if (arrayEqual(params, ['G', 2, 0])) { return new clef.TrebleClef(); } else if (arrayEqual(params, ['G', 2, -1])) { return new clef.Treble8vbClef(); } else if (arrayEqual(params, ['G', 2, 1])) { return new clef.Treble8vaClef(); } else if (arrayEqual(params, ['F', 4, 0])) { return new clef.BassClef(); } else if (arrayEqual(params, ['F', 4, -1])) { return new clef.Bass8vbClef(); } else if (arrayEqual(params, ['C', 3, 0])) { return new clef.AltoClef(); } else if (arrayEqual(params, ['C', 4, 0])) { return new clef.TenorClef(); } else { return new clef.Clef(xnStr, octaveShift); } };