Source: music21/clef.js

/**
 * 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
 *
 * Clef module, see {@link music21.clef} for namespace
 * Clef related objects and properties
 *
 * @exports music21/clef
 * @namespace music21.clef
 * @memberof music21
 * @requires music21/base
 * @requires music21/pitch
 */
import * as base from './base';
import * as pitch from './pitch.js';

/*  music21.Clef
    must be defined before Stream since Stream subclasses call new music21.Clef...
 */

// TODO: Fix to newest Vexflow format...

/**
 *
 * @type {{bass: number, soprano: number, tenor: number, percussion: number, 'mezzo-soprano': number, alto: number, treble: number}}
 */
export const lowestLines = {
    treble: 31,
    soprano: 29,
    'mezzo-soprano': 27,
    alto: 25,
    tenor: 23,
    bass: 19,
    percussion: 31,
};

/**
 *
 * @type {{bass: number, soprano: number, tenor: number, percussion: number, 'mezzo-soprano': number, alto: number, treble: number}}
 */
export const nameToLine = {
    treble: 2,
    soprano: 1,
    'mezzo-soprano': 2,
    alto: 3,
    tenor: 4,
    bass: 4,
    percussion: 3,
};

/**
 *
 * @type {{bass: string, soprano: string, tenor: string, percussion: string, 'mezzo-soprano': string, alto: string, treble: string}}
 */
export const nameToSign = {
    treble: 'G',
    soprano: 'C',
    'mezzo-soprano': 'C',
    alto: 'C',
    tenor: 'C',
    bass: 'F',
    percussion: 'percussion',
};

/**
 * Clef name 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 = lowestLines[name];
            this.sign = nameToSign[name];
            this.line = nameToLine[name] || 1;
            this.lowestLineTrebleOffset
                = lowestLines.treble - this.lowestLine;
        } else {
            this.name = undefined;
            this.lowestLine = 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;
    }
}

/**
 * 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;
    }
}

/**
 * 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);
    }
}

/**
 * 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);
    }
}

/**
 * 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;
    }
}

/**
 * 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;
    }
}

/**
 * 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;
    }
}

/**
 * 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;
    }
}

/**
 * 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;
    }
}

/**
 * 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;
    }
}

/**
 * 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;
    }
}

export const all_clefs = {
    TrebleClef,
    Treble8vbClef,
    Treble8vaClef,
    BassClef,
    Bass8vbClef,
    AltoClef,
    TenorClef,
    SopranoClef,
    MezzoSopranoClef,
    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}
 */
export 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 TrebleClef();
    } else {
        return new BassClef();
    }
}

/**
 *
 * @param {string} clefString
 * @param {number} [octaveShift=0]
 * @returns {music21.clef.Clef}
 */
export function clefFromString(clefString, octaveShift=0) {
    const xnStr = clefString.trim();
    let thisType;
    let lineNum;
    if (xnStr.toLowerCase() === 'percussion') {
        return new 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(all_clefs)) {
            const clefLower = clefKey.toLowerCase();
            const potentialClass = all_clefs[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 TrebleClef();
    } else if (arrayEqual(params, ['G', 2, -1])) {
        return new Treble8vbClef();
    } else if (arrayEqual(params, ['G', 2, 1])) {
        return new Treble8vaClef();
    } else if (arrayEqual(params, ['F', 4, 0])) {
        return new BassClef();
    } else if (arrayEqual(params, ['F', 4, -1])) {
        return new Bass8vbClef();
    } else if (arrayEqual(params, ['C', 3, 0])) {
        return new AltoClef();
    } else if (arrayEqual(params, ['C', 4, 0])) {
        return new TenorClef();
    } else {
        return new Clef(xnStr, octaveShift);
    }
}
Music21j, Copyright © 2013-2021 Michael Scott Asato Cuthbert.
Documentation generated by JSDoc 3.6.3 on Wed Jul 31st 2019 using the DocStrap template.