tonal-interval
Advanced tools
Comparing version 0.69.9 to 1.0.0-0
@@ -5,5 +5,2 @@ 'use strict'; | ||
var intervalNotation = require('interval-notation'); | ||
var tonalPitch = require('tonal-pitch'); | ||
/** | ||
@@ -31,3 +28,3 @@ * [![npm version](https://img.shields.io/npm/v/tonal-interval.svg)](https://www.npmjs.com/package/tonal-interval) | ||
* import * as interval from 'tonal-interval' | ||
* // or var interval = require('tonal-interval') | ||
* // or const interval = require('tonal-interval') | ||
* interval.semitones('4P') // => 5 | ||
@@ -46,92 +43,138 @@ * interval.invert('3m') // => '6M' | ||
*/ | ||
// shorthand tonal notation (with quality after number) | ||
const IVL_TNL = "([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})"; | ||
// standard shorthand notation (with quality before number) | ||
const IVL_STR = "(AA|A|P|M|m|d|dd)([-+]?\\d+)"; | ||
const REGEX = new RegExp("^" + IVL_TNL + "|" + IVL_STR + "$"); | ||
const SIZES = [0, 2, 4, 5, 7, 9, 11]; | ||
const TYPES = "PMMPPMM"; | ||
const CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]; | ||
const tokenize = str => { | ||
const m = REGEX.exec(str); | ||
return m === null ? null : m[1] ? [m[1], m[2]] : [m[4], m[3]]; | ||
}; | ||
const NO_IVL = Object.freeze({ | ||
name: null, | ||
num: null, | ||
q: null, | ||
step: null, | ||
alt: null, | ||
dir: null, | ||
type: null, | ||
simple: null, | ||
semitones: null, | ||
chroma: null, | ||
ic: null | ||
}); | ||
const fillStr = (s, n) => Array(Math.abs(n) + 1).join(s); | ||
const qToAlt = (type, q) => { | ||
if (q === "M" && type === "M") return 0; | ||
if (q === "P" && type === "P") return 0; | ||
if (q === "m" && type === "M") return -1; | ||
if (/^A+$/.test(q)) return q.length; | ||
if (/^d+$/.test(q)) return type === "P" ? -q.length : -q.length - 1; | ||
return null; | ||
}; | ||
const altToQ = (type, alt) => { | ||
if (alt === 0) return type === "M" ? "M" : "P"; | ||
else if (alt === -1 && type === "M") return "m"; | ||
else if (alt > 0) return fillStr("A", alt); | ||
else if (alt < 0) return fillStr("d", type === "P" ? alt : alt + 1); | ||
else return null; | ||
}; | ||
const properties = str => { | ||
const t = tokenize(str); | ||
if (t === null) return NO_IVL; | ||
const p = { num: +t[0], q: t[1] }; | ||
p.step = (Math.abs(p.num) - 1) % 7; | ||
p.type = TYPES[p.step]; | ||
if (p.type === "M" && p.q === "P") return NO_IVL; | ||
p.name = "" + p.num + p.q; | ||
p.dir = p.num < 0 ? -1 : 1; | ||
p.simple = p.num === 8 || p.num === -8 ? p.num : p.dir * (p.step + 1); | ||
p.alt = qToAlt(p.type, p.q); | ||
p.oct = Math.floor((Math.abs(p.num) - 1) / 7); | ||
p.semitones = p.dir * (SIZES[p.step] + p.alt + 12 * p.oct); | ||
p.chroma = ((p.dir * (SIZES[p.step] + p.alt)) % 12 + 12) % 12; | ||
p.ic = CLASSES[p.chroma]; | ||
return Object.freeze(p); | ||
}; | ||
const cache = {}; | ||
/** | ||
* Get interval name. Can be used to test if it's an interval. It accepts intervals | ||
* as pitch or string in shorthand notation or tonal notation. It returns always | ||
* intervals in tonal notation. | ||
* Get interval properties. It returns an object with: | ||
* | ||
* @param {String|Pitch} interval - the interval string or array | ||
* @return {String} the interval name or null if not valid interval | ||
* @example | ||
* interval.toInterval('m-3') // => '-3m' | ||
* interval.toInterval('3') // => null | ||
* - name: name | ||
* - num: number | ||
* - q: quality | ||
* - step: step | ||
* - alt: alteration | ||
* - dir: direction (1 ascending, -1 descending) | ||
* - type: "P" or "M" for perfectable or majorable | ||
* - simple: the simplified number | ||
* - semitones: the size in semitones | ||
* - chroma: the interval chroma | ||
* - ic: the interval class | ||
* | ||
* @function | ||
* @param {String} interval - the interval | ||
* @return {Object} the interval in the form [number, alt] | ||
*/ | ||
function toInterval (ivl) { | ||
var i = tonalPitch.asIvlPitch(ivl); | ||
return i ? tonalPitch.strIvl(i) : null | ||
function props(str) { | ||
if (typeof str !== "string") return NO_IVL; | ||
return cache[str] || (cache[str] = properties(str)); | ||
} | ||
/** | ||
* Get the number of the interval (same as value, but always positive) | ||
* Get the number of the interval | ||
* | ||
* @param {String|Pitch} interval - the interval | ||
* @return {Integer} the positive interval number (P1 is 1, m2 is 2, ...) | ||
* @function | ||
* @param {String} interval - the interval | ||
* @return {Integer} | ||
* @example | ||
* interval.num('m2') // => 2 | ||
* interval.num('P9') // => 9 | ||
* interval.num('P-4') // => 4 | ||
* interval.num('P-4') // => -4 | ||
*/ | ||
function num (ivl) { | ||
var p = props(ivl); | ||
return p ? p.num : null | ||
} | ||
const num = str => props(str).num; | ||
/** | ||
* Get the interval value (the interval number, but positive or negative | ||
* depending the interval direction) | ||
* Get interval name. Can be used to test if it's an interval. It accepts intervals | ||
* as pitch or string in shorthand notation or tonal notation. It returns always | ||
* intervals in tonal notation. | ||
* | ||
* @param {String|Pitch} interval - the interval | ||
* @return {Integer} the positive interval number (P1 is 1, m-2 is -2, ...) | ||
* @function | ||
* @param {String} interval - the interval string or array | ||
* @return {String} the interval name or null if not valid interval | ||
* @example | ||
* interval.num('m2') // => 2 | ||
* interval.num('m9') // => 9 | ||
* interval.num('P-4') // => -4 | ||
* interval.num('m-9') // => -9 | ||
* interval.name('m-3') // => '-3m' | ||
* interval.name('3') // => null | ||
*/ | ||
function value (ivl) { | ||
var p = props(ivl); | ||
return p ? p.num * p.dir : null | ||
} | ||
const name = str => props(str).name; | ||
/** | ||
* Get interval properties. It returns an object with: | ||
* Get interval type. Can be perfectable (1, 4, 5) or majorable (2, 3, 6, 7) | ||
* It does NOT return the actual quality. | ||
* | ||
* - num: the interval number (always positive) | ||
* - alt: the interval alteration (0 for perfect in perfectables, or 0 for major in _majorables_) | ||
* - dir: the interval direction (1 ascending, -1 descending) | ||
* | ||
* @param {String|Pitch} interval - the interval | ||
* @return {Array} the interval in the form [number, alt] | ||
* @function | ||
* @param {String} interval | ||
* @return {String} 'P' for perfectables, 'M' for majorables or null if not | ||
* valid interval | ||
* @example | ||
* interval.parse('m2') // => { num: 2, alt: -1, dir: 1 } | ||
* interval.parse('m9') // => { num: 9, alt: -1, dir: 1 } | ||
* interval.parse('P-4') // => { num: 4, alt: 0, dir: -1} | ||
* interval.parse('m-9') // => { num: 9, alt: -1, dir: -1 } | ||
* interval.type('5A') // => 'P' | ||
*/ | ||
function props (ivl) { | ||
var i = tonalPitch.asIvlPitch(ivl); | ||
if (!i) return null | ||
var d = tonalPitch.decode(i); | ||
return { num: d[0] + 1 + d[2] * 7, alt: d[1], dir: i[2] } | ||
} | ||
const type = str => props(str).type; | ||
/** | ||
* Given a interval property object, get the interval name | ||
* | ||
* @param {Object} props - the interval property object | ||
* | ||
* - num: the interval number | ||
* - alt: the interval alteration | ||
* - dir: the direction | ||
* @return {String} the interval name | ||
*/ | ||
function fromProps (props) { | ||
if (!props || props.num < 1) return null | ||
var octs = Math.floor((props.num) / 8); | ||
var simple = props.num - 7 * octs; | ||
return intervalNotation.build(simple, props.alt || 0, octs, props.dir) | ||
} | ||
/** | ||
* Get size in semitones of an interval | ||
* @param {String|Pitch} ivl | ||
* | ||
* @function | ||
* @param {String} ivl | ||
* @return {Integer} the number of semitones or null if not an interval | ||
@@ -142,34 +185,16 @@ * @example | ||
* // or using tonal | ||
* tonal.semitones('P5') // => 7 | ||
* tonal.interval.semitones('P5') // => 7 | ||
*/ | ||
function semitones (ivl) { | ||
var i = tonalPitch.asIvlPitch(ivl); | ||
return i ? tonalPitch.height(i) : null | ||
} | ||
const semitones = str => props(str).semitones; | ||
// interval numbers | ||
var IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7]; | ||
// interval qualities | ||
var IQ = 'P m M m M P d P m M m M'.split(' '); | ||
/** | ||
* Get interval name from semitones number. Since there are several interval | ||
* names for the same number, the name it's arbitraty, but deterministic. | ||
* @param {Integer} num - the number of semitones (can be negative) | ||
* @return {String} the interval name | ||
* @example | ||
* import { fromSemitones } from 'tonal-interval' | ||
* fromSemitones(7) // => '5P' | ||
* // or using tonal | ||
* tonal.fromSemitones(-7) // => '-5P' | ||
* Get the chroma of the interval. The chroma is a number between 0 and 7 | ||
* that represents the position within an octave (pitch set) | ||
* | ||
* @function | ||
* @param {String} str | ||
* @return {Number} | ||
*/ | ||
function fromSemitones (num) { | ||
var d = num < 0 ? -1 : 1; | ||
var n = Math.abs(num); | ||
var c = n % 12; | ||
var o = Math.floor(n / 12); | ||
return d * (IN[c] + 7 * o) + IQ[c] | ||
} | ||
const chroma = str => props(str).chroma; | ||
var CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]; | ||
/** | ||
@@ -185,2 +210,3 @@ * Get the [interval class](https://en.wikipedia.org/wiki/Interval_class) | ||
* | ||
* @function | ||
* @param {String|Integer} interval - the interval or the number of semitones | ||
@@ -194,25 +220,48 @@ * @return {Integer} A value between 0 and 6 | ||
*/ | ||
function ic (ivl) { | ||
var i = tonalPitch.asIvlPitch(ivl); | ||
var s = i ? tonalPitch.chr(i) : Math.round(ivl); | ||
return isNaN(s) ? null : CLASSES[Math.abs(s) % 12] | ||
} | ||
const ic = str => props(str).ic; | ||
var TYPES = 'PMMPPMM'; | ||
/** | ||
* Get interval type. Can be perfectable (1, 4, 5) or majorable (2, 3, 6, 7) | ||
* It does NOT return the actual quality. | ||
* Given a interval property object, get the interval name | ||
* | ||
* @param {String|Pitch} interval | ||
* @return {String} 'P' for perfectables, 'M' for majorables or null if not | ||
* valid interval | ||
* @function | ||
* @param {Object} props - the interval property object | ||
* | ||
* - num: the interval number | ||
* - alt: the interval alteration | ||
* - oct: the number of octaves | ||
* - dir: the direction | ||
* | ||
* @return {String} the interval name | ||
* @example | ||
* interval.type('5A') // => 'P' | ||
* interval.build({ step: 1, alt: -1, oct: 0, dir: 1 }) // => "1d" | ||
*/ | ||
function type (ivl) { | ||
var i = tonalPitch.asIvlPitch(ivl); | ||
return i ? TYPES[tonalPitch.decode(i)[0]] : null | ||
} | ||
const build = ({ step, alt, oct, dir } = {}) => { | ||
if (step === undefined) return null; | ||
const d = dir < 0 ? "-" : ""; | ||
const num = step + 1 + 7 * oct; | ||
const type = TYPES[step]; | ||
return d + num + altToQ(type, alt); | ||
}; | ||
/** | ||
* Get the simplified version of an interval. | ||
* | ||
* @function | ||
* @param {String} interval - the interval to simplify | ||
* @return {String} the simplified interval | ||
* | ||
* @example | ||
* interval.simplify('9M') // => '2M' | ||
* ['8P', '9M', '10M', '11P', '12P', '13M', '14M', '15P'].map(interval.simplify) | ||
* // => [ '8P', '2M', '3M', '4P', '5P', '6M', '7M', '8P' ] | ||
* interval.simplify('2M') // => '2M' | ||
* interval.simplify('-2M') // => '7m' | ||
*/ | ||
const simplify = str => { | ||
const p = props(str); | ||
if (p === NO_IVL) return null; | ||
return p.simple + p.q; | ||
}; | ||
/** | ||
* Get the inversion (https://en.wikipedia.org/wiki/Inversion_(music)#Intervals) | ||
@@ -222,5 +271,5 @@ * of an interval. | ||
* @function | ||
* @param {String|Pitch} interval - the interval to invert in interval shorthand | ||
* @param {String} interval - the interval to invert in interval shorthand | ||
* notation or interval array notation | ||
* @return {String|Pitch} the inverted interval | ||
* @return {String} the inverted interval | ||
* | ||
@@ -231,43 +280,47 @@ * @example | ||
*/ | ||
var invert = tonalPitch.ivlFn(function (i) { | ||
var d = tonalPitch.decode(i); | ||
// d = [step, alt, oct] | ||
var step = (7 - d[0]) % 7; | ||
var alt = TYPES[d[0]] === 'P' ? -d[1] : -(d[1] + 1); | ||
return tonalPitch.encode(step, alt, d[2], tonalPitch.dir(i)) | ||
}); | ||
const invert = str => { | ||
const p = props(str); | ||
if (p === NO_IVL) return null; | ||
const step = (7 - p.step) % 7; | ||
const alt = p.type === "P" ? -p.alt : -(p.alt + 1); | ||
return build({ step, alt, oct: p.oct, dir: p.dir }); | ||
}; | ||
// interval numbers | ||
var IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7]; | ||
// interval qualities | ||
var IQ = "P m M m M P d P m M m M".split(" "); | ||
/** | ||
* Get the simplified version of an interval. | ||
* | ||
* Get interval name from semitones number. Since there are several interval | ||
* names for the same number, the name it's arbitraty, but deterministic. | ||
* | ||
* @function | ||
* @param {String|Array} interval - the interval to simplify | ||
* @return {String|Array} the simplified interval | ||
* | ||
* @param {Integer} num - the number of semitones (can be negative) | ||
* @return {String} the interval name | ||
* @example | ||
* interval.simplify('9M') // => '2M' | ||
* ['8P', '9M', '10M', '11P', '12P', '13M', '14M', '15P'].map(interval.simplify) | ||
* // => [ '8P', '2M', '3M', '4P', '5P', '6M', '7M', '8P' ] | ||
* interval.simplify('2M') // => '2M' | ||
* interval.simplify('-2M') // => '7m' | ||
* import { fromSemitones } from 'tonal-interval' | ||
* fromSemitones(7) // => '5P' | ||
* // or using tonal | ||
* tonal.fromSemitones(-7) // => '-5P' | ||
*/ | ||
var simplify = tonalPitch.ivlFn(function (i) { | ||
// decode to [step, alt, octave] | ||
var dec = tonalPitch.decode(i); | ||
// if it's not 8 reduce the octaves to 0 | ||
if (dec[0] !== 0 || dec[2] !== 1) dec[2] = 0; | ||
// encode back | ||
return tonalPitch.encode(dec[0], dec[1], dec[2], tonalPitch.dir(i)) | ||
}); | ||
const fromSemitones = num => { | ||
var d = num < 0 ? -1 : 1; | ||
var n = Math.abs(num); | ||
var c = n % 12; | ||
var o = Math.floor(n / 12); | ||
return d * (IN[c] + 7 * o) + IQ[c]; | ||
}; | ||
exports.toInterval = toInterval; | ||
exports.tokenize = tokenize; | ||
exports.props = props; | ||
exports.num = num; | ||
exports.value = value; | ||
exports.props = props; | ||
exports.fromProps = fromProps; | ||
exports.name = name; | ||
exports.type = type; | ||
exports.semitones = semitones; | ||
exports.fromSemitones = fromSemitones; | ||
exports.chroma = chroma; | ||
exports.ic = ic; | ||
exports.type = type; | ||
exports.build = build; | ||
exports.simplify = simplify; | ||
exports.invert = invert; | ||
exports.simplify = simplify; | ||
exports.fromSemitones = fromSemitones; |
331
index.js
@@ -23,3 +23,3 @@ /** | ||
* import * as interval from 'tonal-interval' | ||
* // or var interval = require('tonal-interval') | ||
* // or const interval = require('tonal-interval') | ||
* interval.semitones('4P') // => 5 | ||
@@ -38,96 +38,138 @@ * interval.invert('3m') // => '6M' | ||
*/ | ||
import { build } from 'interval-notation' | ||
import { asIvlPitch, ivlFn, chr, dir, | ||
strIvl, encode, decode, height } from 'tonal-pitch' | ||
// shorthand tonal notation (with quality after number) | ||
const IVL_TNL = "([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})"; | ||
// standard shorthand notation (with quality before number) | ||
const IVL_STR = "(AA|A|P|M|m|d|dd)([-+]?\\d+)"; | ||
const REGEX = new RegExp("^" + IVL_TNL + "|" + IVL_STR + "$"); | ||
const SIZES = [0, 2, 4, 5, 7, 9, 11]; | ||
const TYPES = "PMMPPMM"; | ||
const CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]; | ||
export const tokenize = str => { | ||
const m = REGEX.exec(str); | ||
return m === null ? null : m[1] ? [m[1], m[2]] : [m[4], m[3]]; | ||
}; | ||
const NO_IVL = Object.freeze({ | ||
name: null, | ||
num: null, | ||
q: null, | ||
step: null, | ||
alt: null, | ||
dir: null, | ||
type: null, | ||
simple: null, | ||
semitones: null, | ||
chroma: null, | ||
ic: null | ||
}); | ||
const fillStr = (s, n) => Array(Math.abs(n) + 1).join(s); | ||
const qToAlt = (type, q) => { | ||
if (q === "M" && type === "M") return 0; | ||
if (q === "P" && type === "P") return 0; | ||
if (q === "m" && type === "M") return -1; | ||
if (/^A+$/.test(q)) return q.length; | ||
if (/^d+$/.test(q)) return type === "P" ? -q.length : -q.length - 1; | ||
return null; | ||
}; | ||
const altToQ = (type, alt) => { | ||
if (alt === 0) return type === "M" ? "M" : "P"; | ||
else if (alt === -1 && type === "M") return "m"; | ||
else if (alt > 0) return fillStr("A", alt); | ||
else if (alt < 0) return fillStr("d", type === "P" ? alt : alt + 1); | ||
else return null; | ||
}; | ||
const properties = str => { | ||
const t = tokenize(str); | ||
if (t === null) return NO_IVL; | ||
const p = { num: +t[0], q: t[1] }; | ||
p.step = (Math.abs(p.num) - 1) % 7; | ||
p.type = TYPES[p.step]; | ||
if (p.type === "M" && p.q === "P") return NO_IVL; | ||
p.name = "" + p.num + p.q; | ||
p.dir = p.num < 0 ? -1 : 1; | ||
p.simple = p.num === 8 || p.num === -8 ? p.num : p.dir * (p.step + 1); | ||
p.alt = qToAlt(p.type, p.q); | ||
p.oct = Math.floor((Math.abs(p.num) - 1) / 7); | ||
p.semitones = p.dir * (SIZES[p.step] + p.alt + 12 * p.oct); | ||
p.chroma = ((p.dir * (SIZES[p.step] + p.alt)) % 12 + 12) % 12; | ||
p.ic = CLASSES[p.chroma]; | ||
return Object.freeze(p); | ||
}; | ||
const cache = {}; | ||
/** | ||
* Get interval name. Can be used to test if it's an interval. It accepts intervals | ||
* as pitch or string in shorthand notation or tonal notation. It returns always | ||
* intervals in tonal notation. | ||
* Get interval properties. It returns an object with: | ||
* | ||
* @param {String|Pitch} interval - the interval string or array | ||
* @return {String} the interval name or null if not valid interval | ||
* @example | ||
* interval.toInterval('m-3') // => '-3m' | ||
* interval.toInterval('3') // => null | ||
* - name: name | ||
* - num: number | ||
* - q: quality | ||
* - step: step | ||
* - alt: alteration | ||
* - dir: direction (1 ascending, -1 descending) | ||
* - type: "P" or "M" for perfectable or majorable | ||
* - simple: the simplified number | ||
* - semitones: the size in semitones | ||
* - chroma: the interval chroma | ||
* - ic: the interval class | ||
* | ||
* @function | ||
* @param {String} interval - the interval | ||
* @return {Object} the interval in the form [number, alt] | ||
*/ | ||
export function toInterval (ivl) { | ||
var i = asIvlPitch(ivl) | ||
return i ? strIvl(i) : null | ||
export function props(str) { | ||
if (typeof str !== "string") return NO_IVL; | ||
return cache[str] || (cache[str] = properties(str)); | ||
} | ||
/** | ||
* Get the number of the interval (same as value, but always positive) | ||
* Get the number of the interval | ||
* | ||
* @param {String|Pitch} interval - the interval | ||
* @return {Integer} the positive interval number (P1 is 1, m2 is 2, ...) | ||
* @function | ||
* @param {String} interval - the interval | ||
* @return {Integer} | ||
* @example | ||
* interval.num('m2') // => 2 | ||
* interval.num('P9') // => 9 | ||
* interval.num('P-4') // => 4 | ||
* interval.num('P-4') // => -4 | ||
*/ | ||
export function num (ivl) { | ||
var p = props(ivl) | ||
return p ? p.num : null | ||
} | ||
export const num = str => props(str).num; | ||
/** | ||
* Get the interval value (the interval number, but positive or negative | ||
* depending the interval direction) | ||
* Get interval name. Can be used to test if it's an interval. It accepts intervals | ||
* as pitch or string in shorthand notation or tonal notation. It returns always | ||
* intervals in tonal notation. | ||
* | ||
* @param {String|Pitch} interval - the interval | ||
* @return {Integer} the positive interval number (P1 is 1, m-2 is -2, ...) | ||
* @function | ||
* @param {String} interval - the interval string or array | ||
* @return {String} the interval name or null if not valid interval | ||
* @example | ||
* interval.num('m2') // => 2 | ||
* interval.num('m9') // => 9 | ||
* interval.num('P-4') // => -4 | ||
* interval.num('m-9') // => -9 | ||
* interval.name('m-3') // => '-3m' | ||
* interval.name('3') // => null | ||
*/ | ||
export function value (ivl) { | ||
var p = props(ivl) | ||
return p ? p.num * p.dir : null | ||
} | ||
export const name = str => props(str).name; | ||
/** | ||
* Get interval properties. It returns an object with: | ||
* Get interval type. Can be perfectable (1, 4, 5) or majorable (2, 3, 6, 7) | ||
* It does NOT return the actual quality. | ||
* | ||
* - num: the interval number (always positive) | ||
* - alt: the interval alteration (0 for perfect in perfectables, or 0 for major in _majorables_) | ||
* - dir: the interval direction (1 ascending, -1 descending) | ||
* | ||
* @param {String|Pitch} interval - the interval | ||
* @return {Array} the interval in the form [number, alt] | ||
* @function | ||
* @param {String} interval | ||
* @return {String} 'P' for perfectables, 'M' for majorables or null if not | ||
* valid interval | ||
* @example | ||
* interval.parse('m2') // => { num: 2, alt: -1, dir: 1 } | ||
* interval.parse('m9') // => { num: 9, alt: -1, dir: 1 } | ||
* interval.parse('P-4') // => { num: 4, alt: 0, dir: -1} | ||
* interval.parse('m-9') // => { num: 9, alt: -1, dir: -1 } | ||
* interval.type('5A') // => 'P' | ||
*/ | ||
export function props (ivl) { | ||
var i = asIvlPitch(ivl) | ||
if (!i) return null | ||
var d = decode(i) | ||
return { num: d[0] + 1 + d[2] * 7, alt: d[1], dir: i[2] } | ||
} | ||
export const type = str => props(str).type; | ||
/** | ||
* Given a interval property object, get the interval name | ||
* | ||
* @param {Object} props - the interval property object | ||
* | ||
* - num: the interval number | ||
* - alt: the interval alteration | ||
* - dir: the direction | ||
* @return {String} the interval name | ||
*/ | ||
export function fromProps (props) { | ||
if (!props || props.num < 1) return null | ||
var octs = Math.floor((props.num) / 8) | ||
var simple = props.num - 7 * octs | ||
return build(simple, props.alt || 0, octs, props.dir) | ||
} | ||
/** | ||
* Get size in semitones of an interval | ||
* @param {String|Pitch} ivl | ||
* | ||
* @function | ||
* @param {String} ivl | ||
* @return {Integer} the number of semitones or null if not an interval | ||
@@ -138,34 +180,16 @@ * @example | ||
* // or using tonal | ||
* tonal.semitones('P5') // => 7 | ||
* tonal.interval.semitones('P5') // => 7 | ||
*/ | ||
export function semitones (ivl) { | ||
var i = asIvlPitch(ivl) | ||
return i ? height(i) : null | ||
} | ||
export const semitones = str => props(str).semitones; | ||
// interval numbers | ||
var IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7] | ||
// interval qualities | ||
var IQ = 'P m M m M P d P m M m M'.split(' ') | ||
/** | ||
* Get interval name from semitones number. Since there are several interval | ||
* names for the same number, the name it's arbitraty, but deterministic. | ||
* @param {Integer} num - the number of semitones (can be negative) | ||
* @return {String} the interval name | ||
* @example | ||
* import { fromSemitones } from 'tonal-interval' | ||
* fromSemitones(7) // => '5P' | ||
* // or using tonal | ||
* tonal.fromSemitones(-7) // => '-5P' | ||
* Get the chroma of the interval. The chroma is a number between 0 and 7 | ||
* that represents the position within an octave (pitch set) | ||
* | ||
* @function | ||
* @param {String} str | ||
* @return {Number} | ||
*/ | ||
export function fromSemitones (num) { | ||
var d = num < 0 ? -1 : 1 | ||
var n = Math.abs(num) | ||
var c = n % 12 | ||
var o = Math.floor(n / 12) | ||
return d * (IN[c] + 7 * o) + IQ[c] | ||
} | ||
export const chroma = str => props(str).chroma; | ||
var CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1] | ||
/** | ||
@@ -181,2 +205,3 @@ * Get the [interval class](https://en.wikipedia.org/wiki/Interval_class) | ||
* | ||
* @function | ||
* @param {String|Integer} interval - the interval or the number of semitones | ||
@@ -190,25 +215,48 @@ * @return {Integer} A value between 0 and 6 | ||
*/ | ||
export function ic (ivl) { | ||
var i = asIvlPitch(ivl) | ||
var s = i ? chr(i) : Math.round(ivl) | ||
return isNaN(s) ? null : CLASSES[Math.abs(s) % 12] | ||
} | ||
export const ic = str => props(str).ic; | ||
var TYPES = 'PMMPPMM' | ||
/** | ||
* Get interval type. Can be perfectable (1, 4, 5) or majorable (2, 3, 6, 7) | ||
* It does NOT return the actual quality. | ||
* Given a interval property object, get the interval name | ||
* | ||
* @param {String|Pitch} interval | ||
* @return {String} 'P' for perfectables, 'M' for majorables or null if not | ||
* valid interval | ||
* @function | ||
* @param {Object} props - the interval property object | ||
* | ||
* - num: the interval number | ||
* - alt: the interval alteration | ||
* - oct: the number of octaves | ||
* - dir: the direction | ||
* | ||
* @return {String} the interval name | ||
* @example | ||
* interval.type('5A') // => 'P' | ||
* interval.build({ step: 1, alt: -1, oct: 0, dir: 1 }) // => "1d" | ||
*/ | ||
export function type (ivl) { | ||
var i = asIvlPitch(ivl) | ||
return i ? TYPES[decode(i)[0]] : null | ||
} | ||
export const build = ({ step, alt, oct, dir } = {}) => { | ||
if (step === undefined) return null; | ||
const d = dir < 0 ? "-" : ""; | ||
const num = step + 1 + 7 * oct; | ||
const type = TYPES[step]; | ||
return d + num + altToQ(type, alt); | ||
}; | ||
/** | ||
* Get the simplified version of an interval. | ||
* | ||
* @function | ||
* @param {String} interval - the interval to simplify | ||
* @return {String} the simplified interval | ||
* | ||
* @example | ||
* interval.simplify('9M') // => '2M' | ||
* ['8P', '9M', '10M', '11P', '12P', '13M', '14M', '15P'].map(interval.simplify) | ||
* // => [ '8P', '2M', '3M', '4P', '5P', '6M', '7M', '8P' ] | ||
* interval.simplify('2M') // => '2M' | ||
* interval.simplify('-2M') // => '7m' | ||
*/ | ||
export const simplify = str => { | ||
const p = props(str); | ||
if (p === NO_IVL) return null; | ||
return p.simple + p.q; | ||
}; | ||
/** | ||
* Get the inversion (https://en.wikipedia.org/wiki/Inversion_(music)#Intervals) | ||
@@ -218,5 +266,5 @@ * of an interval. | ||
* @function | ||
* @param {String|Pitch} interval - the interval to invert in interval shorthand | ||
* @param {String} interval - the interval to invert in interval shorthand | ||
* notation or interval array notation | ||
* @return {String|Pitch} the inverted interval | ||
* @return {String} the inverted interval | ||
* | ||
@@ -227,31 +275,34 @@ * @example | ||
*/ | ||
export var invert = ivlFn(function (i) { | ||
var d = decode(i) | ||
// d = [step, alt, oct] | ||
var step = (7 - d[0]) % 7 | ||
var alt = TYPES[d[0]] === 'P' ? -d[1] : -(d[1] + 1) | ||
return encode(step, alt, d[2], dir(i)) | ||
}) | ||
export const invert = str => { | ||
const p = props(str); | ||
if (p === NO_IVL) return null; | ||
const step = (7 - p.step) % 7; | ||
const alt = p.type === "P" ? -p.alt : -(p.alt + 1); | ||
return build({ step, alt, oct: p.oct, dir: p.dir }); | ||
}; | ||
// interval numbers | ||
var IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7]; | ||
// interval qualities | ||
var IQ = "P m M m M P d P m M m M".split(" "); | ||
/** | ||
* Get the simplified version of an interval. | ||
* | ||
* Get interval name from semitones number. Since there are several interval | ||
* names for the same number, the name it's arbitraty, but deterministic. | ||
* | ||
* @function | ||
* @param {String|Array} interval - the interval to simplify | ||
* @return {String|Array} the simplified interval | ||
* | ||
* @param {Integer} num - the number of semitones (can be negative) | ||
* @return {String} the interval name | ||
* @example | ||
* interval.simplify('9M') // => '2M' | ||
* ['8P', '9M', '10M', '11P', '12P', '13M', '14M', '15P'].map(interval.simplify) | ||
* // => [ '8P', '2M', '3M', '4P', '5P', '6M', '7M', '8P' ] | ||
* interval.simplify('2M') // => '2M' | ||
* interval.simplify('-2M') // => '7m' | ||
* import { fromSemitones } from 'tonal-interval' | ||
* fromSemitones(7) // => '5P' | ||
* // or using tonal | ||
* tonal.fromSemitones(-7) // => '-5P' | ||
*/ | ||
export var simplify = ivlFn(function (i) { | ||
// decode to [step, alt, octave] | ||
var dec = decode(i) | ||
// if it's not 8 reduce the octaves to 0 | ||
if (dec[0] !== 0 || dec[2] !== 1) dec[2] = 0 | ||
// encode back | ||
return encode(dec[0], dec[1], dec[2], dir(i)) | ||
}) | ||
export const fromSemitones = num => { | ||
var d = num < 0 ? -1 : 1; | ||
var n = Math.abs(num); | ||
var c = n % 12; | ||
var o = Math.floor(n / 12); | ||
return d * (IN[c] + 7 * o) + IQ[c]; | ||
}; |
{ | ||
"name": "tonal-interval", | ||
"version": "0.69.9", | ||
"version": "1.0.0-0", | ||
"description": "Music interval creation and manipulation", | ||
@@ -20,6 +20,7 @@ "repository": "https://github.com/danigb/tonal/packages/interval", | ||
"license": "MIT", | ||
"dependencies": { | ||
"interval-notation": "^1.0.0", | ||
"tonal-pitch": "^0.69.7" | ||
"babel": { | ||
"presets": [ | ||
"es2015" | ||
] | ||
} | ||
} |
200
README.md
@@ -25,3 +25,3 @@ <a name="module_interval"></a> | ||
import * as interval from 'tonal-interval' | ||
// or var interval = require('tonal-interval') | ||
// or const interval = require('tonal-interval') | ||
interval.semitones('4P') // => 5 | ||
@@ -40,44 +40,48 @@ interval.invert('3m') // => '6M' | ||
* [interval](#module_interval) | ||
* [`.toInterval(interval)`](#module_interval.toInterval) ⇒ <code>String</code> | ||
* [`.props(interval)`](#module_interval.props) ⇒ <code>Object</code> | ||
* [`.num(interval)`](#module_interval.num) ⇒ <code>Integer</code> | ||
* [`.value(interval)`](#module_interval.value) ⇒ <code>Integer</code> | ||
* [`.props(interval)`](#module_interval.props) ⇒ <code>Array</code> | ||
* [`.fromProps(props)`](#module_interval.fromProps) ⇒ <code>String</code> | ||
* [`.name(interval)`](#module_interval.name) ⇒ <code>String</code> | ||
* [`.type(interval)`](#module_interval.type) ⇒ <code>String</code> | ||
* [`.semitones(ivl)`](#module_interval.semitones) ⇒ <code>Integer</code> | ||
* [`.chroma(str)`](#module_interval.chroma) ⇒ <code>Number</code> | ||
* [`.ic(interval)`](#module_interval.ic) ⇒ <code>Integer</code> | ||
* [`.build(props)`](#module_interval.build) ⇒ <code>String</code> | ||
* [`.simplify(interval)`](#module_interval.simplify) ⇒ <code>String</code> | ||
* [`.invert(interval)`](#module_interval.invert) ⇒ <code>String</code> | ||
* [`.fromSemitones(num)`](#module_interval.fromSemitones) ⇒ <code>String</code> | ||
* [`.ic(interval)`](#module_interval.ic) ⇒ <code>Integer</code> | ||
* [`.type(interval)`](#module_interval.type) ⇒ <code>String</code> | ||
* [`.invert(interval)`](#module_interval.invert) ⇒ <code>String</code> \| <code>Pitch</code> | ||
* [`.simplify(interval)`](#module_interval.simplify) ⇒ <code>String</code> \| <code>Array</code> | ||
<a name="module_interval.toInterval"></a> | ||
<a name="module_interval.props"></a> | ||
## `interval.toInterval(interval)` ⇒ <code>String</code> | ||
Get interval name. Can be used to test if it's an interval. It accepts intervals | ||
as pitch or string in shorthand notation or tonal notation. It returns always | ||
intervals in tonal notation. | ||
## `interval.props(interval)` ⇒ <code>Object</code> | ||
Get interval properties. It returns an object with: | ||
- name: name | ||
- num: number | ||
- q: quality | ||
- step: step | ||
- alt: alteration | ||
- dir: direction (1 ascending, -1 descending) | ||
- type: "P" or "M" for perfectable or majorable | ||
- simple: the simplified number | ||
- semitones: the size in semitones | ||
- chroma: the interval chroma | ||
- ic: the interval class | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>String</code> - the interval name or null if not valid interval | ||
**Returns**: <code>Object</code> - the interval in the form [number, alt] | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| interval | <code>String</code> \| <code>Pitch</code> | the interval string or array | | ||
| interval | <code>String</code> | the interval | | ||
**Example** | ||
```js | ||
interval.toInterval('m-3') // => '-3m' | ||
interval.toInterval('3') // => null | ||
``` | ||
<a name="module_interval.num"></a> | ||
## `interval.num(interval)` ⇒ <code>Integer</code> | ||
Get the number of the interval (same as value, but always positive) | ||
Get the number of the interval | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>Integer</code> - the positive interval number (P1 is 1, m2 is 2, ...) | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| interval | <code>String</code> \| <code>Pitch</code> | the interval | | ||
| interval | <code>String</code> | the interval | | ||
@@ -88,59 +92,41 @@ **Example** | ||
interval.num('P9') // => 9 | ||
interval.num('P-4') // => 4 | ||
interval.num('P-4') // => -4 | ||
``` | ||
<a name="module_interval.value"></a> | ||
<a name="module_interval.name"></a> | ||
## `interval.value(interval)` ⇒ <code>Integer</code> | ||
Get the interval value (the interval number, but positive or negative | ||
depending the interval direction) | ||
## `interval.name(interval)` ⇒ <code>String</code> | ||
Get interval name. Can be used to test if it's an interval. It accepts intervals | ||
as pitch or string in shorthand notation or tonal notation. It returns always | ||
intervals in tonal notation. | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>Integer</code> - the positive interval number (P1 is 1, m-2 is -2, ...) | ||
**Returns**: <code>String</code> - the interval name or null if not valid interval | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| interval | <code>String</code> \| <code>Pitch</code> | the interval | | ||
| interval | <code>String</code> | the interval string or array | | ||
**Example** | ||
```js | ||
interval.num('m2') // => 2 | ||
interval.num('m9') // => 9 | ||
interval.num('P-4') // => -4 | ||
interval.num('m-9') // => -9 | ||
interval.name('m-3') // => '-3m' | ||
interval.name('3') // => null | ||
``` | ||
<a name="module_interval.props"></a> | ||
<a name="module_interval.type"></a> | ||
## `interval.props(interval)` ⇒ <code>Array</code> | ||
Get interval properties. It returns an object with: | ||
## `interval.type(interval)` ⇒ <code>String</code> | ||
Get interval type. Can be perfectable (1, 4, 5) or majorable (2, 3, 6, 7) | ||
It does NOT return the actual quality. | ||
- num: the interval number (always positive) | ||
- alt: the interval alteration (0 for perfect in perfectables, or 0 for major in _majorables_) | ||
- dir: the interval direction (1 ascending, -1 descending) | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>Array</code> - the interval in the form [number, alt] | ||
**Returns**: <code>String</code> - 'P' for perfectables, 'M' for majorables or null if not | ||
valid interval | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| interval | <code>String</code> \| <code>Pitch</code> | the interval | | ||
| Param | Type | | ||
| --- | --- | | ||
| interval | <code>String</code> | | ||
**Example** | ||
```js | ||
interval.parse('m2') // => { num: 2, alt: -1, dir: 1 } | ||
interval.parse('m9') // => { num: 9, alt: -1, dir: 1 } | ||
interval.parse('P-4') // => { num: 4, alt: 0, dir: -1} | ||
interval.parse('m-9') // => { num: 9, alt: -1, dir: -1 } | ||
interval.type('5A') // => 'P' | ||
``` | ||
<a name="module_interval.fromProps"></a> | ||
## `interval.fromProps(props)` ⇒ <code>String</code> | ||
Given a interval property object, get the interval name | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>String</code> - the interval name | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| props | <code>Object</code> | the interval property object - num: the interval number - alt: the interval alteration - dir: the direction | | ||
<a name="module_interval.semitones"></a> | ||
@@ -156,3 +142,3 @@ | ||
| --- | --- | | ||
| ivl | <code>String</code> \| <code>Pitch</code> | | ||
| ivl | <code>String</code> | | ||
@@ -164,24 +150,16 @@ **Example** | ||
// or using tonal | ||
tonal.semitones('P5') // => 7 | ||
tonal.interval.semitones('P5') // => 7 | ||
``` | ||
<a name="module_interval.fromSemitones"></a> | ||
<a name="module_interval.chroma"></a> | ||
## `interval.fromSemitones(num)` ⇒ <code>String</code> | ||
Get interval name from semitones number. Since there are several interval | ||
names for the same number, the name it's arbitraty, but deterministic. | ||
## `interval.chroma(str)` ⇒ <code>Number</code> | ||
Get the chroma of the interval. The chroma is a number between 0 and 7 | ||
that represents the position within an octave (pitch set) | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>String</code> - the interval name | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| num | <code>Integer</code> | the number of semitones (can be negative) | | ||
| Param | Type | | ||
| --- | --- | | ||
| str | <code>String</code> | | ||
**Example** | ||
```js | ||
import { fromSemitones } from 'tonal-interval' | ||
fromSemitones(7) // => '5P' | ||
// or using tonal | ||
tonal.fromSemitones(-7) // => '-5P' | ||
``` | ||
<a name="module_interval.ic"></a> | ||
@@ -212,23 +190,41 @@ | ||
``` | ||
<a name="module_interval.type"></a> | ||
<a name="module_interval.build"></a> | ||
## `interval.type(interval)` ⇒ <code>String</code> | ||
Get interval type. Can be perfectable (1, 4, 5) or majorable (2, 3, 6, 7) | ||
It does NOT return the actual quality. | ||
## `interval.build(props)` ⇒ <code>String</code> | ||
Given a interval property object, get the interval name | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>String</code> - 'P' for perfectables, 'M' for majorables or null if not | ||
valid interval | ||
**Returns**: <code>String</code> - the interval name | ||
| Param | Type | | ||
| --- | --- | | ||
| interval | <code>String</code> \| <code>Pitch</code> | | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| props | <code>Object</code> | the interval property object - num: the interval number - alt: the interval alteration - oct: the number of octaves - dir: the direction | | ||
**Example** | ||
```js | ||
interval.type('5A') // => 'P' | ||
interval.build({ step: 1, alt: -1, oct: 0, dir: 1 }) // => "1d" | ||
``` | ||
<a name="module_interval.simplify"></a> | ||
## `interval.simplify(interval)` ⇒ <code>String</code> | ||
Get the simplified version of an interval. | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>String</code> - the simplified interval | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| interval | <code>String</code> | the interval to simplify | | ||
**Example** | ||
```js | ||
interval.simplify('9M') // => '2M' | ||
['8P', '9M', '10M', '11P', '12P', '13M', '14M', '15P'].map(interval.simplify) | ||
// => [ '8P', '2M', '3M', '4P', '5P', '6M', '7M', '8P' ] | ||
interval.simplify('2M') // => '2M' | ||
interval.simplify('-2M') // => '7m' | ||
``` | ||
<a name="module_interval.invert"></a> | ||
## `interval.invert(interval)` ⇒ <code>String</code> \| <code>Pitch</code> | ||
## `interval.invert(interval)` ⇒ <code>String</code> | ||
Get the inversion (https://en.wikipedia.org/wiki/Inversion_(music)#Intervals) | ||
@@ -238,7 +234,7 @@ of an interval. | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>String</code> \| <code>Pitch</code> - the inverted interval | ||
**Returns**: <code>String</code> - the inverted interval | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| interval | <code>String</code> \| <code>Pitch</code> | the interval to invert in interval shorthand notation or interval array notation | | ||
| interval | <code>String</code> | the interval to invert in interval shorthand notation or interval array notation | | ||
@@ -250,21 +246,21 @@ **Example** | ||
``` | ||
<a name="module_interval.simplify"></a> | ||
<a name="module_interval.fromSemitones"></a> | ||
## `interval.simplify(interval)` ⇒ <code>String</code> \| <code>Array</code> | ||
Get the simplified version of an interval. | ||
## `interval.fromSemitones(num)` ⇒ <code>String</code> | ||
Get interval name from semitones number. Since there are several interval | ||
names for the same number, the name it's arbitraty, but deterministic. | ||
**Kind**: static method of [<code>interval</code>](#module_interval) | ||
**Returns**: <code>String</code> \| <code>Array</code> - the simplified interval | ||
**Returns**: <code>String</code> - the interval name | ||
| Param | Type | Description | | ||
| --- | --- | --- | | ||
| interval | <code>String</code> \| <code>Array</code> | the interval to simplify | | ||
| num | <code>Integer</code> | the number of semitones (can be negative) | | ||
**Example** | ||
```js | ||
interval.simplify('9M') // => '2M' | ||
['8P', '9M', '10M', '11P', '12P', '13M', '14M', '15P'].map(interval.simplify) | ||
// => [ '8P', '2M', '3M', '4P', '5P', '6M', '7M', '8P' ] | ||
interval.simplify('2M') // => '2M' | ||
interval.simplify('-2M') // => '7m' | ||
import { fromSemitones } from 'tonal-interval' | ||
fromSemitones(7) // => '5P' | ||
// or using tonal | ||
tonal.fromSemitones(-7) // => '-5P' | ||
``` |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
33913
0
6
717
259
1
- Removedinterval-notation@^1.0.0
- Removedtonal-pitch@^0.69.7
- Removedinterval-notation@1.0.1(transitive)
- Removednote-parser@2.0.1(transitive)
- Removedtonal-encoding@0.69.7(transitive)
- Removedtonal-pitch@0.69.7(transitive)