UNPKG

music21j-port

Version:

A toolkit for computer-aided musicology, Javascript version

1,461 lines (1,241 loc) 42.9 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Music21j Source: music21/roman.js</title> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/sunlight.default.css"> <link type="text/css" rel="stylesheet" href="styles/site.spruce.css"> </head> <body> <div class="container-fluid"> <div class="navbar navbar-fixed-top navbar-inverse"> <div class="navbar-inner"> <a class="brand" href="index.html">Music21j</a> <ul class="nav"> <li class="dropdown"> <a href="namespaces.list.html" class="dropdown-toggle" data-toggle="dropdown">Namespaces<b class="caret"></b></a> <ul class="dropdown-menu "> <li> <a href="music21.html">music21</a> </li> <li> <a href="music21.articulations.html">music21.articulations</a> </li> <li> <a href="music21.audioSearch.html">music21.audioSearch</a> </li> <li> <a href="music21.base.html">music21.base</a> </li> <li> <a href="music21.beam.html">music21.beam</a> </li> <li> <a href="music21.chord.html">music21.chord</a> </li> <li> <a href="music21.clef.html">music21.clef</a> </li> <li> <a href="music21.common.html">music21.common</a> </li> <li> <a href="music21.duration.html">music21.duration</a> </li> <li> <a href="music21.dynamics.html">music21.dynamics</a> </li> <li> <a href="music21.expressions.html">music21.expressions</a> </li> <li> <a href="music21.fromPython.html">music21.fromPython</a> </li> <li> <a href="music21.instrument.html">music21.instrument</a> </li> <li> <a href="music21.interval.html">music21.interval</a> </li> <li> <a href="music21.key.html">music21.key</a> </li> <li> <a href="music21.keyboard.html">music21.keyboard</a> </li> <li> <a href="music21.meter.html">music21.meter</a> </li> <li> <a href="music21.miditools.html">music21.miditools</a> </li> <li> <a href="music21.note.html">music21.note</a> </li> <li> <a href="music21.pitch.html">music21.pitch</a> </li> <li> <a href="music21.prebase.html">music21.prebase</a> </li> <li> <a href="music21.renderOptions.html">music21.renderOptions</a> </li> <li> <a href="music21.roman.html">music21.roman</a> </li> <li> <a href="music21.scale.html">music21.scale</a> </li> <li> <a href="music21.sites.html">music21.sites</a> </li> <li> <a href="music21.stream.html">music21.stream</a> </li> <li> <a href="music21.tempo.html">music21.tempo</a> </li> <li> <a href="music21.tie.html">music21.tie</a> </li> <li> <a href="music21.tinyNotation.html">music21.tinyNotation</a> </li> <li> <a href="music21.vfShow.html">music21.vfShow</a> </li> <li> <a href="music21.webmidi.html">music21.webmidi</a> </li> </ul> </li> <li class="dropdown"> <a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b class="caret"></b></a> <ul class="dropdown-menu "> <li> <a href="music21.articulations.Accent.html">music21.articulations.Accent</a> </li> <li> <a href="music21.articulations.Articulation.html">music21.articulations.Articulation</a> </li> <li> <a href="music21.articulations.DynamicArticulation.html">music21.articulations.DynamicArticulation</a> </li> <li> <a href="music21.articulations.LengthArticulation.html">music21.articulations.LengthArticulation</a> </li> <li> <a href="music21.articulations.Marcato.html">music21.articulations.Marcato</a> </li> <li> <a href="music21.articulations.PitchArticulation.html">music21.articulations.PitchArticulation</a> </li> <li> <a href="music21.articulations.Spiccato.html">music21.articulations.Spiccato</a> </li> <li> <a href="music21.articulations.Staccatissimo.html">music21.articulations.Staccatissimo</a> </li> <li> <a href="music21.articulations.Staccato.html">music21.articulations.Staccato</a> </li> <li> <a href="music21.articulations.StrongAccent.html">music21.articulations.StrongAccent</a> </li> <li> <a href="music21.articulations.Tenuto.html">music21.articulations.Tenuto</a> </li> <li> <a href="music21.articulations.TimbreArticulation.html">music21.articulations.TimbreArticulation</a> </li> <li> <a href="music21.base.Music21Object.html">music21.base.Music21Object</a> </li> <li> <a href="music21.beam.Beam.html">music21.beam.Beam</a> </li> <li> <a href="music21.beam.Beams.html">music21.beam.Beams</a> </li> <li> <a href="music21.chord.Chord.html">music21.chord.Chord</a> </li> <li> <a href="music21.clef.AltoClef.html">music21.clef.AltoClef</a> </li> <li> <a href="music21.clef.BassClef.html">music21.clef.BassClef</a> </li> <li> <a href="music21.clef.Clef.html">music21.clef.Clef</a> </li> <li> <a href="music21.clef.MezzoSopranoClef.html">music21.clef.MezzoSopranoClef</a> </li> <li> <a href="music21.clef.PercussionClef.html">music21.clef.PercussionClef</a> </li> <li> <a href="music21.clef.SopranoClef.html">music21.clef.SopranoClef</a> </li> <li> <a href="music21.clef.TenorClef.html">music21.clef.TenorClef</a> </li> <li> <a href="music21.clef.Treble8vaClef.html">music21.clef.Treble8vaClef</a> </li> <li> <a href="music21.clef.Treble8vbClef.html">music21.clef.Treble8vbClef</a> </li> <li> <a href="music21.clef.TrebleClef.html">music21.clef.TrebleClef</a> </li> <li> <a href="music21.duration.Duration.html">music21.duration.Duration</a> </li> <li> <a href="music21.duration.Tuplet.html">music21.duration.Tuplet</a> </li> <li> <a href="music21.dynamics.Dynamic.html">music21.dynamics.Dynamic</a> </li> <li> <a href="music21.expressions.Expression.html">music21.expressions.Expression</a> </li> <li> <a href="music21.expressions.Fermata.html">music21.expressions.Fermata</a> </li> <li> <a href="music21.fromPython.Converter.html">music21.fromPython.Converter</a> </li> <li> <a href="music21.instrument.Instrument.html">music21.instrument.Instrument</a> </li> <li> <a href="music21.interval.ChromaticInterval.html">music21.interval.ChromaticInterval</a> </li> <li> <a href="music21.interval.DiatonicInterval.html">music21.interval.DiatonicInterval</a> </li> <li> <a href="music21.interval.GenericInterval.html">music21.interval.GenericInterval</a> </li> <li> <a href="music21.interval.Interval.html">music21.interval.Interval</a> </li> <li> <a href="music21.key.Key.html">music21.key.Key</a> </li> <li> <a href="music21.key.KeySignature.html">music21.key.KeySignature</a> </li> <li> <a href="music21.keyboard.BlackKey.html">music21.keyboard.BlackKey</a> </li> <li> <a href="music21.keyboard.Key.html">music21.keyboard.Key</a> </li> <li> <a href="music21.keyboard.Keyboard.html">music21.keyboard.Keyboard</a> </li> <li> <a href="music21.keyboard.WhiteKey.html">music21.keyboard.WhiteKey</a> </li> <li> <a href="music21.meter.TimeSignature.html">music21.meter.TimeSignature</a> </li> <li> <a href="music21.miditools.Event.html">music21.miditools.Event</a> </li> <li> <a href="music21.miditools.MidiPlayer.html">music21.miditools.MidiPlayer</a> </li> <li> <a href="music21.note.GeneralNote.html">music21.note.GeneralNote</a> </li> <li> <a href="music21.note.Lyric.html">music21.note.Lyric</a> </li> <li> <a href="music21.note.Note.html">music21.note.Note</a> </li> <li> <a href="music21.note.NotRest.html">music21.note.NotRest</a> </li> <li> <a href="music21.note.Rest.html">music21.note.Rest</a> </li> <li> <a href="music21.pitch.Accidental.html">music21.pitch.Accidental</a> </li> <li> <a href="music21.pitch.Pitch.html">music21.pitch.Pitch</a> </li> <li> <a href="music21.prebase.ProtoM21Object.html">music21.prebase.ProtoM21Object</a> </li> <li> <a href="music21.renderOptions.RenderOptions.html">music21.renderOptions.RenderOptions</a> </li> <li> <a href="music21.roman.RomanNumeral.html">music21.roman.RomanNumeral</a> </li> <li> <a href="music21.stream.Measure.html">music21.stream.Measure</a> </li> <li> <a href="music21.stream.Part.html">music21.stream.Part</a> </li> <li> <a href="music21.stream.Score.html">music21.stream.Score</a> </li> <li> <a href="music21.stream.Stream.html">music21.stream.Stream</a> </li> <li> <a href="music21.stream.Voice.html">music21.stream.Voice</a> </li> <li> <a href="music21.tempo.Metronome.html">music21.tempo.Metronome</a> </li> <li> <a href="music21.tie.Tie.html">music21.tie.Tie</a> </li> <li> <a href="music21.vfShow.Renderer.html">music21.vfShow.Renderer</a> </li> <li> <a href="music21.vfShow.RenderStack.html">music21.vfShow.RenderStack</a> </li> <li> <a href="Notation.html">Notation</a> </li> <li> <a href="Page.html">Page</a> </li> <li> <a href="Recorder.html">Recorder</a> </li> </ul> </li> <li class="dropdown"> <a href="global.html" class="dropdown-toggle" data-toggle="dropdown">Global<b class="caret"></b></a> <ul class="dropdown-menu "> <li> <a href="global.html#getM21attribute">getM21attribute</a> </li> <li> <a href="global.html#layout">layout</a> </li> <li> <a href="global.html#midiNumDiffFromFrequency">midiNumDiffFromFrequency</a> </li> <li> <a href="global.html#recorderWorkerJs">recorderWorkerJs</a> </li> <li> <a href="global.html#unpickler">unpickler</a> </li> <li> <a href="global.html#warnBanner">warnBanner</a> </li> </ul> </li> </ul> </div> </div> <div class="row-fluid"> <div class="span12"> <div id="main"> <h1 class="page-title">Source: music21/roman.js</h1> <section> <article> <pre class="sunlight-highlight-javascript linenums">/** * music21j -- Javascript reimplementation of Core music21p features. * music21/roman -- roman.RomanNumberal -- Chord subclass * * Copyright (c) 2013-17, Michael Scott Cuthbert and cuthbertLab * Based on music21 (=music21p), Copyright (c) 2006–17, Michael Scott Cuthbert and cuthbertLab * */ import { Music21Exception } from './exceptions21.js'; import { chord } from './chord.js'; import { common } from './common.js'; // import { debug } from './debug.js'; import { figuredBass } from './figuredBass.js'; import { harmony } from './harmony.js'; import { interval } from './interval.js'; import { key } from './key.js'; import { pitch } from './pitch.js'; import { scale } from './scale.js'; /** * Roman numeral module. See {@link music21.roman} namespace * * @exports music21/roman */ /** * music21.roman -- namespace for dealing with RomanNumeral analysis. * * @namespace music21.roman * @memberof music21 * @requires music21/chord * @requires music21/common * @requires music21/figuredBass * @requires music21/harmony * @requires music21/key * @requires music21/pitch * @requires music21/interval */ export const roman = {}; roman.figureShorthands = { '53': '', '3': '', '63': '6', '753': '7', '75': '7', // controversial perhaps '73': '7', // controversial perhaps '9753': '9', '975': '9', // controversial perhaps '953': '9', // controversial perhaps '97': '9', // controversial perhaps '95': '9', // controversial perhaps '93': '9', // controversial perhaps '653': '65', '6b53': '6b5', '643': '43', '642': '42', bb7b5b3: 'o7', bb7b53: 'o7', // '6b5bb3': 'o65', b7b5b3: '/o7', }; roman.functionalityScores = { I: 100, i: 90, V7: 80, V: 70, V65: 68, I6: 65, V6: 63, V43: 61, I64: 60, IV: 59, i6: 58, viio7: 57, V42: 55, viio65: 53, viio6: 52, '#viio65': 51, ii: 50, '#viio6': 49, ii65: 48, ii43: 47, ii42: 46, IV6: 45, ii6: 43, VI: 42, '#VI': 41, vi: 40, viio: 39, '#viio': 39, iio: 37, // common in Minor iio42: 36, bII6: 35, // Neapolitan iio43: 32, iio65: 31, '#vio': 28, '#vio6': 28, III: 22, v: 20, VII: 19, VII7: 18, IV65: 17, IV7: 16, iii: 15, iii6: 12, vi6: 10, }; /** * expandShortHand - expand a string of numbers into an array * * N.B. this is NOT where abbreviations get expanded * * @memberof music21.roman * @param {string} shorthand string of a figure w/o roman to parse * @return {Array&lt;string>} array of shorthands */ roman.expandShortHand = function expandShortHand(shorthand) { shorthand = shorthand.replace('/', ''); if (shorthand.match(/[b-]$/)) { shorthand += '3'; } shorthand = shorthand.replace('11', 'x'); shorthand = shorthand.replace('13', 'y'); shorthand = shorthand.replace('15', 'z'); const rx = new RegExp('#*-*b*o*[1-9xyz]', 'g'); let shorthandGroups = []; let match = rx.exec(shorthand); while (match !== null) { shorthandGroups.push(match[0]); match = rx.exec(shorthand); } if (shorthandGroups.length === 1 &amp;&amp; shorthandGroups[0].endsWith('3')) { shorthandGroups = ['5', shorthandGroups[0]]; } const shGroupOut = []; for (let sh of shorthandGroups) { sh = sh.replace('x', '11'); sh = sh.replace('y', '13'); sh = sh.replace('z', '15'); shGroupOut.push(sh); } return shGroupOut; }; /** * correctSuffixForChordQuality - Correct a given inversionString suffix given a * chord of various qualities. * * @memberof music21.roman * @param {music21.chord.Chord} chordObj * @param {string} inversionString a string like '6' to fix. * @return {string} corrected inversionString */ roman.correctSuffixForChordQuality = function correctSuffixForChordQuality( chordObj, inversionString ) { const fifthType = chordObj.semitonesFromChordStep(5); let qualityName = ''; if (fifthType === 6) { qualityName = 'o'; } else if (fifthType === 8) { qualityName = '+'; } if ( inversionString !== undefined &amp;&amp; (inversionString.startsWith('o') || inversionString.startsWith('/o')) ) { if (qualityName === 'o') { // don't call viio7, viioo7. qualityName = ''; } } const seventhType = chordObj.semitonesFromChordStep(7); if (seventhType !== undefined &amp;&amp; fifthType === 6) { // there is a seventh and this is a diminished 5 if (seventhType === 10 &amp;&amp; qualityName === 'o') { qualityName = '/o'; } else if (seventhType !== 9) { // do something for very odd chords built on diminished triad. } } return qualityName + inversionString; }; /** * maps an index number to a roman numeral in lowercase * * @memberof music21.roman * @example * music21.roman.romanToNumber[4] * // 'iv' */ roman.romanToNumber = [undefined, 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii']; /** * Represents a RomanNumeral. By default, capital Roman Numerals are * major chords; lowercase are minor. * * @class RomanNumeral * @memberof music21.roman * @extends music21.chord.Chord * @param {string} figure - the roman numeral as a string, e.g., 'IV', 'viio', 'V7' * @param {string|music21.key.Key} [keyStr='C'] * @property {Array&lt;music21.pitch.Pitch>} scale - (readonly) returns the scale associated with the roman numeral * @property {music21.key.Key} key - the key associated with the RomanNumeral (not allowed to be undefined yet) * @property {string} figure - the figure as passed in * @property {string} degreeName - the name associated with the scale degree, such as "mediant" etc., scale 7 will be "leading tone" or "subtonic" appropriately * @property {int} scaleDegree * @property {string} impliedQuality - "major", "minor", "diminished", "augmented" * @property {Array&lt;music21.pitch.Pitch>} pitches - RomanNumerals are Chord objects, so .pitches will work for them also. */ export class RomanNumeral extends harmony.Harmony { constructor(figure = '', keyStr, keywords) { const params = { updatePitches: false, parseFigure: false }; common.merge(params, keywords); super(figure, params); this._parsingComplete = false; // not yet used... this.primaryFigure = undefined; this.secondaryRomanNumeral = undefined; this.secondaryRomanNumeralKey = undefined; this.pivotChord = undefined; this.scaleCardinality = 7; this._figure = undefined; this.caseMatters = true; if (typeof figure === 'number') { this.caseMatters = false; } this.scaleDegree = undefined; this.frontAlterationString = undefined; this.frontAlterationTransposeInterval = undefined; this.frontAlterationAccidental = undefined; this.romanNumeralAlone = undefined; this.impliedQuality = undefined; this.impliedScale = undefined; this.scaleOffset = undefined; this.useImpliedScale = false; this.bracketedAlterations = []; this.omittedSteps = []; this.followsKeyChange = false; this._functionalityScore = undefined; this._scale = undefined; // the key this.figure = figure; this.key = keyStr; // to remove... this.numbers = ''; if (figure !== '') { this._parseFigure(); this._parsingComplete = true; this._updatePitches(); } } _parseFigure() { let workingFigure = this.figure; let useScale = this.impliedScale; if (!this.useImpliedScale) { useScale = this.key; } [workingFigure, useScale] = this._correctForSecondaryRomanNumeral( useScale ); this.primaryFigure = workingFigure; workingFigure = this._parseOmittedSteps(workingFigure); workingFigure = this._parseBracketedAlterations(workingFigure); workingFigure = workingFigure.replace(/^N6/, 'bII6'); workingFigure = workingFigure.replace(/^N/, 'bII6'); workingFigure = this._parseFrontAlterations(workingFigure); [workingFigure, useScale] = this._parseRNAloneAmidstAug6( workingFigure, useScale ); workingFigure = this._setImpliedQualityFromString(workingFigure); this._tempRoot = useScale.pitchFromDegree(this.scaleDegree); this._fixMinorVIandVII(useScale); const expandedFigure = roman.expandShortHand(workingFigure); this.figuresNotationObj = new figuredBass.Notation( expandedFigure.toString() ); const numbersArr = workingFigure.match(/\d+/); if (numbersArr != null) { workingFigure = workingFigure.replace(/\d+/, ''); this.numbers = parseInt(numbersArr[0]); } } _parseFrontAlterations(workingFigure) { let frontAlterationString = ''; let frontAlterationTransposeInterval; let frontAlterationAccidental; const _alterationRegex = new RegExp('^(b+|-+|#+)'); const match = _alterationRegex.exec(workingFigure); if (match != null) { const group = match[1]; let alteration = group.length; if (group[0] === 'b' || group[0] === '-') { alteration *= -1; } frontAlterationTransposeInterval = interval.intervalFromGenericAndChromatic( 1, alteration ); frontAlterationAccidental = new pitch.Accidental(alteration); frontAlterationString = group; workingFigure = workingFigure.replace(_alterationRegex, ''); } this.frontAlterationString = frontAlterationString; this.frontAlterationTransposeInterval = frontAlterationTransposeInterval; this.frontAlterationAccidental = frontAlterationAccidental; return workingFigure; } _correctBracketedPitches() { for (const innerAlteration of this.bracketedAlterations) { const [alterNotation, chordStep] = innerAlteration; const alterPitch = this.getChordStep(chordStep); if (alterPitch === undefined) { continue; } const newAccidental = new pitch.Accidental(alterNotation); if (alterPitch.accidental === undefined) { alterPitch.accidental = newAccidental; } else { alterPitch.accidental.set( alterPitch.accidental.alter + newAccidental.alter ); } } } _setImpliedQualityFromString(workingFigure) { let impliedQuality = ''; if (workingFigure.startsWith('o')) { impliedQuality = 'diminished'; workingFigure = workingFigure.replace(/^o/, ''); } else if (workingFigure.startsWith('/o')) { impliedQuality = 'half-diminished'; workingFigure = workingFigure.replace(/^\/o/, ''); } else if (workingFigure.startsWith('+')) { impliedQuality = 'augmented'; workingFigure = workingFigure.replace(/^\+/, ''); } else if (workingFigure.endsWith('d7')) { impliedQuality = 'dominant-seventh'; workingFigure = workingFigure.replace(/d7$/, '7'); } else if ( this.caseMatters &amp;&amp; this.romanNumeralAlone.toUpperCase() === this.romanNumeralAlone ) { impliedQuality = 'major'; } else if ( this.caseMatters &amp;&amp; this.romanNumeralAlone.toLowerCase() === this.romanNumeralAlone ) { impliedQuality = 'minor'; } this.impliedQuality = impliedQuality; return workingFigure; } _fixMinorVIandVII(useScale) { if (useScale.mode !== 'minor') { return; } if (!this.caseMatters) { return; } if (this.scaleDegree !== 6 &amp;&amp; this.scaleDegree !== 7) { return; } if ( !['minor', 'diminished', 'half-diminished'].includes( this.impliedQuality ) ) { return; } const fati = this.frontAlterationTransposeInterval; if (fati !== undefined) { const newFati = interval.add([fati, new interval.Interval('A1')]); this.frontAlterationTransposeInterval = newFati; this.frontAlterationAccidental.alter = this.frontAlterationAccidental.alter + 1; } else { this.frontAlterationTransposeInterval = new interval.Interval('A1'); this.frontAlterationAccidental = new pitch.Accidental(1); } this._tempRoot = this.frontAlterationTransposeInterval.transposePitch( this._tempRoot ); return; } _parseRNAloneAmidstAug6(workingFigure, useScale) { let romanNumeralAlone = ''; const _romanNumeralAloneRegex = new RegExp( '(IV|I{1,3}|VI{0,2}|iv|i{1,3}|vi{0,2}|N)' ); const _augmentedSixthRegex = new RegExp('(It|Ger|Fr|Sw)'); const rm = _romanNumeralAloneRegex.exec(workingFigure); const a6match = _augmentedSixthRegex.exec(workingFigure); if (rm === null &amp;&amp; a6match === null) { throw new Music21Exception( `No roman numeral found in ${workingFigure}` ); } if (a6match !== null) { if (useScale.mode === 'major') { useScale = new key.Key(useScale.tonic.name, 'minor'); this.impliedScale = useScale; this.useImpliedScale = true; } romanNumeralAlone = a6match[1]; if (['It', 'Ger'].includes(romanNumeralAlone)) { this.scaleDegree = 4; } else { this.scaleDegree = 2; } workingFigure = workingFigure.replace(_augmentedSixthRegex, ''); this.romanNumeralAlone = romanNumeralAlone; if (romanNumeralAlone !== 'Fr') { this.bracketedAlterations.push(['#', 1]); } if (romanNumeralAlone === 'Fr' || romanNumeralAlone === 'Sw') { this.bracketedAlterations.push(['#', 3]); } } else { romanNumeralAlone = rm[1]; this.scaleDegree = common.fromRoman(romanNumeralAlone); workingFigure = workingFigure.replace(_romanNumeralAloneRegex, ''); this.romanNumeralAlone = romanNumeralAlone; } return [workingFigure, useScale]; } /** * get romanNumeral - return either romanNumeralAlone (II) or with frontAlterationAccidental (#II) * * @return {string} new romanNumeral; */ get romanNumeral() { if (this.frontAlterationAccidental === undefined) { return this.romanNumeralAlone; } else { return ( this.frontAlterationAccidental.modifier + this.romanNumeralAlone ); } } get scale() { if (this._scale !== undefined) { return this._scale; } else { this._scale = this.key.getScale(); return this._scale; } } get key() { return this._scale; } set key(keyOrScale) { if (typeof keyOrScale === 'string') { this._scale = new key.Key(keyOrScale); } else if (typeof keyOrScale === 'undefined') { this._scale = new key.Key('C'); } else { this._scale = keyOrScale; } if (keyOrScale === undefined) { this.useImpliedScale = true; this.impliedScale = new scale.MajorScale('C'); } else { this.useImpliedScale = false; this.impliedScale = false; } if (this._parsingComplete) { this._updatePitches(); } } get figure() { return this._figure; } set figure(newFigure) { this._figure = newFigure; if (this._parsingComplete) { this._parseFigure(); this._updatePitches(); } } get figureAndKey() { let tonicName = this.key.tonic.name; let mode = ''; if (this.key.mode !== undefined) { mode = ' ' + this.key.mode; } if (mode === ' minor') { tonicName = tonicName.toLowerCase(); } else if (mode === ' major') { tonicName = tonicName.toUpperCase(); } return this.figure + ' in ' + tonicName + mode; } get degreeName() { if (this.scaleDegree &lt; 7) { return [ undefined, 'Tonic', 'Supertonic', 'Mediant', 'Subdominant', 'Dominant', 'Submediant', ][this.scaleDegree]; } else { const tonicPitch = this.key.tonic; let diffRootToTonic = (tonicPitch.ps - this.root().ps) % 12; if (diffRootToTonic &lt; 0) { diffRootToTonic += 12; } if (diffRootToTonic === 1) { return 'Leading-tone'; } else { return 'Subtonic'; } } } /** * Update the .pitches array. Called at instantiation, but not automatically afterwards. * * @memberof music21.roman.RomanNumeral */ _updatePitches() { let useScale; if (this.secondaryRomanNumeralKey !== undefined) { useScale = this.secondaryRomanNumeralKey; } else if (!this.useImpliedScale) { useScale = this.key; } else { useScale = this.impliedScale; } this.scaleCardinality = 7; // simple speedup; const bassScaleDegree = this.bassScaleDegreeFromNotation( this.figuresNotationObj ); const bassPitch = useScale.pitchFromDegree( bassScaleDegree, 'ascending' ); const pitches = [bassPitch]; let lastPitch = bassPitch; const numberNotes = this.figuresNotationObj.numbers.length; for (let j = 0; j &lt; numberNotes; j++) { const i = numberNotes - j - 1; const thisScaleDegree = bassScaleDegree + this.figuresNotationObj.numbers[i] - 1; const newPitch = useScale.pitchFromDegree( thisScaleDegree, 'ascending' ); const pitchName = this.figuresNotationObj.modifiers[ i ].modifyPitchName(newPitch.name); const newNewPitch = new pitch.Pitch(pitchName); newNewPitch.octave = newPitch.octave; if (newNewPitch.ps &lt; lastPitch.ps) { newNewPitch.octave += 1; } pitches.push(newNewPitch); lastPitch = newNewPitch; } if (this.frontAlterationTransposeInterval !== undefined) { const newPitches = []; for (const thisPitch of pitches) { const newPitch = this.frontAlterationTransposeInterval.transposePitch( thisPitch ); newPitches.push(newPitch); } this.pitches = newPitches; } else { this.pitches = pitches; } this._matchAccidentalsToQuality(this.impliedQuality); this.scaleOffset = this.frontAlterationTransposeInterval; if (this.omittedSteps.length) { const omittedPitches = []; for (const thisCS of this.omittedSteps) { const p = this.getChordStep(thisCS); if (p !== undefined) { omittedPitches.push(p.name); } } const newPitches = []; for (const thisPitch of pitches) { if (!omittedPitches.includes(thisPitch.name)) { newPitches.push(thisPitch); } } this.pitches = newPitches; // do something... } this._correctBracketedPitches(); } bassScaleDegreeFromNotation(notationObject) { const c = new pitch.Pitch('C3'); const cDNN = c.diatonicNoteNum; // always 22 const pitches = [c]; for (const i of notationObject.numbers) { const distanceToMove = i - 1; const newDiatonicNumber = cDNN + distanceToMove; const [newStep, newOctave] = interval.convertDiatonicNumberToStep( newDiatonicNumber ); const newPitch = new pitch.Pitch('C3'); newPitch.step = newStep; newPitch.octave = newOctave; pitches.push(newPitch); } const tempChord = new chord.Chord(pitches); const rootDNN = tempChord.root().diatonicNoteNum; const staffDistanceFromBassToRoot = rootDNN - cDNN; let bassSD = common.posMod( this.scaleDegree - staffDistanceFromBassToRoot, 7 ); if (bassSD === 0) { bassSD = 7; } return bassSD; } _matchAccidentalsToQuality(impliedQuality) { const correctSemitones = this._findSemitoneSizeForQuality( impliedQuality ); const chordStepsToExamine = [3, 5, 7]; for (let i = 0; i &lt; chordStepsToExamine.length; i++) { const thisChordStep = chordStepsToExamine[i]; const thisCorrect = correctSemitones[i]; const thisSemis = this.semitonesFromChordStep(thisChordStep); if (thisCorrect === undefined) { continue; } if (thisSemis === undefined) { continue; } if (thisSemis === thisCorrect) { continue; } let correctedSemis = thisCorrect - thisSemis; if (correctedSemis >= 6) { correctedSemis = -1 * (12 - correctedSemis); } else if (correctedSemis &lt;= -6) { correctedSemis += 12; } const faultyPitch = this.getChordStep(thisChordStep); // TODO: check for faultyPitch is undefined if (faultyPitch.accidental === undefined) { faultyPitch.accidental = new pitch.Accidental(correctedSemis); } else { const acc = faultyPitch.accidental; correctedSemis += acc.alter; if (correctedSemis >= 6) { correctedSemis = -1 * (12 - correctedSemis); } else if (correctedSemis &lt;= -6) { correctedSemis += 12; } acc.set(correctedSemis); } } } _correctForSecondaryRomanNumeral(useScale, figure) { if (figure === undefined) { figure = this._figure; } let workingFigure = figure; const rx = new RegExp('(.*?)/([#a-np-zA-NP-Z].*)'); const match = rx.exec(figure); if (match !== null) { const primaryFigure = match[1]; const secondaryFigure = match[2]; const secondaryRomanNumeral = new RomanNumeral( secondaryFigure, useScale, this.caseMatters ); this.secondaryRomanNumeral = secondaryRomanNumeral; let secondaryMode; if (secondaryRomanNumeral.quality === 'minor') { secondaryMode = 'minor'; } else if (secondaryRomanNumeral.quality === 'major') { secondaryMode = 'minor'; } else if (secondaryRomanNumeral.semitonesFromChordStep(3) === 3) { secondaryMode = 'minor'; } else { secondaryMode = 'major'; } this.secondaryRomanNumeralKey = new key.Key( secondaryRomanNumeral.root().name, secondaryMode ); useScale = this.secondaryRomanNumeralKey; workingFigure = primaryFigure; } return [workingFigure, useScale]; } _parseOmittedSteps(workingFigure) { const omittedSteps = []; const rx = new RegExp(/\[no(\d+)\]s*/); let match = rx.exec(workingFigure); while (match !== null) { let thisStep = match[1]; thisStep = parseInt(thisStep); thisStep = thisStep % 7 || 7; omittedSteps.push(thisStep); workingFigure = workingFigure.replace(rx, ''); match = rx.exec(workingFigure); } this.omittedSteps = omittedSteps; return workingFigure; } _parseBracketedAlterations(workingFigure) { const bracketedAlterations = this.bracketedAlterations; const rx = new RegExp(/\[(b+|-+|#+)(\d+)\]/); let match = rx.exec(workingFigure); while (match !== null) { const matchAlteration = match[1]; const matchDegree = parseInt(match[2]); bracketedAlterations.push([matchAlteration, matchDegree]); workingFigure = workingFigure.replace(rx, ''); match = rx.exec(workingFigure); } return workingFigure; } _findSemitoneSizeForQuality(impliedQuality) { let correctSemitones; if (impliedQuality === 'major') { correctSemitones = [4, 7]; } else if (impliedQuality === 'minor') { correctSemitones = [3, 7]; } else if (impliedQuality === 'diminished') { correctSemitones = [3, 6, 9]; } else if (impliedQuality === 'half-diminished') { correctSemitones = [3, 6, 10]; } else if (impliedQuality === 'augmented') { correctSemitones = [4, 8]; } else if (impliedQuality === 'dominant-seventh') { correctSemitones = [4, 7, 10]; } else { correctSemitones = []; } return correctSemitones; } /** * Gives a string display. Note that since inversion is not yet supported * it needs to be given separately. * * Inverting 7th chords does not work. * * @memberof music21.roman.RomanNumeral * @param {string} displayType - ['roman', 'bassName', 'nameOnly', other] * @param {int} [inversion=0] * @returns {String} */ asString(displayType, inversion) { const keyObj = this.key; const tonicName = keyObj.tonic.name; const mode = keyObj.mode; // specifying inversion is for backwards compatibility only. if (inversion === undefined) { inversion = 0; } let inversionName = ''; if (inversion === 1) { if (displayType === 'roman') { inversionName = '6'; } else { inversionName = ' (first inversion)'; } } else if (inversion === 2) { if (displayType === 'roman') { inversionName = '64'; } else { inversionName = ' (second inversion)'; } } let fullChordName; let connector = ' in '; let suffix = ''; if (displayType === 'roman') { fullChordName = this.figure; fullChordName = fullChordName.replace('/o', 'ø'); } else if (displayType === 'nameOnly') { // use only with only choice being tonicName fullChordName = ''; connector = ''; suffix = ' triad'; } else if (displayType === 'bassName') { fullChordName = this.bass().name.replace(/-/, 'b'); } else { // "default" submediant, etc... fullChordName = this.degreeName; if (this.numbers !== undefined) { fullChordName += ' ' + this.numbers.toString(); } } let tonicDisplay = tonicName.replace(/-/, 'b'); if (mode === 'minor') { tonicDisplay = tonicDisplay.toLowerCase(); } const chordStr = fullChordName + inversionName + connector + tonicDisplay + ' ' + mode + suffix; return chordStr; } } roman.RomanNumeral = RomanNumeral; </pre> </article> </section> </div> <div class="clearfix"></div> <footer> <span class="copyright"> Music21j, Copyright © 2013-2018 Michael Scott Cuthbert and cuthbertLab. </span> <br /> <span class="jsdoc-message"> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Wed Jul 4th 2018 using the <a href="https://github.com/terryweiss/docstrap">DocStrap template</a>. </span> </footer> </div> <br clear="both"> </div> </div> <!--<script src="scripts/sunlight.js"></script>--> <script src="scripts/docstrap.lib.js"></script> <script src="scripts/bootstrap-dropdown.js"></script> <script src="scripts/toc.js"></script> <script> $( function () { $( "[id*='$']" ).each( function () { var $this = $( this ); $this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) ); } ); $( "#toc" ).toc( { anchorName : function ( i, heading, prefix ) { return $( heading ).attr( "id" ) || ( prefix + i ); }, selectors : "h1,h2,h3,h4", showAndHide : false, scrollTo : "100px" } ); $( "#toc>ul" ).addClass( "nav nav-pills nav-stacked" ); $( "#main span[id^='toc']" ).addClass( "toc-shim" ); $( '.dropdown-toggle' ).dropdown(); // $( ".tutorial-section pre, .readme-section pre" ).addClass( "sunlight-highlight-javascript" ).addClass( "linenums" ); $( ".tutorial-section pre, .readme-section pre" ).each( function () { var $this = $( this ); var example = $this.find( "code" ); exampleText = example.html(); var lang = /{@lang (.*?)}/.exec( exampleText ); if ( lang && lang[1] ) { exampleText = exampleText.replace( lang[0], "" ); example.html( exampleText ); lang = lang[1]; } else { lang = "javascript"; } if ( lang ) { $this .addClass( "sunlight-highlight-" + lang ) .addClass( "linenums" ) .html( example.html() ); } } ); Sunlight.highlightAll( { lineNumbers : true, showMenu : true, enableDoclinks : true } ); } ); </script> <!--Navigation and Symbol Display--> <!--Google Analytics--> </body> </html>