/**
* This module provides functions for producing lists of MIDI note
* numbers from scale degrees, note names, or nashville numbers.
* @module chords
* @examples test/data/examples/chords-examples.js
*/
import * as core from './core.js'
import * as utils from './utils.js'
/**
* degrees for each type relative to major scale, user can add more
* @type {Object(string, Array.<(string|number)>)}
* @static
*/
const chordDegrees = {
'': [1, 3, 5], // major chord doean't require a designation
M: [1, 3, 5],
Dom: [1, 3, 5],
m: [1, 'b3', 5],
'+': [1, 3, '#5'],
b5: [1, 3, 'b5'],
dim: [1, 'b3', 'b5'],
aug: [1, '3', '#5'],
sus4: [1, 4, 5],
sus2: [1, 2, 5],
M6: [1, 3, 5, 6],
m6: [1, 'b3', 5, 6],
7: [1, 3, 5, 'b7'],
Dom7: [1, 3, 5, 'b7'],
M7: [1, 3, 5, 7],
m7: [1, 'b3', 5, 'b7'],
mM7: [1, 'b3', 5, 7],
'+7': [1, 3, '#5', 'b7'], // augmented 7th chord, aka augmented minor 7th chord
'+M7': [1, 3, '#5', 7], // augmented major 7th chord
dim7: [1, 'b3', 'b5', 6],
'7b5': [1, 3, 'b5', 'b7'],
m7b5: [1, 'b3', 'b5', 'b7'],
add9: [1, 3, 5, 9]
}
/**
* triad and 7th types for major and minor scales
* @type {Object(string, string[])}
* @static
*/
const nashvilleChordTypes = {
M: ['M', 'm', 'm', 'M', 'M', 'm', 'dim'],
m: ['m', 'dim', 'M', 'm', 'm', 'M', 'M'],
hm: ['m', 'dim', 'aug', 'm', 'M', 'M', 'dim'],
M7: ['M7', 'm7', 'm7', 'M7', '7', 'm7', 'm7b5'],
m7: ['m7', 'm7b5', 'M7', 'm7', 'm7', 'M7', '7'],
hm7: ['mM7', 'm7b5', '+7', 'm7', '7', 'M7', 'dim7']
}
/**
* What are the MIDI note numbers for the *degrees*?
* @param {Array.<(string|number)>} degrees list of scale degrees
* @param {Object} options {octave(default octave), shift(where is middle C)}
* @returns {number[]} array of MIDI note numbers for *degrees*
* @static
*/
function degreesToMidi (degrees,
{ root = 'C', scale, defaultOctave, shift = core.origin.shift } = {}) {
//
const rootIdx = utils.noteIndex(root)
const result = []
for (const spec of degrees) {
let { degree, octave } = utils.parseDegree(spec) // eslint-disable-line
octave = (octave === undefined) ? defaultOctave : parseInt(octave)
result.push(core.chromatic(degree, { scale, octave, shift: shift + rootIdx }))
}
return result
}
/**
* What are the MIDI note numbers for the list of *notes*?
* @param {string[]} notes
* @param {Object=} options {octave(default octave), shift(where is middle C)}
* @returns {number[]} array of MIDI note numbers
* @static
*/
function notesToMidi (notes, { defaultOctave } = {}) {
const result = []
for (const spec of notes) {
let { note, octave } = utils.parseNote(spec) // eslint-disable-line
octave = (octave === undefined) ? defaultOctave : parseInt(octave)
const degree = utils.noteIndex(note) + 1
const scale = 'Chromatic'
result.push(core.chromatic(degree, { scale, octave }))
}
return result
}
/**
* What are the MIDI note numbers for the *name*d chord (such as 'C#7b5',
* or 'BM7:3')?
* @param {Object=} options { name = <chord name>, scale = 'Ionian',
* defaultOctave = origin.octave }
* @returns {number[]} array of MIDI note numbers
* @static
*/
function chordToMidi ({ name, scale, defaultOctave }) {
let { note, type, octave } = utils.parseChord(name) // eslint-disable-line
octave = (octave === undefined) ? defaultOctave : parseInt(octave)
const shift = core.origin.shift + utils.noteIndex(note)
const degrees = chordDegrees[type || ''] // relative to major scale
const result = core.chromatics(degrees, { scale, octave, shift })
return result
}
/**
* What are the MIDI note numbers for the *name*d Nashville chord
* (such as '1', '4^7:3')?
* @param {Object} options { name = <chord name>, key = 'C', scale = 'M7' }
* @returns {number[]} array of MIDI note numbers
* @static
*/
function nashvilleToMidi ({ name, key = 'C', defaultOctave, scale = 'M7' }) {
// eslint-disable-next-line
let { nashNum, offset, type, octave } = utils.parseNashvilleChord(name)
octave = (octave === undefined) ? defaultOctave : parseInt(octave)
type = type || nashvilleChordTypes[scale][nashNum - 1]
// relative to major scale
const degrees = chordDegrees[type]
// will shift all degrees by nashNum's chromatic degree
const shift = core.origin.shift + utils.noteIndex(key) +
core.chromatic(nashNum, { octave: 0, shift: 0 }) + offset
const result = core.chromatics(degrees, { scale: 'Ionian', octave, shift })
return result
}
export {
chordDegrees,
degreesToMidi,
notesToMidi,
chordToMidi,
nashvilleToMidi
}