UNPKG

fretboard-api

Version:

A kind of API for fretboard diagrams

1,896 lines (1,855 loc) 66.9 kB
//TODO: move these types into index /** * * @param shape The Shape */ function firstPlayedString(shape) { var n = shape.frets.findIndex(function (string) { return string != null && string.length > 0; }); if (n < 0) throw new Error("Invalid shape, at least one string must be played."); return n; } /** * Returns first played fret (non null) * @param shape The Shape */ function firstPlayedFret(shape) { // const firstString = firstPlayedString(shape); // if (firstString < 0) throw new Error("Invalid shape, at least a string must be played."); var fs = shape.frets[firstPlayedString(shape)]; var f = fs ? fs.find(function (fret) { return fret !== null; }) : undefined; if (f == undefined) { throw new Error('no played position found'); } else { return f; } } /** * In string definition, use 'X' to denote a non-played string. * In number[] definition, use 'null' to denote a non-played string. * "022100" --> [[0], [2], [2], [1], [0], [0]] * "8 10 10 9 8 8" --> [[8], [10], [10], [9], [8], [8]] * [8, 10, 10, 9, 8, 8] --> [[8], [10], [10], [9], [8], [8]] * "24,124,134,134,24,12" --> [[2, 4], [1, 2, 4], [1, 3, 4], [1, 3, 4], [2, 4], [1, 2]] * "8 10, 7 8 10, 7 9 10, 7 9 10, 8 10, 7 8" --> [[8, 10], [7, 8, 10], [7, 9, 10], [7, 9, 10], [8, 10], [7, 8]] * @param {?(array|string)} frets - frets. * @return {array} array of fret numbers. */ // export function normalizeInputFormat(frets: string): Fret[][]|undefined; // export function normalizeInputFormat(frets: number[]): Fret[][]|undefined; function normalizeInputFormat(frets) { //TODO: add formats: // // "---010" --> [[], [], [], [0], [1], [0]] // // // if (!frets) { //TODO: throw an error instead? return []; } if (Array.isArray(frets)) { return frets.map(function (s) { return s == null ? null : [s]; }); } // replace multiples blanks with one space: var fs = frets.toUpperCase().replace(/\s+/g, ' '); if (fs.indexOf(',') < 0) { // only one fretted note per string var a = fs.indexOf(' ') >= 0 ? fs.split(' ') // "8 10 ..." --> ["8", "10", ...] : Array.from(fs); // "022100" --> ["0", "2", "2", "1", "0", "0"] return a.map(function (s) { if (s.toUpperCase() === 'X') return null; // value "null" means non-played string return [parseInt(s, 10)]; }); } else { // more than one fretted note per string var a = fs.replace(/,\s*/g, ',').split(','); // "8 10, 7 8 10, ..." --> ["8 10", "7 8 10", ...] return a.map(function (s) { if (s.toUpperCase() === 'X') return null; // value "null" means non-played string return (s.indexOf(' ') >= 0 ? s.split(' ') // ["8 10", ...] --> [["8", "10"], ...] : Array.from(s) // ["24", "124", ...] --> [["2", "4"], ["1", "2", "4"], ...] ).map(function (s) { if (s.toUpperCase() === 'X') throw new Error('invalid format'); return parseInt(s, 10); }); }); } } /** * * @param fingers */ function normalizeFingers(fingers) { // if (typeof frets !== 'string') return frets; // if ((frets === undefined) || (frets === null)) { //FIXME: simplify this test if (!fingers) { //TODO: throw an error instead? return []; } if (Array.isArray(fingers)) { return fingers.map(function (s) { return s == null ? null : [s]; }); } var fs = fingers.toUpperCase().replace(/\s+/g, ' '); // replace multiples blanks with one space if (fs.indexOf(',') < 0) { // only one fretted note per string var a = fs.indexOf(' ') >= 0 ? fs.split(' ') // "8 10 ..." --> ["8", "10", ...] : Array.from(fs); // "022100" --> ["0", "2", "2", "1", "0", "0"] return a.map(function (s) { if (s.toUpperCase() === 'X') return null; // value 0 means non-played string // if (s === '-') return []; // nothing played on this string return [parseInt(s, 10)]; }); } else { var a = fs.replace(/,\s*/g, ',').split(','); // "8 10, 7 8 10, ..." --> ["8 10", "7 8 10", ...] return a.map(function (s) { if (s.toUpperCase() === 'X') return [0]; // if (s === '' || s === '-') return []; // nothing played on this string return (s.indexOf(' ') >= 0 ? s.split(' ') // ["8 10", ...] --> [["8", "10"], ...] : Array.from(s) // ["24", "124", ...] --> [["2", "4"], ["1", "2", "4"], ...] ).map(function (s) { if (s.toUpperCase() === 'X') throw new Error('invalid format'); // if (s === '-') throw new Error('invalid format'); return parseInt(s, 10); }); }); } } function create(shape) { var newShape = { frets: [null], root: { string: 0, fret: 0 }, position: { string: 0, fret: 0 }, intervals: [], notes: [], fromFret: 0, toFret: 0 }; // if (typeof shape === 'string') { // we use two distinct tests to help Typescript. // // o.frets = normalizeInputFormat(shape); // // o.frets = [[0], [2], [2], [1], [0], [0]]; // // o.fingers = null; // o.root = {string: 0, fret: 0}; // // } else if (Array.isArray(shape)) { if ((typeof shape === 'string') || Array.isArray(shape)) { newShape.frets = normalizeInputFormat(shape); // console.log(JSON.stringify(s)); // o.frets = [[0], [2], [2], [1], [0], [0]]; // o.fingers = null; // o.root = {string: 0, fret: 0}; } else if (typeof shape === 'object') { // Assert.hasProperty('frets', shape); // if (shape.frets) { // o.frets = normalizeInputFormat(shape.frets); // } // o.fingers = normalizeInputFormat(shape.fingers); newShape = { // ...s, frets: normalizeInputFormat(shape.frets), fingers: normalizeFingers(shape.fingers), position: shape.position, root: shape.root, intervals: [], notes: [], fromFret: 0, toFret: 0 }; // ['frets', 'fingers'].map(p => o[p] = normalizeInputFormat(shape[p])); // ['position', 'root'].map(p => o[p] = shape[p]); // } else { // throw new Error("InvalidArgumentException"); // TODO: return a more helpful message } // console.log(o.frets); if (!newShape.frets || (newShape.frets.length === 0)) { // this allows the creation of empty shapes return newShape; } // if we did not supply the root definition, compute it: if (!shape.root) { // by default takes the first fretted note on the first played string var firstString = firstPlayedString(newShape); if (firstString < 0) throw new Error("Invalid shape, at least a string must be played."); // console.log("firstString", firstString); newShape.root = { string: firstString, // fret: firstPlayedFret(o.frets[firstString]) fret: firstPlayedFret(newShape) }; // console.log("root", JSON.stringify(s)); } // if we did not supply the position definition, compute it: if (!shape.position) { newShape.position = { string: newShape.root.string, fret: newShape.root.fret }; } // compute min fret: newShape.fromFret = newShape.root.fret; // we need to start somewhere for (var s = 0; s < newShape.frets.length; s++) { // for each string var f = newShape.frets[s]; if (!f) continue; for (var i = 0; i < f.length; i++) { // for each fret if (f[i] < newShape.fromFret) { newShape.fromFret = f[i]; } } } // compute max fret: newShape.toFret = 0; for (var s = 0; s < newShape.frets.length; s++) { // for each string var f = newShape.frets[s]; if (!f) continue; for (var i = 0; i < f.length; i++) { // for each fret if (f[i] > newShape.toFret) { newShape.toFret = f[i]; } } } return newShape; } /** * * @param shape * @returns {*} */ function getFretPosition(shape) { if (shape.position === undefined || shape.position === null) { var s = shape.frets[firstPlayedString(shape)]; // const s0 = s[0]; if (s == null) { // to make typescript happy return 0; } else { return s[0]; } // // @ts-ignore // return s[0]; } else { return shape.position.fret; } } var Shape = { create: create, normalizeInputFormat: normalizeInputFormat, // normalizeFingers, getFretPosition: getFretPosition // used in Fretboard }; var bass4 = { standard: ['E1', 'A1', 'D2', 'G2'], drop_d: ['D1', 'A1', 'D2', 'G2'] }; var guitar6 = { standard: ['E2', 'A2', 'D3', 'G3', 'B3', 'E4'], standard_d: ['D2', 'G2', 'C3', 'F3', 'A3', 'D4'], drop_d: ['D2', 'A2', 'D3', 'G3', 'B3', 'E4'], drop_c: ['C2', 'G2', 'C3', 'F3', 'A3', 'D4'], double_drop_d: ['D2', 'A2', 'D3', 'G3', 'B3', 'D4'], dadgad: ['D2', 'A2', 'D3', 'G3', 'A3', 'D4'] }; var bass = bass4; var guitar = guitar6; var Tuning = { bass: bass, bass4: bass4, guitar: guitar, guitar6: guitar6 }; /* export const Tunings: { [name: string]: TuningType } = { bass4: { standard : ['E1', 'A1', 'D2', 'G2'], drop_d : ['D1', 'A1', 'D2', 'G2'] }, bass5: { standard : ['B0', 'E1', 'A1', 'D2', 'G2'], tenor : ['E1', 'A1', 'D2', 'G2', 'C3'] }, bass6: { standard : ['B0', 'E1', 'A1', 'D2', 'G2', 'C3'] }, guitar6: { standard : ['E2', 'A2', 'D3', 'G3', 'B3', 'E4'], standard_d : ['D2', 'G2', 'C3', 'F3', 'A3', 'D4'], drop_d : ['D2', 'A2', 'D3', 'G3', 'B3', 'E4'], drop_c : ['C2', 'G2', 'C3', 'F3', 'A3', 'D4'], double_drop_d : ['D2', 'A2', 'D3', 'G3', 'B3', 'D4'], dadgad : ['D2', 'A2', 'D3', 'G3', 'A3', 'D4'] }, guitar7: { // 7 strings guitar standard : ['B1', 'E2', 'A2', 'D3', 'G3', 'B3', 'E4'], drop_a : ['A1', 'E2', 'A2', 'D3', 'G3', 'B3', 'E4'] }, guitar8: { // 8 strings guitar standard : ['F#1', 'B1', 'E2', 'A2', 'D3', 'G3', 'B3', 'E4'], drop_e : ['E1', 'B1', 'E2', 'A2', 'D3', 'G3', 'B3', 'E4'] } }; Tunings['bass'] = Tunings.bass4; Tunings['guitar'] = Tunings.guitar6; */ // https://en.wikipedia.org/wiki/List_of_guitar_tunings // https://en.wikibooks.org/wiki/Guitar/Alternate_Tunings function n(n){for(var t=arguments.length,r=Array(t>1?t-1:0),e=1;e<t;e++)r[e-1]=arguments[e];if("production"!==process.env.NODE_ENV){var i=H[n],o=i?"function"==typeof i?i.apply(null,r):i:"unknown error nr: "+n;throw Error("[Immer] "+o)}throw Error("[Immer] minified error nr: "+n+(r.length?" "+r.join(","):"")+". Find the full error at: https://bit.ly/3cXEKWf")}function t(n){return !!n&&!!n[B]}function r(n){return !!n&&(function(n){if(!n||"object"!=typeof n)return !1;var t=Object.getPrototypeOf(n);return !t||t===Object.prototype}(n)||Array.isArray(n)||!!n[q]||!!n.constructor[q]||c(n)||s(n))}function i(n,t){0===o(n)?L(n).forEach((function(r){return t(r,n[r],n)})):n.forEach((function(r,e){return t(e,r,n)}));}function o(n){var t=n[B];return t?t.i>3?t.i-4:t.i:Array.isArray(n)?1:c(n)?2:s(n)?3:0}function u(n,t){return 2===o(n)?n.has(t):Object.prototype.hasOwnProperty.call(n,t)}function a(n,t){return 2===o(n)?n.get(t):n[t]}function f(n,t){return n===t?0!==n||1/n==1/t:n!=n&&t!=t}function c(n){return $&&n instanceof Map}function s(n){return U&&n instanceof Set}function v(n){return n.o||n.t}function p(t,r){if(void 0===r&&(r=!1),Array.isArray(t))return t.slice();var e=Object.create(Object.getPrototypeOf(t));return i(t,(function(i){if(i!==B){var o=Object.getOwnPropertyDescriptor(t,i),u=o.value;o.get&&(r||n(1),u=o.get.call(t)),o.enumerable?e[i]=u:Object.defineProperty(e,i,{value:u,writable:!0,configurable:!0});}})),e}function d(n,e){t(n)||Object.isFrozen(n)||!r(n)||(o(n)>1&&(n.set=n.add=n.clear=n.delete=l),Object.freeze(n),e&&i(n,(function(n,t){return d(t,!0)})));}function l(){n(2);}function h(t){var r=Q[t];return r||n("production"!==process.env.NODE_ENV?18:19,t),r}function m(){return "production"===process.env.NODE_ENV||J||n(0),J}function b(n,t){t&&(h("Patches"),n.u=[],n.s=[],n.v=t);}function _(n){j(n),n.p.forEach(g),n.p=null;}function j(n){n===J&&(J=n.l);}function O(n){return J={p:[],l:J,h:n,m:!0,_:0}}function g(n){var t=n[B];0===t.i||1===t.i?t.j():t.O=!0;}function w(t,e){e._=e.p.length;var i=e.p[0],o=void 0!==t&&t!==i;return e.h.g||h("ES5").S(e,t,o),o?(i[B].P&&(_(e),n(4)),r(t)&&(t=S(e,t),e.l||M(e,t)),e.u&&h("Patches").M(i[B],t,e.u,e.s)):t=S(e,i,[]),_(e),e.u&&e.v(e.u,e.s),t!==X?t:void 0}function S(n,t,r){if(Object.isFrozen(t))return t;var e=t[B];if(!e)return i(t,(function(i,o){return P(n,e,t,i,o,r)})),t;if(e.A!==n)return t;if(!e.P)return M(n,e.t,!0),e.t;if(!e.I){e.I=!0,e.A._--;var o=4===e.i||5===e.i?e.o=p(e.k,!0):e.o;i(o,(function(t,i){return P(n,e,o,t,i,r)})),M(n,o,!1),r&&n.u&&h("Patches").R(e,r,n.u,n.s);}return e.o}function P(e,i,c,s,v,p){if("production"!==process.env.NODE_ENV&&v===c&&n(5),t(v)){var d=S(e,v,p&&i&&3!==i.i&&!u(i.D,s)?p.concat(s):void 0);if(h=s,y=d,2===(m=o(l=c))?l.set(h,y):3===m?(l.delete(h),l.add(y)):l[h]=y,!t(d))return;e.m=!1;}var l,h,y,m;if((!i||!f(v,a(i.t,s)))&&r(v)){if(!e.h.N&&e._<1)return;S(e,v),i&&i.A.l||M(e,v);}}function M(n,t,r){void 0===r&&(r=!1),n.h.N&&n.m&&d(t,r);}function A(n,t){var r=n[B],e=Reflect.getOwnPropertyDescriptor(r?v(r):n,t);return e&&e.value}function z(n){if(!n.P){if(n.P=!0,0===n.i||1===n.i){var t=n.o=p(n.t);i(n.p,(function(n,r){t[n]=r;})),n.p=void 0;}n.l&&z(n.l);}}function x(n){n.o||(n.o=p(n.t));}function I(n,t,r){var e=c(t)?h("MapSet").T(t,r):s(t)?h("MapSet").F(t,r):n.g?function(n,t){var r=Array.isArray(n),e={i:r?1:0,A:t?t.A:m(),P:!1,I:!1,D:{},l:t,t:n,k:null,p:{},o:null,j:null,C:!1},i=e,o=V;r&&(i=[e],o=Y);var u=Proxy.revocable(i,o),a=u.revoke,f=u.proxy;return e.k=f,e.j=a,f}(t,r):h("ES5").J(t,r);return (r?r.A:m()).p.push(e),e}var C,J,K="undefined"!=typeof Symbol,$="undefined"!=typeof Map,U="undefined"!=typeof Set,W="undefined"!=typeof Proxy&&void 0!==Proxy.revocable&&"undefined"!=typeof Reflect,X=K?Symbol("immer-nothing"):((C={})["immer-nothing"]=!0,C),q=K?Symbol("immer-draftable"):"__$immer_draftable",B=K?Symbol("immer-state"):"__$immer_state",H={0:"Illegal state",1:"Immer drafts cannot have computed properties",2:"This object has been frozen and should not be mutated",3:function(n){return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? "+n},4:"An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.",5:"Immer forbids circular references",6:"The first or second argument to `produce` must be a function",7:"The third argument to `produce` must be a function or undefined",8:"First argument to `createDraft` must be a plain object, an array, or an immerable object",9:"First argument to `finishDraft` must be a draft returned by `createDraft`",10:"The given draft is already finalized",11:"Object.defineProperty() cannot be used on an Immer draft",12:"Object.setPrototypeOf() cannot be used on an Immer draft",13:"Immer only supports deleting array indices",14:"Immer only supports setting array indices and the 'length' property",15:function(n){return "Cannot apply patch, path doesn't resolve: "+n},16:'Sets cannot have "replace" patches.',17:function(n){return "Unsupported patch operation: "+n},18:function(n){return "The plugin for '"+n+"' has not been loaded into Immer. To enable the plugin, import and call `enable"+n+"()` when initializing your application."},19:"plugin not loaded",20:"Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available"},L="undefined"!=typeof Reflect&&Reflect.ownKeys?Reflect.ownKeys:void 0!==Object.getOwnPropertySymbols?function(n){return Object.getOwnPropertyNames(n).concat(Object.getOwnPropertySymbols(n))}:Object.getOwnPropertyNames,Q={},V={get:function(n,t){if(t===B)return n;var e=n.p;if(!n.P&&u(e,t))return e[t];var i=v(n)[t];if(n.I||!r(i))return i;if(n.P){if(i!==A(n.t,t))return i;e=n.o;}return e[t]=I(n.A.h,i,n)},has:function(n,t){return t in v(n)},ownKeys:function(n){return Reflect.ownKeys(v(n))},set:function(n,t,r){if(!n.P){var e=A(n.t,t);if(r?f(e,r)||r===n.p[t]:f(e,r)&&t in n.t)return !0;x(n),z(n);}return n.D[t]=!0,n.o[t]=r,!0},deleteProperty:function(n,t){return void 0!==A(n.t,t)||t in n.t?(n.D[t]=!1,x(n),z(n)):n.D[t]&&delete n.D[t],n.o&&delete n.o[t],!0},getOwnPropertyDescriptor:function(n,t){var r=v(n),e=Reflect.getOwnPropertyDescriptor(r,t);return e&&(e.writable=!0,e.configurable=1!==n.i||"length"!==t),e},defineProperty:function(){n(11);},getPrototypeOf:function(n){return Object.getPrototypeOf(n.t)},setPrototypeOf:function(){n(12);}},Y={};i(V,(function(n,t){Y[n]=function(){return arguments[0]=arguments[0][0],t.apply(this,arguments)};})),Y.deleteProperty=function(t,r){return "production"!==process.env.NODE_ENV&&isNaN(parseInt(r))&&n(13),V.deleteProperty.call(this,t[0],r)},Y.set=function(t,r,e){return "production"!==process.env.NODE_ENV&&"length"!==r&&isNaN(parseInt(r))&&n(14),V.set.call(this,t[0],r,e,t[0])};var Z=function(){function e(n){this.g=W,this.N="production"!==process.env.NODE_ENV,"boolean"==typeof(null==n?void 0:n.useProxies)&&this.setUseProxies(n.useProxies),"boolean"==typeof(null==n?void 0:n.autoFreeze)&&this.setAutoFreeze(n.autoFreeze),this.produce=this.produce.bind(this),this.produceWithPatches=this.produceWithPatches.bind(this);}var i=e.prototype;return i.produce=function(t,e,i){if("function"==typeof t&&"function"!=typeof e){var o=e;e=t;var u=this;return function(n){var t=this;void 0===n&&(n=o);for(var r=arguments.length,i=Array(r>1?r-1:0),a=1;a<r;a++)i[a-1]=arguments[a];return u.produce(n,(function(n){var r;return (r=e).call.apply(r,[t,n].concat(i))}))}}var a;if("function"!=typeof e&&n(6),void 0!==i&&"function"!=typeof i&&n(7),r(t)){var f=O(this),c=I(this,t,void 0),s=!0;try{a=e(c),s=!1;}finally{s?_(f):j(f);}return "undefined"!=typeof Promise&&a instanceof Promise?a.then((function(n){return b(f,i),w(n,f)}),(function(n){throw _(f),n})):(b(f,i),w(a,f))}if((a=e(t))!==X)return void 0===a&&(a=t),this.N&&d(a,!0),a},i.produceWithPatches=function(n,t){var r,e,i=this;return "function"==typeof n?function(t){for(var r=arguments.length,e=Array(r>1?r-1:0),o=1;o<r;o++)e[o-1]=arguments[o];return i.produceWithPatches(t,(function(t){return n.apply(void 0,[t].concat(e))}))}:[this.produce(n,t,(function(n,t){r=n,e=t;})),r,e]},i.createDraft=function(t){r(t)||n(8);var e=O(this),i=I(this,t,void 0);return i[B].C=!0,j(e),i},i.finishDraft=function(t,r){var e=t&&t[B];"production"!==process.env.NODE_ENV&&(e&&e.C||n(9),e.I&&n(10));var i=e.A;return b(i,r),w(void 0,i)},i.setAutoFreeze=function(n){this.N=n;},i.setUseProxies=function(t){W||n(20),this.g=t;},i.applyPatches=function(n,r){var e;for(e=r.length-1;e>=0;e--){var i=r[e];if(0===i.path.length&&"replace"===i.op){n=i.value;break}}var o=h("Patches").U;return t(n)?o(n,r):this.produce(n,(function(n){return o(n,r.slice(e+1))}))},e}(),nn=new Z,tn=nn.produce,rn=nn.produceWithPatches.bind(nn),en=nn.setAutoFreeze.bind(nn),on=nn.setUseProxies.bind(nn),un=nn.applyPatches.bind(nn),an=nn.createDraft.bind(nn),fn=nn.finishDraft.bind(nn); var NAMES = "C C# Db D D# Eb E F F# Gb G G# Ab A A# Bb B".split(" "); var names = function (accTypes) { return typeof accTypes !== "string" ? NAMES.slice() : NAMES.filter(function (n) { var acc = n[1] || " "; return accTypes.indexOf(acc) !== -1; }); }; var SHARPS = names(" #"); var FLATS = names(" b"); var REGEX = /^([a-gA-G]?)(#{1,}|b{1,}|x{1,}|)(-?\d*)\s*(.*)$/; function tokenize(str) { if (typeof str !== "string") str = ""; var m = REGEX.exec(str); return [m[1].toUpperCase(), m[2].replace(/x/g, "##"), m[3], m[4]]; } var NO_NOTE = Object.freeze({ pc: null, name: null, step: null, alt: null, oct: null, octStr: null, chroma: null, midi: null, freq: null }); var SEMI = [0, 2, 4, 5, 7, 9, 11]; var properties = function (str) { var tokens = tokenize(str); if (tokens[0] === "" || tokens[3] !== "") return NO_NOTE; var letter = tokens[0], acc = tokens[1], octStr = tokens[2]; var p = { letter: letter, acc: acc, octStr: octStr, pc: letter + acc, name: letter + acc + octStr, step: (letter.charCodeAt(0) + 3) % 7, alt: acc[0] === "b" ? -acc.length : acc.length, oct: octStr.length ? +octStr : null, chroma: 0, midi: null, freq: null }; p.chroma = (SEMI[p.step] + p.alt + 120) % 12; p.midi = p.oct !== null ? SEMI[p.step] + p.alt + 12 * (p.oct + 1) : null; p.freq = midiToFreq(p.midi); return Object.freeze(p); }; var memo = function (fn, cache) { if (cache === void 0) { cache = {}; } return function (str) { return cache[str] || (cache[str] = fn(str)); }; }; var props = memo(properties); var pc = function (str) { return props(str).pc; }; var midiToFreq = function (midi, tuning) { if (tuning === void 0) { tuning = 440; } return typeof midi === "number" ? Math.pow(2, (midi - 69) / 12) * tuning : null; }; var chroma = function (str) { return props(str).chroma; }; var LETTERS = "CDEFGAB"; var stepToLetter = function (step) { return LETTERS[step]; }; var fillStr = function (s, n) { return Array(n + 1).join(s); }; var numToStr = function (num, op) { return typeof num !== "number" ? "" : op(num); }; var altToAcc = function (alt) { return numToStr(alt, function (alt) { return (alt < 0 ? fillStr("b", -alt) : fillStr("#", alt)); }); }; var from = function (fromProps, baseNote) { if (fromProps === void 0) { fromProps = {}; } if (baseNote === void 0) { baseNote = null; } var _a = baseNote ? Object.assign({}, props(baseNote), fromProps) : fromProps, step = _a.step, alt = _a.alt, oct = _a.oct; if (typeof step !== "number") return null; var letter = stepToLetter(step); if (!letter) return null; var pc = letter + altToAcc(alt); return oct || oct === 0 ? pc + oct : pc; }; var build = from; function fromMidi(num, sharps) { if (sharps === void 0) { sharps = false; } num = Math.round(num); var pcs = sharps === true ? SHARPS : FLATS; var pc = pcs[num % 12]; var o = Math.floor(num / 12) - 1; return pc + o; } var simplify = function (note, sameAcc) { if (sameAcc === void 0) { sameAcc = true; } var _a = props(note), alt = _a.alt, chroma = _a.chroma, midi = _a.midi; if (chroma === null) return null; var alteration = alt; var useSharps = sameAcc === false ? alteration < 0 : alteration > 0; return midi === null ? pc(fromMidi(chroma, useSharps)) : fromMidi(midi, useSharps); }; var IVL_TNL = "([-+]?\\d+)(d{1,4}|m|M|P|A{1,4})"; var IVL_STR = "(AA|A|P|M|m|d|dd)([-+]?\\d+)"; var REGEX$1 = new RegExp("^" + IVL_TNL + "|" + IVL_STR + "$"); var SIZES = [0, 2, 4, 5, 7, 9, 11]; var TYPES = "PMMPPMM"; var CLASSES = [0, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]; var tokenize$1 = function (str) { var m = REGEX$1.exec("" + str); if (m === null) return null; return (m[1] ? [m[1], m[2]] : [m[4], m[3]]); }; var NO_IVL = Object.freeze({ name: null, num: null, q: null, step: null, alt: null, dir: null, type: null, simple: null, semitones: null, chroma: null, oct: null }); var fillStr$1 = function (s, n) { return Array(Math.abs(n) + 1).join(s); }; var qToAlt = function (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; }; var altToQ = function (type, alt) { if (alt === 0) return type === "M" ? "M" : "P"; else if (alt === -1 && type === "M") return "m"; else if (alt > 0) return fillStr$1("A", alt); else if (alt < 0) return fillStr$1("d", type === "P" ? alt : alt + 1); else return null; }; var numToStep = function (num) { return (Math.abs(num) - 1) % 7; }; var properties$1 = function (str) { var t = tokenize$1(str); if (t === null) return NO_IVL; var p = { num: 0, q: "d", name: "", type: "M", step: 0, dir: -1, simple: 1, alt: 0, oct: 0, semitones: 0, chroma: 0, ic: 0 }; p.num = +t[0]; p.q = t[1]; p.step = numToStep(p.num); 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); return Object.freeze(p); }; var cache = {}; function props$1(str) { if (typeof str !== "string") return NO_IVL; return cache[str] || (cache[str] = properties$1(str)); } var chroma$1 = function (str) { return props$1(str).chroma; }; var ic = function (ivl) { if (typeof ivl === "string") ivl = props$1(ivl).chroma; return typeof ivl === "number" ? CLASSES[ivl % 12] : null; }; var build$1 = function (_a) { var _b = _a === void 0 ? {} : _a, num = _b.num, step = _b.step, alt = _b.alt, _c = _b.oct, oct = _c === void 0 ? 1 : _c, dir = _b.dir; if (step !== undefined) num = step + 1 + 7 * oct; if (num === undefined) return null; if (typeof alt !== "number") return null; var d = typeof dir !== "number" ? "" : dir < 0 ? "-" : ""; var type = TYPES[numToStep(num)]; return (d + num + altToQ(type, alt)); }; var simplify$1 = function (str) { var p = props$1(str); if (p === NO_IVL) return null; var intervalProps = p; return intervalProps.simple + intervalProps.q; }; var IN = [1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7]; var IQ = "P m M m M P d P m M m M".split(" "); var fromSemitones = function (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]; }; /** * [![npm version](https://img.shields.io/npm/v/tonal-distance.svg)](https://www.npmjs.com/package/tonal-distance) * [![tonal](https://img.shields.io/badge/tonal-distance-yellow.svg)](https://github.com/danigb/tonal/tree/master/packages/tonal/distance) * * Transpose notes by intervals and find distances between notes * * @example * // es6 * import * as Distance from "tonal-distance" * Distance.interval("C3", "C4") // => "1P" * * @example * // es6 import selected functions * import { interval, semitones, transpose } from "tonal-distance" * * semitones("C" ,"D") // => 2 * interval("C4", "G4") // => "5P" * transpose("C4", "P5") // => "G4" * * @example * // included in tonal facade * const Tonal = require("tonal"); * Tonal.Distance.transpose("C4", "P5") * Tonal.Distance.transposeBy("P5", "C4") * * @module Distance */ // Map from letter step to number of fifths starting from "C": // { C: 0, D: 2, E: 4, F: -1, G: 1, A: 3, B: 5 } var FIFTHS = [0, 2, 4, -1, 1, 3, 5]; // Given a number of fifths, return the octaves they span var fOcts = function (f) { return Math.floor((f * 7) / 12); }; // Get the number of octaves it span each step var FIFTH_OCTS = FIFTHS.map(fOcts); var encode = function (ref) { var step = ref.step; var alt = ref.alt; var oct = ref.oct; var dir = ref.dir; if ( dir === void 0 ) dir = 1; var f = FIFTHS[step] + 7 * alt; if (oct === null) { return [dir * f]; } var o = oct - FIFTH_OCTS[step] - 4 * alt; return [dir * f, dir * o]; }; // We need to get the steps from fifths // Fifths for CDEFGAB are [ 0, 2, 4, -1, 1, 3, 5 ] // We add 1 to fifths to avoid negative numbers, so: // for ["F", "C", "G", "D", "A", "E", "B"] we have: var STEPS = [3, 0, 4, 1, 5, 2, 6]; // Return the number of fifths as if it were unaltered function unaltered(f) { var i = (f + 1) % 7; return i < 0 ? 7 + i : i; } var decode = function (f, o, dir) { var step = STEPS[unaltered(f)]; var alt = Math.floor((f + 1) / 7); if (o === undefined) { return { step: step, alt: alt, dir: dir }; } var oct = o + 4 * alt + FIFTH_OCTS[step]; return { step: step, alt: alt, oct: oct, dir: dir }; }; var memo$1 = function (fn, cache) { if ( cache === void 0 ) cache = {}; return function (str) { return cache[str] || (cache[str] = fn(str)); }; }; var encoder = function (props) { return memo$1(function (str) { var p = props(str); return p.name === null ? null : encode(p); }); }; var encodeNote = encoder(props); var encodeIvl = encoder(props$1); /** * Transpose a note by an interval. The note can be a pitch class. * * This function can be partially applied. * * @param {string} note * @param {string} interval * @return {string} the transposed note * @example * import { tranpose } from "tonal-distance" * transpose("d3", "3M") // => "F#3" * // it works with pitch classes * transpose("D", "3M") // => "F#" * // can be partially applied * ["C", "D", "E", "F", "G"].map(transpose("M3)) // => ["E", "F#", "G#", "A", "B"] */ function transpose(note, interval) { if (arguments.length === 1) { return function (i) { return transpose(note, i); }; } var n = encodeNote(note); var i = encodeIvl(interval); if (n === null || i === null) { return null; } var tr = n.length === 1 ? [n[0] + i[0]] : [n[0] + i[0], n[1] + i[1]]; return build(decode(tr[0], tr[1])); } var isDescending = function (e) { return e[0] * 7 + e[1] * 12 < 0; }; var decodeIvl = function (i) { return isDescending(i) ? decode(-i[0], -i[1], -1) : decode(i[0], i[1], 1); }; /** * Find the interval between two pitches. It works with pitch classes * (both must be pitch classes and the interval is always ascending) * * Can be partially applied * * @param {string} from - distance from * @param {string} to - distance to * @return {string} the interval distance * * @example * import { interval } from "tonal-distance" * interval("C2", "C3") // => "P8" * interval("G", "B") // => "M3" * * @example * import * as Distance from "tonal-distance" * Distance.interval("M2", "P5") // => "P4" */ function interval(from, to) { if (arguments.length === 1) { return function (t) { return interval(from, t); }; } var f = encodeNote(from); var t = encodeNote(to); if (f === null || t === null || f.length !== t.length) { return null; } var d = f.length === 1 ? [t[0] - f[0], -Math.floor(((t[0] - f[0]) * 7) / 12)] : [t[0] - f[0], t[1] - f[1]]; return build$1(decodeIvl(d)); } /** * Get the distance between two notes in semitones * * @param {String|Pitch} from - first note * @param {String|Pitch} to - last note * @return {Integer} the distance in semitones or null if not valid notes * @example * import { semitones } from "tonal-distance" * semitones("C3", "A2") // => -3 * // or use tonal * Tonal.Distance.semitones("C3", "G3") // => 7 */ function semitones(from, to) { if (arguments.length === 1) { return function (t) { return semitones(from, t); }; } var f = props(from); var t = props(to); return f.midi !== null && t.midi !== null ? t.midi - f.midi : f.chroma !== null && t.chroma !== null ? (t.chroma - f.chroma + 12) % 12 : null; } var chromatic = [ "1P 2m 2M 3m 3M 4P 4A 5P 6m 6M 7m 7M" ]; var lydian = [ "1P 2M 3M 4A 5P 6M 7M" ]; var major = [ "1P 2M 3M 4P 5P 6M 7M", [ "ionian" ] ]; var mixolydian = [ "1P 2M 3M 4P 5P 6M 7m", [ "dominant" ] ]; var dorian = [ "1P 2M 3m 4P 5P 6M 7m" ]; var aeolian = [ "1P 2M 3m 4P 5P 6m 7m", [ "minor" ] ]; var phrygian = [ "1P 2m 3m 4P 5P 6m 7m" ]; var locrian = [ "1P 2m 3m 4P 5d 6m 7m" ]; var altered = [ "1P 2m 3m 3M 5d 6m 7m", [ "super locrian", "diminished whole tone", "pomeroy" ] ]; var diminished = [ "1P 2M 3m 4P 5d 6m 6M 7M", [ "whole-half diminished" ] ]; var iwato = [ "1P 2m 4P 5d 7m" ]; var hirajoshi = [ "1P 2M 3m 5P 6m" ]; var kumoijoshi = [ "1P 2m 4P 5P 6m" ]; var pelog = [ "1P 2m 3m 5P 6m" ]; var prometheus = [ "1P 2M 3M 4A 6M 7m" ]; var ritusen = [ "1P 2M 4P 5P 6M" ]; var scriabin = [ "1P 2m 3M 5P 6M" ]; var piongio = [ "1P 2M 4P 5P 6M 7m" ]; var augmented = [ "1P 2A 3M 5P 5A 7M" ]; var neopolitan = [ "1P 2m 3m 4P 5P 6m 7M" ]; var egyptian = [ "1P 2M 4P 5P 7m" ]; var oriental = [ "1P 2m 3M 4P 5d 6M 7m" ]; var flamenco = [ "1P 2m 3m 3M 4A 5P 7m" ]; var balinese = [ "1P 2m 3m 4P 5P 6m 7M" ]; var persian = [ "1P 2m 3M 4P 5d 6m 7M" ]; var bebop = [ "1P 2M 3M 4P 5P 6M 7m 7M" ]; var enigmatic = [ "1P 2m 3M 5d 6m 7m 7M" ]; var ichikosucho = [ "1P 2M 3M 4P 5d 5P 6M 7M" ]; var sdata = { chromatic: chromatic, lydian: lydian, major: major, mixolydian: mixolydian, dorian: dorian, aeolian: aeolian, phrygian: phrygian, locrian: locrian, "melodic minor": [ "1P 2M 3m 4P 5P 6M 7M" ], "melodic minor second mode": [ "1P 2m 3m 4P 5P 6M 7m" ], "lydian augmented": [ "1P 2M 3M 4A 5A 6M 7M" ], "lydian dominant": [ "1P 2M 3M 4A 5P 6M 7m", [ "lydian b7" ] ], "melodic minor fifth mode": [ "1P 2M 3M 4P 5P 6m 7m", [ "hindu", "mixolydian b6M" ] ], "locrian #2": [ "1P 2M 3m 4P 5d 6m 7m", [ "half-diminished" ] ], altered: altered, "harmonic minor": [ "1P 2M 3m 4P 5P 6m 7M" ], "phrygian dominant": [ "1P 2m 3M 4P 5P 6m 7m", [ "spanish", "phrygian major" ] ], "half-whole diminished": [ "1P 2m 3m 3M 4A 5P 6M 7m", [ "dominant diminished" ] ], diminished: diminished, "major pentatonic": [ "1P 2M 3M 5P 6M", [ "pentatonic" ] ], "lydian pentatonic": [ "1P 3M 4A 5P 7M", [ "chinese" ] ], "mixolydian pentatonic": [ "1P 3M 4P 5P 7m", [ "indian" ] ], "locrian pentatonic": [ "1P 3m 4P 5d 7m", [ "minor seven flat five pentatonic" ] ], "minor pentatonic": [ "1P 3m 4P 5P 7m" ], "minor six pentatonic": [ "1P 3m 4P 5P 6M" ], "minor hexatonic": [ "1P 2M 3m 4P 5P 7M" ], "flat three pentatonic": [ "1P 2M 3m 5P 6M", [ "kumoi" ] ], "flat six pentatonic": [ "1P 2M 3M 5P 6m" ], "major flat two pentatonic": [ "1P 2m 3M 5P 6M" ], "whole tone pentatonic": [ "1P 3M 5d 6m 7m" ], "ionian pentatonic": [ "1P 3M 4P 5P 7M" ], "lydian #5P pentatonic": [ "1P 3M 4A 5A 7M" ], "lydian dominant pentatonic": [ "1P 3M 4A 5P 7m" ], "minor #7M pentatonic": [ "1P 3m 4P 5P 7M" ], "super locrian pentatonic": [ "1P 3m 4d 5d 7m" ], "in-sen": [ "1P 2m 4P 5P 7m" ], iwato: iwato, hirajoshi: hirajoshi, kumoijoshi: kumoijoshi, pelog: pelog, "vietnamese 1": [ "1P 3m 4P 5P 6m" ], "vietnamese 2": [ "1P 3m 4P 5P 7m" ], prometheus: prometheus, "prometheus neopolitan": [ "1P 2m 3M 4A 6M 7m" ], ritusen: ritusen, scriabin: scriabin, piongio: piongio, "major blues": [ "1P 2M 3m 3M 5P 6M" ], "minor blues": [ "1P 3m 4P 5d 5P 7m", [ "blues" ] ], "composite blues": [ "1P 2M 3m 3M 4P 5d 5P 6M 7m" ], augmented: augmented, "augmented heptatonic": [ "1P 2A 3M 4P 5P 5A 7M" ], "dorian #4": [ "1P 2M 3m 4A 5P 6M 7m" ], "lydian diminished": [ "1P 2M 3m 4A 5P 6M 7M" ], "whole tone": [ "1P 2M 3M 4A 5A 7m" ], "leading whole tone": [ "1P 2M 3M 4A 5A 7m 7M" ], "lydian minor": [ "1P 2M 3M 4A 5P 6m 7m" ], "locrian major": [ "1P 2M 3M 4P 5d 6m 7m", [ "arabian" ] ], neopolitan: neopolitan, "neopolitan minor": [ "1P 2m 3m 4P 5P 6m 7M" ], "neopolitan major": [ "1P 2m 3m 4P 5P 6M 7M", [ "dorian b2" ] ], "neopolitan major pentatonic": [ "1P 3M 4P 5d 7m" ], "romanian minor": [ "1P 2M 3m 5d 5P 6M 7m" ], "double harmonic lydian": [ "1P 2m 3M 4A 5P 6m 7M" ], "harmonic major": [ "1P 2M 3M 4P 5P 6m 7M" ], "double harmonic major": [ "1P 2m 3M 4P 5P 6m 7M", [ "gypsy" ] ], egyptian: egyptian, "hungarian minor": [ "1P 2M 3m 4A 5P 6m 7M" ], "hungarian major": [ "1P 2A 3M 4A 5P 6M 7m" ], oriental: oriental, "spanish heptatonic": [ "1P 2m 3m 3M 4P 5P 6m 7m" ], flamenco: flamenco, balinese: balinese, "todi raga": [ "1P 2m 3m 4A 5P 6m 7M" ], "malkos raga": [ "1P 3m 4P 6m 7m" ], "kafi raga": [ "1P 3m 3M 4P 5P 6M 7m 7M" ], "purvi raga": [ "1P 2m 3M 4P 4A 5P 6m 7M" ], persian: persian, bebop: bebop, "bebop dominant": [ "1P 2M 3M 4P 5P 6M 7m 7M" ], "bebop minor": [ "1P 2M 3m 3M 4P 5P 6M 7m" ], "bebop major": [ "1P 2M 3M 4P 5P 5A 6M 7M" ], "bebop locrian": [ "1P 2m 3m 4P 5d 5P 6m 7m" ], "minor bebop": [ "1P 2M 3m 4P 5P 6m 7m 7M" ], "mystery #1": [ "1P 2m 3M 5d 6m 7m" ], enigmatic: enigmatic, "minor six diminished": [ "1P 2M 3m 4P 5P 6m 6M 7M" ], "ionian augmented": [ "1P 2M 3M 4P 5A 6M 7M" ], "lydian #9": [ "1P 2m 3M 4A 5P 6M 7M" ], ichikosucho: ichikosucho, "six tone symmetric": [ "1P 2m 3M 4P 5A 6M" ] }; var M$1 = [ "1P 3M 5P", [ "Major", "" ] ]; var M13 = [ "1P 3M 5P 7M 9M 13M", [ "maj13", "Maj13" ] ]; var M6 = [ "1P 3M 5P 13M", [ "6" ] ]; var M69 = [ "1P 3M 5P 6M 9M", [ "69" ] ]; var M7add13 = [ "1P 3M 5P 6M 7M 9M" ]; var M7b5 = [ "1P 3M 5d 7M" ]; var M7b6 = [ "1P 3M 6m 7M" ]; var M7b9 = [ "1P 3M 5P 7M 9m" ]; var M7sus4 = [ "1P 4P 5P 7M" ]; var M9 = [ "1P 3M 5P 7M 9M", [ "maj9", "Maj9" ] ]; var M9b5 = [ "1P 3M 5d 7M 9M" ]; var M9sus4 = [ "1P 4P 5P 7M 9M" ]; var Madd9 = [ "1P 3M 5P 9M", [ "2", "add9", "add2" ] ]; var Maj7 = [ "1P 3M 5P 7M", [ "maj7", "M7" ] ]; var Mb5 = [ "1P 3M 5d" ]; var Mb6 = [ "1P 3M 13m" ]; var Msus2 = [ "1P 2M 5P", [ "add9no3", "sus2" ] ]; var Msus4 = [ "1P 4P 5P", [ "sus", "sus4" ] ]; var Maddb9 = [ "1P 3M 5P 9m" ]; var m$1 = [ "1P 3m 5P" ]; var m11 = [ "1P 3m 5P 7m 9M 11P", [ "_11" ] ]; var m11b5 = [ "1P 3m 7m 12d 2M 4P", [ "h11", "_11b5" ] ]; var m13 = [ "1P 3m 5P 7m 9M 11P 13M", [ "_13" ] ]; var m6 = [ "1P 3m 4P 5P 13M", [ "_6" ] ]; var m69 = [ "1P 3m 5P 6M 9M", [ "_69" ] ]; var m7 = [ "1P 3m 5P 7m", [ "minor7", "_", "_7" ] ]; var m7add11 = [ "1P 3m 5P 7m 11P", [ "m7add4" ] ]; var m7b5 = [ "1P 3m 5d 7m", [ "half-diminished", "h7", "_7b5" ] ]; var m9 = [ "1P 3m 5P 7m 9M", [ "_9" ] ]; var m9b5 = [ "1P 3m 7m 12d 2M", [ "h9", "-9b5" ] ]; var mMaj7 = [ "1P 3m 5P 7M", [ "mM7", "_M7" ] ]; var mMaj7b6 = [ "1P 3m 5P 6m 7M", [ "mM7b6" ] ]; var mM9 = [ "1P 3m 5P 7M 9M", [ "mMaj9", "-M9" ] ]; var mM9b6 = [ "1P 3m 5P 6m 7M 9M", [ "mMaj9b6" ] ]; var mb6M7 = [ "1P 3m 6m 7M" ]; var mb6b9 = [ "1P 3m 6m 9m" ]; var o$1 = [ "1P 3m 5d", [ "mb5", "dim" ] ]; var o7 = [ "1P 3m 5d 13M", [ "diminished", "m6b5", "dim7" ] ]; var o7M7 = [ "1P 3m 5d 6M 7M" ]; var oM7 = [ "1P 3m 5d 7M" ]; var sus24 = [ "1P 2M 4P 5P", [ "sus4add9" ] ]; var madd4 = [ "1P 3m 4P 5P" ]; var madd9 = [ "1P 3m 5P 9M" ]; var cdata = { "4": [ "1P 4P 7m 10m", [ "quartal" ] ], "5": [ "1P 5P" ], "7": [ "1P 3M 5P 7m", [ "Dominant", "Dom" ] ], "9": [ "1P 3M 5P 7m 9M", [ "79" ] ], "11": [ "1P 5P 7m 9M 11P" ], "13": [ "1P 3M 5P 7m 9M 13M", [ "13_" ] ], "64": [ "5P 8P 10M" ], M: M$1, "M#5": [ "1P 3M 5A", [ "augmented", "maj#5", "Maj#5", "+", "aug" ] ], "M#5add9": [ "1P 3M 5A 9M", [ "+add9" ] ], M13: M13, "M13#11": [ "1P 3M 5P 7M 9M 11A 13M", [ "maj13#11", "Maj13#11", "M13+4", "M13#4" ] ], M6: M6, "M6#11": [ "1P 3M 5P 6M 11A", [ "M6b5", "6#11", "6b5" ] ], M69: M69, "M69#11": [ "1P 3M 5P 6M 9M 11A" ], "M7#11": [ "1P 3M 5P 7M 11A", [ "maj7#11", "Maj7#11", "M7+4", "M7#4" ] ], "M7#5": [ "1P 3M 5A 7M", [ "maj7#5", "Maj7#5", "maj9#5", "M7+" ] ], "M7#5sus4": [ "1P 4P 5A 7M" ], "M7#9#11": [ "1P 3M 5P 7M 9A 11A" ], M7add13: M7add13, M7b5: M7b5, M7b6: M7b6, M7b9: M7b9, M7sus4: M7sus4, M9: M9, "M9#11": [ "1P 3M 5P 7M 9M 11A", [ "maj9#11", "Maj9#11", "M9+4", "M9#4" ] ], "M9#5": [ "1P 3M 5A 7M 9M", [ "Maj9#5" ] ], "M9#5sus4": [ "1P 4P 5A 7M 9M" ], M9b5: M9b5, M9sus4: M9sus4, Madd9: Madd9, Maj7: Maj7, Mb5: Mb5, Mb6: Mb6, Msus2: Msus2, Msus4: Msus4, Maddb9: Maddb9, "11b9": [ "1P 5P 7m 9m 11P" ], "13#11": [ "1P 3M 5P 7m 9M 11A 13M", [ "13+4", "13#4" ] ], "13#9": [ "1P 3M 5P 7m 9A 13M", [ "13#9_" ] ], "13#9#11": [ "1P 3M 5P 7m 9A 11A 13M" ], "13b5": [ "1P 3M 5d 6M 7m 9M" ], "13b9": [ "1P 3M 5P 7m 9m 13M" ], "13b9#11": [ "1P 3M 5P 7m 9m 11A 13M" ], "13no5": [ "1P 3M 7m 9M 13M" ], "13sus4": [ "1P 4P 5P 7m 9M 13M", [ "13sus" ] ], "69#11": [ "1P 3M 5P 6M 9M 11A" ], "7#11": [ "1P 3M 5P 7m 11A", [ "7+4", "7#4", "7#11_", "7#4_" ] ], "7#11b13": [ "1P 3M 5P 7m 11A 13m", [ "7b5b13" ] ], "7#5": [ "1P 3M 5A 7m", [ "+7", "7aug", "aug7" ] ], "7#5#9": [ "1P 3M 5A 7m 9A", [ "7alt", "7#5#9_", "7#9b13_" ] ], "7#5b9": [ "1P 3M 5A 7m 9m" ], "7#5b9#11": [ "1P 3M 5A 7m 9m 11A" ], "7#5sus4": [ "1P 4P 5A 7m" ], "7#9": [ "1P 3M 5P 7m 9A", [ "7#9_" ] ], "7#9#11": [ "1P 3M 5P 7m 9A 11A", [ "7b5#9" ] ], "7#9#11b13": [ "1P 3M 5P 7m 9A 11A 13m" ], "7#9b13": [ "1P 3M 5P 7m 9A 13m" ], "7add6": [ "1P 3M 5P 7m 13M", [ "67", "7add13" ] ], "7b13": [ "1P 3M 7m 13m" ], "7b5": [ "1P 3M 5d 7m" ], "7b6": [ "1P 3M 5P 6m 7m" ], "7b9": [ "1P 3M 5P 7m 9m" ], "7b9#11": [ "1P 3M 5P 7m 9m 11A", [ "7b5b9" ] ], "7b9#9": [ "1P 3M 5P 7m 9m 9A" ], "7b9b13": [ "1P 3M 5P 7m 9m 13m" ], "7b9b13#11": [ "1P 3M 5P 7m 9m 11A 13m", [ "7b9#11b13", "7b5b9b13" ] ], "7no5": [ "1P 3M 7m" ], "7sus4": [ "1P 4P 5P 7m", [ "7sus" ] ], "7sus4b9": [ "1P 4P 5P 7m 9m", [ "susb9", "7susb9", "7b9sus", "7b9sus4", "phryg" ] ], "7sus4b9b13": [ "1P 4P 5P 7m 9m 13m", [ "7b9b13sus4" ] ], "9#11": [ "1P 3M 5P 7m 9M 11A", [ "9+4", "9#4", "9#11_", "9#4_" ] ], "9#11b13": [ "1P 3M 5P 7m 9M 11A 13m", [ "9b5b13" ] ], "9#5": [ "1P 3M 5A 7m 9M", [ "9+" ] ], "9#5#11": [ "1P 3M 5A 7m 9M 11A" ], "9b13": [ "1P 3M 7m 9M 13m" ], "9b5": [ "1P 3M 5d 7m 9M" ], "9no5": [ "1P 3M 7m 9M" ], "9sus4": [ "1P 4P 5P 7m 9M", [ "9sus" ] ], m: m$1, "m#5": [ "1P 3m 5A", [ "m+", "mb6" ] ], m11: m11, "m11A 5": [ "1P 3m 6m 7m 9M 11P" ], m11b5: m11b5, m13: m13, m6: m6, m69: m69, m7: m7, "m7#5": [ "1P 3m 6m 7m" ], m7add11: m7add11, m7b5: m7b5, m9: m9, "m9#5": [ "1P 3m 6m 7m 9M" ], m9b5: m9b5, mMaj7: mMaj7, mMaj7b6: mMaj7b6, mM9: mM9, mM9b6: mM9b6, mb6M7: mb6M7, mb6b9: mb6b9, o: o$1, o7: o7, o7M7: o7M7, oM7: oM7, sus24: sus24, "+add#9": [ "1P 3M 5A 9A" ], madd4: madd4, madd9: madd9 }; /** * [![npm version](https://img.shields.io/npm/v/tonal-pcset.svg?style=flat-square)](https://www.npmjs.com/package/tonal-pcset) * [![tonal](https://img.shields.io/badge/tonal-pcset-yellow.svg?style=flat-square)](https://www.npmjs.com/browse/keyword/tonal) * * `tonal-pcset` is a collection of functions to work with pitch class sets, oriented * to make comparations (isEqual, isSubset, isSuperset) * * This is part of [tonal](https://www.npmjs.com/package/tonal) music theory library. * * You can install via npm: `npm i --save tonal-pcset` * * ```js * // es6 * import PcSet from "tonal-pcset" * var PcSet = require("tonal-pcset") * * PcSet.isEqual("c2 d5 e6", "c6 e3 d1") // => true * ``` * * ## API documentation * * @module PcSet */ var chr = function (str) { return chroma(str) || chroma$1(str) || 0; }; /** * Get chroma of a pitch class set. A chroma identifies each set uniquely. * It"s a 12-digit binary each presenting one semitone of the octave. * * Note that this function accepts a chroma as parameter and return it * without modification. * * @param {Array|String} set - the pitch class set * @return {string} a binary representation of the pitch class set * @example * PcSet.chroma(["C", "D", "E"]) // => "1010100000000" */ function chroma$2(set) { if (isChroma(set)) { return set; } if (!Array.isArray(set)) { return ""; } var b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; set.map(chr).forEach(function (i) { b[i] = 1; }); return b.join(""); } var REGEX$2 = /^[01]{12}$/; /** * Test if the given string is a pitch class set chroma. * @param {string} chroma - the pitch class set chroma * @return {Boolean} true if its a valid pcset chroma * @example * PcSet.isChroma("101010101010") // => true * PcSet.isChroma("101001") // => false */ function isChroma(set) { return REGEX$2.test(set); } /** * [![npm version](https://img.shields.io/npm/v/tonal-dictionary.svg)](https://www.npmjs.com/package/tonal-dictionary) * * `tonal-dictionary` contains a dictionary of musical scales and chords * * This is part of [tonal](https://www.npmjs.com/package/tonal) music theory library. * * @example * // es6 * import * as Dictionary from "tonal-dictionary" * // es5 * const Dictionary = require("tonal-dictionary") * * @example * Dictionary.chord("Maj7") // => ["1P", "3M", "5P", "7M"] * * @module Dictionary */ var dictionary = function (raw) { var keys = Object.keys(raw).sort(); var data = []; var index = []; var add = function (name, ivls, chroma) { data[name] = ivls; index[chroma] = index[chroma] || []; index[chroma].push(name); }; keys.forEach(function (key) { var ivls = raw[key][0].split(" "); var alias = raw[key][1]; var chr = chroma$2(ivls); add(key, ivls, chr); if (alias) { alias.forEach(function (a) { return add(a, ivls, chr); }); } }); var allKeys = Object.keys(data).sort(); var dict = function (name) { return data[name]; }; dict.names = function (p) { if (typeof p === "string") { return (index[p] || []).slice(); } else { return (p === true ? allKeys : keys).slice(); } }; return dict; }; /** * A dictionary of scales: a function that given a scale name (without tonic) * returns an array of intervals * * @function * @param {string} name * @return {Array} intervals * @example * import { scale } from "tonal-dictionary" * scale("major") // => ["1P", "2M", ...] * scale.names(); // => ["major", ...] */ var scale = dictionary(sdata); /** * A dictionary of chords: a function that given a chord type * returns an array of intervals * * @function * @param {string} type * @return {Array} intervals * @example * import { chord } from "tonal-dictionary" * chord("Maj7") // => ["1P", "3M", ...] * chord.names(); // => ["Maj3", ...] */ var chord = dictionary(cdata); /** * [![npm version](https://img.shields.io/npm/v/tonal-scale.svg?style=flat-square)](https://www.npmjs.com/package/tonal-scale) * * A scale is a collection of pitches in ascending or descending order. * * This module provides functions to get and manipulate scales. * * @example * // es6 * import * as Scale from "tonal-scale" * // es5 * const Scale = require("tonal-scale"); * * @example * Scale.notes("Ab bebop") // => [ "Ab", "Bb", "C", "Db", "Eb", "F", "Gb", "G" ] * Scale.names() => ["major", "minor", ...] * @module Scale */ var NO_SCALE = Object.freeze({ name: null, intervals: [], names: [], chroma: null, setnum: null }); /** * [![npm version](https://img.shields.io/npm/v/tonal-chord.svg)](https://www.npmjs.com/package/tonal-chord) * [![tonal](https://img.shields.io/badge/tonal-chord-yellow.svg)](https://www.npmjs.com/browse/keyword/tonal) * * `tonal-chord` is a collection of functions to manipulate musical chords * * This is part of [tonal](https://www.npmjs.com/package/tonal) music theory library. * * @example * // es6 * import * as Chord from "tonal-chord" * // es5 * const Chord = require("tonal-chord") * * @example * Chord.notes("CMaj7") // => ["C", "E", "G", "B"] * * @module Chord */ var NO_CHORD = Object.freeze({ name: null, names: [], intervals: [], chroma: null, setnum: null }); /** * [ 'E2', 'A2', 'D3', 'G3', 'B3', 'E4' ] --> [ '1P', '4P', '4P', '4P', '3M', '4P' ] * * @param tuning * @returns {ReadonlyArray<any>} */ function computeTuningIntervals(tuning) { if (tuning === void 0) { tuning = Tuning.guitar.standard; } var tuningIntervals = Array(tuning.length).fill(null); for (var i = 0; i < tuning.length; i++) { // const d = Distance.interval("C2", "C3"); var d = interval(tuning[(i - 1 + tuning.length) % tuning.length], tuning[i]); // console.log("computeTuningIntervals d", d); if (typeof d !== "string") { // because Distance.interval can return a function throw new Error("unexpected error"); } var simple = simplify$1(d); tuningIntervals[i] = simple === '-1P' ? '1P' : simple;