Source: music21/interval.js

/**
 * music21j -- Javascript reimplementation of Core music21p features.
 * music21/interval -- Interval routines
 *
 * Copyright (c) 2013-16, Michael Scott Cuthbert and cuthbertLab
 * Based on music21 (=music21p), Copyright (c) 2006-18, Michael Scott Cuthbert and cuthbertLab
 *
 */
import { common } from './common.js';
import { debug } from './debug.js';
import { prebase } from './prebase.js';
import { pitch } from './pitch.js';

/**
 * interval module. See {@link music21.interval} for namespace
 *
 * @exports music21/interval
 */
/**
 * Interval related objects
 *
 * @namespace music21.interval
 * @memberof music21
 * @requires music21/prebase
 * @requires music21/pitch
 */
export const interval = {};

/**
 * Interval Directions as an Object/map
 *
 * @memberof music21.interval
 * @example
 * if (music21.interval.Direction.OBLIQUE >
 *     music21.interval.Direction.ASCENDING ) {
 *    console.log(music21.interval.Direction.DESCENDING);
 * }
 *
 */
interval.Direction = {
    DESCENDING: -1,
    OBLIQUE: 0,
    ASCENDING: 1,
};

/**
 * N.B. a dict in music21p -- the indexes here let Direction call them + 1
 *
 * @memberof music21.interval
 * @example
 * console.log(music21.interval.IntervalDirectionTerms[music21l.interval.Direction.OBLIQUE + 1])
 * // "Oblique"
 */
interval.IntervalDirectionTerms = ['Descending', 'Oblique', 'Ascending'];

/**
 * ordinals for music terms...
 *
 * @memberof music21.interval
 * @example
 * for (var i = 1; // N.B. 0 = undefined
 *      i < music21.interval.MusicOrdinals.length;
 *      i++) {
 *     console.log(i, music21.interval.MusicOrdinals[i]);
 * }
 * // 1, Unison
 * // 2, Second
 * // 3, Third
 * // ...
 * // 8, Octave
 * // ...
 * // 15, Double Octave
 */
interval.MusicOrdinals = [
    undefined,
    'Unison',
    'Second',
    'Third',
    'Fourth',
    'Fifth',
    'Sixth',
    'Seventh',
    'Octave',
    'Ninth',
    'Tenth',
    'Eleventh',
    'Twelfth',
    'Thirteenth',
    'Fourteenth',
    'Double Octave',
];

/**
 * Represents an interval such as unison, second, etc.
 *
 * Properties are demonstrated below.
 *
 * @class GenericInterval
 * @memberof music21.interval
 * @param {Int} [gi=1] - generic interval (1 or higher, or -2 or lower)
 * @example
 * var gi = new music21.interval.GenericInterval(-14)
 * gi.value
 * // -14
 * gi.directed
 * // -14
 * gi.undirected
 * // 14
 * gi.direction == music21.interval.Direction.DESCENDING
 * // true
 * gi.isSkip
 * // true
 * gi.isStep
 * // false
 * gi.isDiatonicStep
 * // false  // augmented unisons are not diatonicSteps but can't tell yet..
 * gi.isUnison
 * // false
 * gi.simpledDirected
 * // -7
 * gi.simpleUndirected
 * // 7
 * gi.undirectedOctaves
 * // 1
 * gi.semiSimpleUndirected
 * // 7  -- semiSimple distinguishes between 8 and 1; that's all
 * gi.semiSimpleDirected
 * // 7  -- semiSimple distinguishes between 8 and 1; that's all
 * gi.perfectable
 * // false
 * gi.niceName
 * // "Fourteenth"
 * gi.directedNiceName
 * // "Descending Fourteenth"
 * gi.simpleNiceName
 * // "Seventh"
 * gi.staffDistance
 * // -13
 * gi.mod7inversion
 * // 2  // sevenths invert to seconds
 *
 */
export class GenericInterval extends prebase.ProtoM21Object {
    constructor(gi) {
        super();
        if (gi === undefined) {
            gi = 1;
        }
        this.value = gi; // todo: convertGeneric() from python
        this.directed = this.value;
        this.undirected = Math.abs(this.value);

        if (this.directed === 1) {
            this.direction = interval.Direction.OBLIQUE;
        } else if (this.directed < 0) {
            this.direction = interval.Direction.DESCENDING;
        } else if (this.directed > 1) {
            this.direction = interval.Direction.ASCENDING;
        }
        // else (raise exception)

        if (this.undirected > 2) {
            this.isSkip = true;
        } else {
            this.isSkip = false;
        }

        if (this.undirected === 2) {
            this.isDiatonicStep = true;
        } else {
            this.isDiatonicStep = false;
        }

        this.isStep = this.isDiatonicStep;

        if (this.undirected === 1) {
            this.isUnison = true;
        } else {
            this.isUnison = false;
        }

        let tempSteps = common.posMod(this.undirected, 7);
        let tempOctaves = parseInt(this.undirected / 7);
        if (tempSteps === 0) {
            tempOctaves -= 1;
            tempSteps = 7;
        }
        this.simpleUndirected = tempSteps;
        this.undirectedOctaves = tempOctaves;
        if (tempSteps === 1 && tempOctaves >= 1) {
            this.semiSimpleUndirected = 8;
        } else {
            this.semiSimpleUndirected = this.simpleUndirected;
        }

        if (this.direction === interval.Direction.DESCENDING) {
            this.octaves = -1 * tempOctaves;
            if (tempSteps !== 1) {
                this.simpleDirected = -1 * tempSteps;
            } else {
                this.simpleDirected = 1; // no descending unisons...
            }
            this.semiSimpleDirected = -1 * this.semiSimpleUndirected;
        } else {
            this.octaves = tempOctaves;
            this.simpleDirected = tempSteps;
            this.semiSimpleDirected = this.semiSimpleUndirected;
        }
        if (
            this.simpleUndirected === 1
            || this.simpleUndirected === 4
            || this.simpleUndirected === 5
        ) {
            this.perfectable = true;
        } else {
            this.perfectable = false;
        }

        if (this.undirected < interval.MusicOrdinals.length) {
            this.niceName = interval.MusicOrdinals[this.undirected];
        } else {
            this.niceName = this.undirected.toString();
        }

        this.simpleNiceName = interval.MusicOrdinals[this.simpleUndirected];
        this.semiSimpleNiceName
            = interval.MusicOrdinals[this.semiSimpleUndirected];

        if (Math.abs(this.directed) === 1) {
            this.staffDistance = 0;
        } else if (this.directed > 1) {
            this.staffDistance = this.directed - 1;
        } else if (this.directed < -1) {
            this.staffDistance = this.directed + 1;
        }
        // else: raise IntervalException("Non-integer, -1, or 0 not permitted as a diatonic interval")

        // 2 -> 7; 3 -> 6; 8 -> 1 etc.
        this.mod7inversion = 9 - this.semiSimpleUndirected;

        if (this.direction === interval.Direction.DESCENDING) {
            this.mod7 = this.mod7inversion; // see chord.semitonesFromChordStep for usage...
        } else {
            this.mod7 = this.simpleDirected;
        }
    }

    /**
     * Returns a new GenericInterval which is the mod7inversion; 3rds (and 10ths etc.) to 6ths, etc.
     *
     * @memberof music21.interval.GenericInterval
     * @returns {music21.interval.GenericInterval}
     */
    complement() {
        return new interval.GenericInterval(this.mod7inversion);
    }

    /**
     * Returns a new GenericInterval which has the opposite direction (descending becomes ascending, etc.)
     *
     * @memberof music21.interval.GenericInterval
     * @returns {music21.interval.GenericInterval}
     */
    reverse() {
        if (this.undirected === 1) {
            return new interval.GenericInterval(1);
        } else {
            return new interval.GenericInterval(
                this.undirected * (-1 * this.direction)
            );
        }
    }
    /**
     * Given a specifier, return a new DiatonicInterval with this generic.
     *
     * @memberof music21.interval.GenericInterval
     * @param {string|Int} specifier - a specifier such as "P","m","M","A","dd" etc.
     * @returns {music21.interval.DiatonicInterval}
     */
    getDiatonic(specifier) {
        return new interval.DiatonicInterval(specifier, this);
    }

    /**
     * Transpose a pitch by this generic interval, maintaining accidentals
     *
     * @memberof music21.interval.GenericInterval
     * @param {music21.pitch.Pitch} p
     * @returns {music21.pitch.Pitch}
     */
    transposePitch(p) {
        const pitch2 = new pitch.Pitch();
        pitch2.step = p.step;
        pitch2.octave = p.octave;

        const oldDiatonicNum = p.diatonicNoteNum;

        const distanceToMove = this.staffDistance;

        // if not reverse...
        const newDiatonicNumber = oldDiatonicNum + distanceToMove;
        const [newStep, newOctave] = interval.convertDiatonicNumberToStep(
            newDiatonicNumber
        );
        pitch2.step = newStep;
        pitch2.octave = newOctave;
        if (p.accidental !== undefined) {
            pitch2.accidental = new pitch.Accidental(p.accidental.name);
        }
        return pitch2;
    }
}
interval.GenericInterval = GenericInterval;

interval.IntervalSpecifiersEnum = {
    PERFECT: 1,
    MAJOR: 2,
    MINOR: 3,
    AUGMENTED: 4,
    DIMINISHED: 5,
    DBLAUG: 6,
    DBLDIM: 7,
    TRPAUG: 8,
    TRPDIM: 9,
    QUADAUG: 10,
    QUADDIM: 11,
};

interval.IntervalNiceSpecNames = [
    'ERROR',
    'Perfect',
    'Major',
    'Minor',
    'Augmented',
    'Diminished',
    'Doubly-Augmented',
    'Doubly-Diminished',
    'Triply-Augmented',
    'Triply-Diminished',
    'Quadruply-Augmented',
    'Quadruply-Diminished',
];
interval.IntervalPrefixSpecs = [
    undefined,
    'P',
    'M',
    'm',
    'A',
    'd',
    'AA',
    'dd',
    'AAA',
    'ddd',
    'AAAA',
    'dddd',
];

interval.IntervalOrderedPerfSpecs = [
    'dddd',
    'ddd',
    'dd',
    'd',
    'P',
    'A',
    'AA',
    'AAA',
    'AAAA',
];

interval.IntervalPerfSpecifiers = [
    interval.IntervalSpecifiersEnum.QUADDMIN,
    interval.IntervalSpecifiersEnum.TRPDIM,
    interval.IntervalSpecifiersEnum.DBLDIM,
    interval.IntervalSpecifiersEnum.DIMINISHED,
    interval.IntervalSpecifiersEnum.PERFECT,
    interval.IntervalSpecifiersEnum.AUGMENTED,
    interval.IntervalSpecifiersEnum.DBLAUG,
    interval.IntervalSpecifiersEnum.TRPAUG,
    interval.IntervalSpecifiersEnum.QUADAUG,
];
interval.IntervalPerfOffset = 4;

interval.IntervalOrderedImperfSpecs = [
    'dddd',
    'ddd',
    'dd',
    'd',
    'm',
    'M',
    'A',
    'AA',
    'AAA',
    'AAAA',
];

interval.IntervalSpecifiers = [
    interval.IntervalSpecifiersEnum.QUADDMIN,
    interval.IntervalSpecifiersEnum.TRPDIM,
    interval.IntervalSpecifiersEnum.DBLDIM,
    interval.IntervalSpecifiersEnum.DIMINISHED,
    interval.IntervalSpecifiersEnum.MINOR,
    interval.IntervalSpecifiersEnum.MAJOR,
    interval.IntervalSpecifiersEnum.AUGMENTED,
    interval.IntervalSpecifiersEnum.DBLAUG,
    interval.IntervalSpecifiersEnum.TRPAUG,
    interval.IntervalSpecifiersEnum.QUADAUG,
];
interval.IntervalMajOffset = 5;

interval.IntervalSemitonesGeneric = {
    1: 0,
    2: 2,
    3: 4,
    4: 5,
    5: 7,
    6: 9,
    7: 11,
};
interval.IntervalAdjustPerfect = {
    P: 0,
    A: 1,
    AA: 2,
    AAA: 3,
    AAAA: 4,
    d: -1,
    dd: -2,
    ddd: -3,
    dddd: -4,
}; // offset from Perfect

interval.IntervalAdjustImperf = {
    M: 0,
    m: -1,
    A: 1,
    AA: 2,
    AAA: 3,
    AAAA: 4,
    d: -2,
    dd: -3,
    ddd: -4,
    dddd: -5,
}; // offset from major

/**
 * Represents a Diatonic interval.  See example for usage.
 *
 * @class DiatonicInterval
 * @memberof music21.interval
 * @param {string|Int|undefined} [specifier='P'] - a specifier such as "P", "d", "m", "M" etc.
 * @param {music21.interval.GenericInterval|Int} [generic=1] - a `GenericInterval` object or a number to be converted to one
 * @example
 * var di = new music21.interval.DiatonicInterval("M", 10);
 * di.generic.isClassOrSubclass('GenericInterval');
 * // true
 * di.specifierAbbreviation;
 * // 'M'
 * di.name;
 * // 'M10'
 * di.direction == music21.interval.Direction.ASCENDING;
 * // true
 * di.niceName
 * // "Major Tenth"
 *
 * // See music21p for more possibilities.
 */
export class DiatonicInterval extends prebase.ProtoM21Object {
    constructor(specifier, generic) {
        super();

        if (specifier === undefined) {
            specifier = 'P';
        }
        if (generic === undefined) {
            generic = new interval.GenericInterval(1);
        } else if (typeof generic === 'number') {
            generic = new interval.GenericInterval(generic);
        }

        this.name = '';
        if (typeof specifier === 'number') {
            this.specifier = specifier;
        } else {
            this.specifier = interval.IntervalPrefixSpecs.indexOf(specifier); // todo: convertSpecifier();
        }
        this.generic = generic;

        if (
            generic.undirected !== 1
            || specifier === interval.IntervalSpecifiersEnum.PERFECT
        ) {
            this.direction = generic.direction;
        } else if (
            interval.IntervalPerfSpecifiers.indexOf(specifier)
            <= interval.IntervalPerfSpecifiers.indexOf(
                interval.IntervalSpecifiersEnum.DIMINISHED
            )
        ) {
            // diminished unisons -- very controversial
            this.direction = interval.Direction.DESCENDING;
        } else {
            this.direction = interval.Direction.ASCENDING;
        }
        const diatonicDirectionNiceName
            = interval.IntervalDirectionTerms[this.direction + 1];
        this.name
            = interval.IntervalPrefixSpecs[this.specifier]
            + generic.undirected.toString();
        this.niceName
            = interval.IntervalNiceSpecNames[this.specifier]
            + ' '
            + generic.niceName;
        this.simpleName
            = interval.IntervalPrefixSpecs[this.specifier]
            + generic.simpleUndirected.toString();
        this.simpleNiceName
            = interval.IntervalNiceSpecNames[this.specifier]
            + ' '
            + generic.simpleNiceName;
        this.semiSimpleName
            = interval.IntervalPrefixSpecs[this.specifier]
            + generic.semiSimpleUndirected.toString();
        this.semiSimpleNiceName
            = interval.IntervalNiceSpecNames[this.specifier]
            + ' '
            + generic.semiSimpleNiceName;
        this.directedName
            = interval.IntervalPrefixSpecs[this.specifier]
            + generic.directed.toString();
        this.directedNiceName = diatonicDirectionNiceName + ' ' + this.niceName;
        this.directedSimpleName
            = interval.IntervalPrefixSpecs[this.specifier]
            + generic.simpleDirected.toString();
        this.directedSimpleNiceName
            = diatonicDirectionNiceName + ' ' + this.simpleNiceName;
        this.directedSemiSimpleName
            = interval.IntervalPrefixSpecs[this.specifier]
            + generic.semiSimpleDirected.toString();
        this.directedSemiSimpleNiceName
            = diatonicDirectionNiceName + ' ' + this.semiSimpleNameName;
        this.specificName = interval.IntervalNiceSpecNames[this.specifier];
        this.perfectable = generic.perfectable;
        this.isDiatonicStep = generic.isDiatonicStep;
        this.isStep = generic.isStep;

        // generate inversions
        if (this.perfectable) {
            this.orderedSpecifierIndex = interval.IntervalOrderedPerfSpecs.indexOf(
                interval.IntervalPrefixSpecs[this.specifier]
            );
            this.invertedOrderedSpecIndex
                = interval.IntervalOrderedPerfSpecs.length
                - 1
                - this.orderedSpecifierIndex;
            this.invertedOrderedSpecifier
                = interval.IntervalOrderedPerfSpecs[
                    this.invertedOrderedSpecIndex
                ];
        } else {
            this.orderedSpecifierIndex = interval.IntervalOrderedImperfSpecs.indexOf(
                interval.IntervalPrefixSpecs[this.specifier]
            );
            this.invertedOrderedSpecIndex
                = interval.IntervalOrderedImperfSpecs.length
                - 1
                - this.orderedSpecifierIndex;
            this.invertedOrderedSpecifier
                = interval.IntervalOrderedImperfSpecs[
                    this.invertedOrderedSpecIndex
                ];
        }

        this.mod7inversion
            = this.invertedOrderedSpecifier + generic.mod7inversion.toString();
        /* ( if (this.direction == interval.Direction.DESCENDING) {
			this.mod7 = this.mod7inversion;
		} else {
			this.mod7 = this.simpleName;
		} */

        // TODO: reverse()
        // TODO: property cents
    }

    /**
     * Returns a ChromaticInterval object of the same size.
     *
     * @memberof music21.interval.DiatonicInterval
     * @returns {music21.interval.ChromaticInterval}
     */
    getChromatic() {
        const octaveOffset = Math.floor(
            Math.abs(this.generic.staffDistance) / 7
        );
        const semitonesStart
            = interval.IntervalSemitonesGeneric[this.generic.simpleUndirected];
        const specName = interval.IntervalPrefixSpecs[this.specifier];

        let semitonesAdjust = 0;
        if (this.generic.perfectable) {
            semitonesAdjust = interval.IntervalAdjustPerfect[specName];
        } else {
            semitonesAdjust = interval.IntervalAdjustImperf[specName];
        }

        let semitones = octaveOffset * 12 + semitonesStart + semitonesAdjust;

        // direction should be same as original

        if (this.generic.direction === interval.Direction.DESCENDING) {
            semitones *= -1;
        }
        if (debug) {
            console.log(
                'DiatonicInterval.getChromatic -- octaveOffset: ' + octaveOffset
            );
            console.log(
                'DiatonicInterval.getChromatic -- semitonesStart: '
                    + semitonesStart
            );
            console.log(
                'DiatonicInterval.getChromatic -- specName: ' + specName
            );
            console.log(
                'DiatonicInterval.getChromatic -- semitonesAdjust: '
                    + semitonesAdjust
            );
            console.log(
                'DiatonicInterval.getChromatic -- semitones: ' + semitones
            );
        }
        return new interval.ChromaticInterval(semitones);
    }

    transposePitch(p) {
        const fullIntervalObject = new Interval(this, this.getChromatic());
        return fullIntervalObject.transposePitch(p);
    }

    get specifierAbbreviation() {
        return interval.IntervalPrefixSpecs[this.specifier];
    }

    get cents() {
        return this.getChromatic().cents;
    }
}
interval.DiatonicInterval = DiatonicInterval;

/**
 * @class ChromaticInterval
 * @memberof music21.interval
 * @param {number} value - number of semitones (positive or negative)
 * @property {number} cents
 * @property {number} value
 * @property {number} undirected - absolute value of value
 * @property {number} mod12 - reduction to one octave
 * @property {number} intervalClass - reduction to within a tritone (11 = 1, etc.)
 *
 */
export class ChromaticInterval extends prebase.ProtoM21Object {
    constructor(value) {
        super();

        this.semitones = value;
        this.cents = Math.round(value * 100.0, 5);
        this.directed = value;
        this.undirected = Math.abs(value);

        if (this.directed === 0) {
            this.direction = interval.Direction.OBLIQUE;
        } else if (this.directed === this.undirected) {
            this.direction = interval.Direction.ASCENDING;
        } else {
            this.direction = interval.Direction.DESCENDING;
        }

        this.mod12 = common.posMod(this.semitones, 12);
        this.simpleUndirected = common.posMod(this.undirected, 12);
        if (this.direction === interval.Direction.DESCENDING) {
            this.simpleDirected = -1 * this.simpleUndirected;
        } else {
            this.simpleDirected = this.simpleUndirected;
        }

        this.intervalClass = this.mod12;
        if (this.mod12 > 6) {
            this.intervalClass = 12 - this.mod12;
        }

        if (this.undirected === 1) {
            this.isChromaticStep = true;
        } else {
            this.isChromaticStep = false;
        }
    }
    reverse() {
        return new interval.ChromaticInterval(
            this.undirected * (-1 * this.direction)
        );
    }

    //  TODO: this.getDiatonic()

    /**
     * Transposes pitches but does not maintain accidentals, etc.
     *
     * @memberof music21.interval.ChromaticInterval
     * @property {music21.pitch.Pitch} p - pitch to transpose
     * @returns {music21.pitch.Pitch}
     */
    transposePitch(p) {
        let useImplicitOctave = false;
        if (p.octave === undefined) {
            // not yet implemented in m21j
            useImplicitOctave = true;
        }
        const pps = p.ps;
        const newPitch = new pitch.Pitch();
        newPitch.ps = pps + this.semitones;
        if (useImplicitOctave) {
            newPitch.octave = undefined;
        }
        return newPitch;
    }
}
interval.ChromaticInterval = ChromaticInterval;

interval.IntervalStepNames = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];

/**
 * @function music21.interval.convertDiatonicNumberToStep
 * @memberof music21.interval
 * @param {Int} dn - diatonic number, where 29 = C4, C#4 etc.
 * @returns {Array} two element array of {string} stepName and {Int} octave
 */
interval.convertDiatonicNumberToStep = function convertDiatonicNumberToStep(
    dn
) {
    let stepNumber;
    let octave;
    if (dn === 0) {
        return ['B', -1];
    } else if (dn > 0) {
        octave = Math.floor((dn - 1) / 7);
        stepNumber = dn - 1 - octave * 7;
    } else {
        // low notes... test, because js(floor) != py(int);
        octave = Math.floor(dn / 7);
        stepNumber = dn - 1 - (octave + 1) * 7;
    }
    const stepName = interval.IntervalStepNames[stepNumber];
    return [stepName, octave];
};

/**
 * This is the main, powerful Interval class.
 *
 * Instantiate with either a string ("M3") or two {@link music21.pitch.Pitch} or two {@link music21.note.Note}
 *
 * See music21p instructions for usage.
 *
 * @class Interval
 * @memberof music21.interval
 * @example
 * var n1 = new music21.note.Note("C4");
 * var n2 = new music21.note.Note("F#5");
 * var iv = new music21.interval.Interval(n1, n2);
 * iv.isConsonant();
 * // false
 * iv.semitones;
 * // 18
 * iv.niceName
 * // "Augmented Eleventh"
 */
export class Interval extends prebase.ProtoM21Object {
    constructor(...restArgs) {
        super();
        // todo: allow full range of ways of specifying as in m21p
        if (restArgs.length === 1) {
            const arg0 = restArgs[0];
            if (typeof arg0 === 'string') {
                // simple...
                const specifier = arg0.replace(/\d+/, '').replace(/-/, '');
                let generic = parseInt(arg0.replace(/\D+/, ''));
                if (arg0.includes('-')) {
                    generic *= -1;
                }
                const gI = new interval.GenericInterval(generic);
                const dI = new interval.DiatonicInterval(specifier, gI);
                this.diatonic = dI;
                this.chromatic = this.diatonic.getChromatic();
            } else if (arg0.specifier !== undefined) {
                // assume diatonic...
                this.diatonic = arg0;
                this.chromatic = this.diatonic.getChromatic();
            } else {
                console.error('cant parse string arguments to Interval yet');
            }
        } else if (restArgs.length === 2) {
            if (
                restArgs[0].pitch === undefined
                && restArgs[0].diatonicNoteNum === undefined
            ) {
                this.diatonic = restArgs[0];
                this.chromatic = restArgs[1];
            } else {
                const n1 = restArgs[0];
                const n2 = restArgs[1];
                const gInt = interval.notesToGeneric(n1, n2);
                const cInt = interval.notesToChromatic(n1, n2);

                this.diatonic = interval.intervalsToDiatonic(gInt, cInt);
                this.chromatic = cInt;
            }
        }
        this.reinit();
    }
    get complement() {
        return new interval.Interval(this.diatonic.mod7inversion);
    }
    reinit() {
        this.direction = this.chromatic.direction;
        this.specifier = this.diatonic.specifier;
        this.diatonicType = this.diatonic.specifier;
        // this.specificName = this.diatonic.specificName;
        this.generic = this.diatonic.generic;

        this.name = this.diatonic.name;
        this.niceName = this.diatonic.niceName;
        this.simpleName = this.diatonic.simpleName;
        this.simpleNiceName = this.diatonic.simpleNiceName;
        this.semiSimpleName = this.diatonic.semiSimpleName;
        this.semiSimpleNiceName = this.diatonic.semiSimpleNiceName;

        this.directedName = this.diatonic.directedName;
        this.directedNiceName = this.diatonic.directedNiceName;
        this.directedSimpleName = this.diatonic.directedSimpleName;
        this.directedSimpleNiceName = this.diatonic.directedSimpleNiceName;

        // other names...
        this.isDiatonicStep = this.diatonic.isDiatonicStep;

        this.isChromaticStep = this.chromatic.isChromaticStep;
        this.semitones = this.chromatic.semitones;
        this.intervalClass = this.chromatic.intervalClass;
        this.cents = this.chromatic.cents;
        this.isStep = this.isChromaticStep || this.isDiatonicStep;
    }

    /**
     * @memberof music21.interval.Interval
     * @returns {Boolean}
     */
    isConsonant() {
        const sn = this.simpleName;
        const consonantNames = ['P5', 'm3', 'M3', 'm6', 'M6', 'P1'];
        if (consonantNames.indexOf(sn) !== -1) {
            return true;
        } else {
            return false;
        }
    }

    //  todo general: microtones
    /**
     * @memberof music21.interval.Interval
     * @param {music21.pitch.Pitch} p - pitch to transpose
     * @returns {music21.pitch.Pitch}
     */
    transposePitch(p) {
        // todo: reverse, clearAccidentalDisplay, maxAccidental;

        /*
        var useImplicitOctave = false;
        if (p.octave === undefined) {
            useImplicitOctave = true;
        }
         */
        const pitch2 = this.diatonic.generic.transposePitch(p);
        pitch2.accidental = undefined;
        // step and octave are right now, but not necessarily accidental
        const halfStepsToFix
            = this.chromatic.semitones - parseInt(pitch2.ps - p.ps);
        if (halfStepsToFix !== 0) {
            pitch2.accidental = new pitch.Accidental(halfStepsToFix);
        }
        if (debug) {
            console.log('Interval.transposePitch -- new step ' + pitch2.step);
            console.log(
                'Interval.transposePitch -- new octave ' + pitch2.octave
            );
            console.log(
                'Interval.transposePitch -- fixing halfsteps for '
                    + halfStepsToFix
            );
        }
        return pitch2;
    }
}
interval.Interval = Interval;

interval.intervalFromGenericAndChromatic = function intervalFromGenericAndChromatic(
    gInt,
    cInt
) {
    if (typeof gInt === 'number') {
        gInt = new GenericInterval(gInt);
    }
    if (typeof cInt === 'number') {
        cInt = new ChromaticInterval(cInt);
    }
    const specifier = interval._getSpecifierFromGenericChromatic(gInt, cInt);
    const dInt = new DiatonicInterval(specifier, gInt);
    return new Interval(dInt, cInt);
};
/**
 * Convert two notes or pitches to a GenericInterval;
 */
interval.notesToGeneric = function notesToGeneric(n1, n2) {
    let p1 = n1;
    if (n1.pitch !== undefined) {
        p1 = n1.pitch;
    }
    let p2 = n2;
    if (n2.pitch !== undefined) {
        p2 = n2.pitch;
    }
    const staffDist = p2.diatonicNoteNum - p1.diatonicNoteNum;
    const genDist = interval.convertStaffDistanceToInterval(staffDist);
    return new interval.GenericInterval(genDist);
};

interval.convertStaffDistanceToInterval = function convertStaffDistanceToInterval(
    staffDist
) {
    if (staffDist === 0) {
        return 1;
    } else if (staffDist > 0) {
        return staffDist + 1;
    } else {
        return staffDist - 1;
    }
};

interval.notesToChromatic = function notesToChromatic(n1, n2) {
    let p1 = n1;
    if (n1.pitch !== undefined) {
        p1 = n1.pitch;
    }
    let p2 = n2;
    if (n2.pitch !== undefined) {
        p2 = n2.pitch;
    }
    return new interval.ChromaticInterval(p2.ps - p1.ps);
};

interval.intervalsToDiatonic = function intervalsToDiatonic(gInt, cInt) {
    const specifier = interval._getSpecifierFromGenericChromatic(gInt, cInt);
    // todo -- convert specifier...
    return new interval.DiatonicInterval(specifier, gInt);
};

interval._getSpecifierFromGenericChromatic = function _getSpecifierFromGenericChromatic(
    gInt,
    cInt
) {
    const noteVals = [undefined, 0, 2, 4, 5, 7, 9, 11];
    const normalSemis
        = noteVals[gInt.simpleUndirected] + 12 * gInt.undirectedOctaves;
    let theseSemis = 0;
    if (
        gInt.direction !== cInt.direction
        && gInt.direction !== interval.Direction.OBLIQUE
        && cInt.direction !== interval.Direction.OBLIQUE
    ) {
        // intervals like d2 and dd2 etc. (the last test doesn't matter, since -1*0 === 0, but in theory it should be there)
        theseSemis = -1 * cInt.undirected;
    } else if (gInt.undirected === 1) {
        theseSemis = cInt.directed; // matters for unison
    } else {
        // all normal intervals
        theseSemis = cInt.undirected;
    }
    const semisRounded = Math.round(theseSemis);
    let specifier = '';
    if (gInt.perfectable) {
        specifier
            = interval.IntervalPerfSpecifiers[
                interval.IntervalPerfOffset + semisRounded - normalSemis
            ];
    } else {
        specifier
            = interval.IntervalSpecifiers[
                interval.IntervalMajOffset + semisRounded - normalSemis
            ];
    }
    return specifier;
};
Music21j, Copyright © 2013-2018 Michael Scott Cuthbert and cuthbertLab.
Documentation generated by JSDoc 3.5.5 on Wed Jul 4th 2018 using the DocStrap template.